1、套接字工作流程
- 服務器端先初始化Socket,建立一個套接字
- 與端口綁定(bind),用 bind 函數來綁定一個端口號和 IP 地址。
- 對端口進行監聽(listen),服務器調用 listen 函數,使服務器的這個端口和 IP 處於監聽狀態
- 服務器調用accept阻塞,等待客戶端連接。等待客戶機的連接。
- 客戶機用 socket 函數建立一個套接字,設定遠程 IP 和端口。
- 客戶機調用 connect 函數連接遠程計算機指定的端口。
- 服務器用 accept 函數來接受遠程計算機的連接,建立起與客戶機之間的通信。
- 建立連接以後,客戶機用 write 函數向 socket 中寫入數據。也可以用 read 函數讀取服務器發送來的數據。
- 服務器用 read 函數讀取客戶機發送來的數據,也可以用 write 函數來發送數據。
- 完成通信以後,用 close 函數關閉 socket 連接。
常用語法:
import socket
socket.socket(socket_family,socket_type,protocal=0)
# 獲取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 獲取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
注:socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默認值爲 0。
服務端套接字函數
- s.bind() 綁定(主機,端口號)到套接字
- s.listen() 開始TCP監聽
- s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來
注意:bind()其中INADDR_ANY就是指定地址爲0.0.0.0的地址,這個地址事實上表示不確定地址,或“所有地址”、“任意地址”。
客戶端套接字函數
- s.connect() 主動初始化TCP服務器連接
- s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用途的套接字函數
- s.recv() 接收TCP數據
- s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完)
- s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完)
- s.recvfrom() 接收UDP數據
- s.sendto() 發送UDP數據
- s.getpeername() 連接到當前套接字的遠端的地址
- s.getsockname() 當前套接字的地址
- s.getsockopt() 返回指定套接字的參數
- s.setsockopt() 設置指定套接字的參數
- s.close() 關閉套接字
面向鎖的套接字方法
- s.setblocking() 設置套接字的阻塞與非阻塞模式
- s.settimeout() 設置阻塞套接字操作的超時時間
- s.gettimeout() 得到阻塞套接字操作的超時時間
面向文件的套接字的函數
- s.fileno() 套接字的文件描述符
- s.makefile() 創建一個與該套接字相關的文件
客戶端和服務端交互示例:
# 服務端
import socket
test=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
test.bind(('127.0.0.1',8080)) # 綁定的IP和端口
test.listen(5) # 參數表示最大可以掛起的連接數
conn,client_addr=test.accept() # 建立的鏈接,客戶端的鏈接消息
print(conn)
print(client_addr)
client_data=conn.recv(1024) # 表示最大收取的消息
conn.send(client_data.upper()) # 將消息轉換爲大寫後發回
conn.close() # 關閉鏈接
test.close() #
# 客戶端
import socket
test=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
test.connect(('127.0.0.1',8080))
test.send('hello'.encode('utf-8'))
server_data=test.recv(1024)
print('the server respond:',server_data)
test.close()
防止啓動的時候地址端口被佔用,未被釋放,可在bind前添加test.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
:
import socket
test=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
test.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 在鏈接異常終止後,再次啓動會複用之前的IP端口,防止資源沒有釋放而產生地址衝突
test.bind(('127.0.0.1',8080)) # 綁定的IP和端口
test.listen(5) # 參數表示最大可以掛起的連接數
conn,client_addr=test.accept() # 建立的鏈接,客戶端的鏈接消息
while True: #
client_data=conn.recv(1024) # 表示最大收取的消息
conn.send(client_data.upper()) # 將消息轉換爲大寫後發回
conn.close() # 關閉鏈接
test.close()
提示:當客戶端發送空字符串時,服務端是不會收取的,這樣就會倒是通訊終止在當前,可以通過控制客戶端發送的內容來防止出現此問題。
————————————————————————————————————
參考:
- https://www.jianshu.com/p/f75bc9971169 作者:斷尾壁虎V
- https://blog.csdn.net/qq_26399665/article/details/52932755
- https://www.cnblogs.com/daijingkun/p/10871976.html