socket介紹
1、什么是socket
socket是應用層與傳輸層中間的一個軟件抽象層,它是一組接口。它把TCP/IP這些復雜的協議統一封裝起來
這樣我們只要知道如何使用socket就好,就已經符合了傳輸層往下的一大串協議
2、為什么要使用socket
如果沒有socket而我們寫的代碼又要讓別人能正確解析,就需要一層層往下研究協議,寫出符合協議的代碼
而我們大家傳輸層往下的代碼基本一樣,所以把這些代碼封裝成一個模塊,方便大家使用,不用去考慮傳輸層
以下的東西
3、socket發展
套接字起源於20世紀70年代,一開始,套接字被設計用在同一台主機上多個應用程序的通訊,這也被稱為
進程間通訊或者IPC。后來網絡發展,又被應用於網絡協議上。
套接字有兩種(或者稱為有兩個種族):
基於文件類型的套接字家族:名字 --> AF_UNIX
基於網絡類型的套接字家族:名字 --> AF_INET被用於ipv4 AF_INET6被用於ipv6
socket用法
# 需要明確的是:
關於網絡協議和socket相關概念,對於所有編程語言都是一致的,區別僅僅是個編程語言的函數名稱不同
1、服務器端
import socket # 獲得socket對象 AF_INET表示基於網絡的套接字 sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # SOCK_STREAM表示的是TCP協議 SOCK_DGRAM表示的是UDP協議 # 將socket對象綁定ip地址和端口號 sk.bind(('127.0.0.1',8080)) # 參數需要傳入一個元組 # 相當於電話的開機 括號里的參數表示可以同時接收5個請求 sk.listen(5) # 一般服務器都不需要關閉,所以加個while循環 while True: # 進入監聽狀態,等待別人鏈接過來,有兩個返回值,一個是對方的socket對象,一個是對方的ip以及端口 client,addr = sk.accept() # 收發消息一般都需要多次循環發送,也加個while循環 while True: try: # 如果對面強行關閉,為了程序不崩,就需要異常處理 msg = client.recv(1024) # recv表示接收,括號里是最大接收字節 if not msg: # 如果接收到的消息是空字符串,就表示對面正常退出,需要打斷循環 break # send表示發送數據,發送的數據必須是二進制數據 client.send('二進制數據'.encode('utf-8')) except Exception as e: print(e) break client.close() # 關閉對面傳過來的對象 sk.close() # 關閉服務器的socket對象
2、客戶端
import socket # 獲取socket對象,括號里默認是AF_INET 和 SOCK_STREAM 所以可以不寫 sk = socket.socket() # 鏈接到服務器端 括號里也是一個元組,包含ip地址以及對方的端口號 sk.connect(('127.0.0.1',8080)) # 自己的端口號系統會隨機分配,不需要設置 while True: # 收發信息也需要循環 try: # 為了防止服務器非正常下線而導致客戶端直接崩潰,需要加入異常處理 cmd = input('>>(q:退出)') if cmd == 'q': # 給客戶端一個可以正常退出的方法 break if not cmd: # 如果直接敲回車,那么cmd就是空字符串,TCP協議對此進行優化 continue # 正常收發消息過程中,你傳入空字符串,它不會幫你發送 sk.send(cmd.encode('utf-8')) # 如果上面沒有做空判斷,輸入空會直接跳過send這步,這樣客戶端和服務器端都處於接收狀態,卡住不動 sk.recv(1024) except Exception as e: print(e) break sk.close() # 關閉socket對象
3、基於TCP的socket通訊流程圖

socket常見問題
1、端口占用
報錯信息
Traceback (most recent call last):
File "F:/pyprogram/day31/homework/topic2/test.py", line 6, in <module>
sk.bind(('127.0.0.1',8080))
OSError: [WinError 10048] 通常每個套接字地址(協議/網絡地址/端口)只允許使用一次。
問題發生的原因
1.可能是由於你已經啟動了服務器程序,卻又再次啟動了服務器程序,同一個端口不能被多個進程使用導致
2.三次握手或四次揮手時,發生了異常導致對方程序已經結束而服務器任然處於time_wait狀態導致
3.在高並發的場景下,由於連接的客戶端太多,也會產生大量處於time_wait狀態連接
解決的方案
第一種重新選擇端口后就可以,或者把之前的占用端口的進程關閉掉
第二三種以后再說
2、強行關閉鏈接
報錯信息
Traceback (most recent call last):
File "F:/pyprogram/day31/part2/客戶端.py", line 12, in <module>
client.send(msg.encode('utf-8'))
ConnectionResetError: [WinError 10054] 遠程主機強迫關閉了一個現有的連接。
問題發生的原因
當客戶端與服務器鏈接成功后,如果一方沒有執行close,而是直接強行終止程序(或是遇到異常被迫終止)
都會導致另一方發生問題
在windows下,接收數據的一方在recv函數處將拋出異常
解決的方案
用try...except語法抓取異常,抓到對方強退異常后並處理
3、客戶端正常結束,服務器端無限循環空字符
報錯信息
不會產生報錯信息,但是服務器端會一直循環空字符串
問題發生的原因
客戶端正常close()之后,會給服務器發送一個空字符串,
如果服務器端沒有發送數據的話,就會一直循環接收這個空字符串,占用CPU資源
解決的方案
在服務器端接收的地方加上空字符串判斷,如果是空字符串,就表示對方客戶端退出了
那么判斷空成功,讓自己也退出循環
