Python網絡編程之Python編寫TCP協(xié)議程序的步驟
TCP客戶端程序開發(fā)
1. 開發(fā) TCP 客戶端程序開發(fā)步驟回顧
- 創(chuàng)建客戶端套接字對象
- 和服務端套接字建立連接
- 發(fā)送數(shù)據
- 接收數(shù)據
- 關閉客戶端套接字
2. socket 類的介紹
導入 socket 模塊 import socket
創(chuàng)建客戶端 socket 對象 socket.socket(AddressFamily, Type)
參數(shù)說明:
- AddressFamily 表示IP地址類型, 分為TPv4和IPv6
- Type 表示傳輸協(xié)議類型
方法說明:
- connect((host, port)) 表示和服務端套接字建立連接, host是服務器ip地址,port是應用程序的端口號
- send(data) 表示發(fā)送數(shù)據,data是二進制數(shù)據
- recv(buffersize) 表示接收數(shù)據, buffersize是每次接收數(shù)據的長度
3. TCP 客戶端程序開發(fā)示例代碼
import socket
if __name__ == '__main__':
# 創(chuàng)建tcp客戶端套接字
# 1. AF_INET:表示ipv4
# 2. SOCK_STREAM: tcp傳輸協(xié)議
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 和服務端應用程序建立連接
tcp_client_socket.connect(("192.168.131.62", 8080))
# 代碼執(zhí)行到此,說明連接建立成功
# 準備發(fā)送的數(shù)據
send_data = "你好服務端,我是客戶端小黑!".encode("gbk")
# 發(fā)送數(shù)據
tcp_client_socket.send(send_data)
# 接收數(shù)據, 這次接收的數(shù)據最大字節(jié)數(shù)是1024
recv_data = tcp_client_socket.recv(1024)
# 返回的直接是服務端程序發(fā)送的二進制數(shù)據
print(recv_data)
# 對數(shù)據進行解碼
recv_content = recv_data.decode("gbk")
print("接收服務端的數(shù)據為:", recv_content)
# 關閉套接字
tcp_client_socket.close()執(zhí)行結果:
b'hello'
接收服務端的數(shù)據為: hello
說明
- str.encode(編碼格式) 表示把字符串編碼成為二進制
- data.decode(編碼格式) 表示把二進制解碼成為字符串
網絡調試助手充當服務端程序:

TCP服務端程序開發(fā)
1. 開發(fā) TCP 服務端程序開發(fā)步驟回顧
- 創(chuàng)建服務端端套接字對象
- 綁定端口號
- 設置監(jiān)聽
- 等待接受客戶端的連接請求
- 接收數(shù)據
- 發(fā)送數(shù)據
- 關閉套接字
2. socket 類的介紹
導入 socket 模塊
import socket
創(chuàng)建服務端 socket 對象
socket.socket(AddressFamily, Type)
參數(shù)說明:
- AddressFamily 表示IP地址類型, 分為TPv4和IPv6
- Type 表示傳輸協(xié)議類型
方法說明:
- bind((host, port)) 表示綁定端口號, host 是 ip 地址,port 是端口號,ip 地址一般不指定,表示本機的任何一個ip地址都可以。
- listen (backlog) 表示設置監(jiān)聽,backlog參數(shù)表示最大等待建立連接的個數(shù)。
- accept() 表示等待接受客戶端的連接請求
- send(data) 表示發(fā)送數(shù)據,data 是二進制數(shù)據
- recv(buffersize) 表示接收數(shù)據, buffersize 是每次接收數(shù)據的長度
3. TCP 服務端程序開發(fā)示例代碼
import socket
if __name__ == '__main__':
# 創(chuàng)建tcp服務端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 設置端口號復用,讓程序退出端口號立即釋放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 給程序綁定端口號
tcp_server_socket.bind(("", 8989))
# 設置監(jiān)聽
# 128:最大等待建立連接的個數(shù), 提示: 目前是單任務的服務端,同一時刻只能服務與一個客戶端,后續(xù)使用多任務能夠讓服務端同時服務與多個客戶端,
# 不需要讓客戶端進行等待建立連接
# listen后的這個套接字只負責接收客戶端連接請求,不能收發(fā)消息,收發(fā)消息使用返回的這個新套接字來完成
tcp_server_socket.listen(128)
# 等待客戶端建立連接的請求, 只有客戶端和服務端建立連接成功代碼才會解阻塞,代碼才能繼續(xù)往下執(zhí)行
# 1. 專門和客戶端通信的套接字: service_client_socket
# 2. 客戶端的ip地址和端口號: ip_port
service_client_socket, ip_port = tcp_server_socket.accept()
# 代碼執(zhí)行到此說明連接建立成功
print("客戶端的ip地址和端口號:", ip_port)
# 接收客戶端發(fā)送的數(shù)據, 這次接收數(shù)據的最大字節(jié)數(shù)是1024
recv_data = service_client_socket.recv(1024)
# 獲取數(shù)據的長度
recv_data_length = len(recv_data)
print("接收數(shù)據的長度為:", recv_data_length)
# 對二進制數(shù)據進行解碼
recv_content = recv_data.decode("gbk")
print("接收客戶端的數(shù)據為:", recv_content)
# 準備發(fā)送的數(shù)據
send_data = "ok, 問題正在處理中...".encode("gbk")
# 發(fā)送數(shù)據給客戶端
service_client_socket.send(send_data)
# 關閉服務與客戶端的套接字, 終止和客戶端通信的服務
service_client_socket.close()
# 關閉服務端的套接字, 終止和客戶端提供建立連接請求的服務
tcp_server_socket.close()執(zhí)行結果:
客戶端的ip地址和端口號: ('172.16.47.209', 52472)
接收數(shù)據的長度為: 5
接收客戶端的數(shù)據為: hello
說明:
- 更換服務端端口號
- 設置端口號復用(推薦大家使用),也就是說讓服務端程序退出后端口號立即釋放。
解決辦法有兩種:
更換服務端端口號設置端口號復用(推薦大家使用),也就是說讓服務端程序退出后端口號立即釋放。
設置端口號復用的代碼如下:
# 參數(shù)1: 表示當前套接字 # 參數(shù)2: 設置端口號復用選項 # 參數(shù)3: 設置端口號復用選項對應的值 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
網絡調試助手充當客戶端程序:

TCP網絡應用程序的注意點
- 當 TCP 客戶端程序想要和 TCP 服務端程序進行通信的時候必須要先建立連接
- TCP 客戶端程序一般不需要綁定端口號,因為客戶端是主動發(fā)起建立連接的。
- TCP 服務端程序必須綁定端口號,否則客戶端找不到這個 TCP 服務端程序。
- listen 后的套接字是被動套接字,只負責接收新的客戶端的連接請求,不能收發(fā)消息。
- 當 TCP 客戶端程序和 TCP 服務端程序連接成功后, TCP 服務器端程序會產生一個新的套接字,收發(fā)客戶端消息使用該套接字。
- 關閉 accept 返回的套接字意味著和這個客戶端已經通信完畢。
- 關閉 listen 后的套接字意味著服務端的套接字關閉了,會導致新的客戶端不能連接服務端,但是之前已經接成功的客戶端還能正常通信。
- 當客戶端的套接字調用 close 后,服務器端的 recv 會解阻塞,返回的數(shù)據長度為0,服務端可以通過返回數(shù)據的長度來判斷客戶端是否已經下線,反之服務端關閉套接字,客戶端的 recv 也會解阻塞,返回的數(shù)據長度也為0。
案例:多任務版TCP服務端程序開發(fā)
1. 需求
目前我們開發(fā)的TCP服務端程序只能服務于一個客戶端,如何開發(fā)一個多任務版的TCP服務端程序能夠服務于多個客戶端呢?
完成多任務,可以使用線程,比進程更加節(jié)省內存資源。
2. 具體實現(xiàn)步驟
- 編寫一個TCP服務端程序,循環(huán)等待接受客戶端的連接請求
- 當客戶端和服務端建立連接成功,創(chuàng)建子線程,使用子線程專門處理客戶端的請求,防止主線程阻塞
- 把創(chuàng)建的子線程設置成為守護主線程,防止主線程無法退出。
3. 多任務版TCP服務端程序的示例代碼:
import socket
import threading
# 處理客戶端的請求操作
def handle_client_request(service_client_socket, ip_port):
# 循環(huán)接收客戶端發(fā)送的數(shù)據
while True:
# 接收客戶端發(fā)送的數(shù)據
recv_data = service_client_socket.recv(1024)
# 容器類型判斷是否有數(shù)據可以直接使用if語句進行判斷,如果容器類型里面有數(shù)據表示條件成立,否則條件失敗
# 容器類型: 列表、字典、元組、字符串、set、range、二進制數(shù)據
if recv_data:
print(recv_data.decode("gbk"), ip_port)
# 回復
service_client_socket.send("ok,問題正在處理中...".encode("gbk"))
else:
print("客戶端下線了:", ip_port)
break
# 終止和客戶端進行通信
service_client_socket.close()
if __name__ == '__main__':
# 創(chuàng)建tcp服務端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 設置端口號復用,讓程序退出端口號立即釋放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 綁定端口號
tcp_server_socket.bind(("", 9090))
# 設置監(jiān)聽, listen后的套接字是被動套接字,只負責接收客戶端的連接請求
tcp_server_socket.listen(128)
# 循環(huán)等待接收客戶端的連接請求
while True:
# 等待接收客戶端的連接請求
service_client_socket, ip_port = tcp_server_socket.accept()
print("客戶端連接成功:", ip_port)
# 當客戶端和服務端建立連接成功以后,需要創(chuàng)建一個子線程,不同子線程負責接收不同客戶端的消息
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
# 設置守護主線程
sub_thread.setDaemon(True)
# 啟動子線程
sub_thread.start()
# tcp服務端套接字可以不需要關閉,因為服務端程序需要一直運行
# tcp_server_socket.close()
執(zhí)行結果:
客戶端連接成功: ('172.16.47.209', 51528)
客戶端連接成功: ('172.16.47.209', 51714)
hello1 ('172.16.47.209', 51528)
hello2 ('172.16.47.209', 51714)
socket的send和recv原理剖析
1. 認識TCP socket的發(fā)送和接收緩沖區(qū)
當創(chuàng)建一個TCP socket對象的時候會有一個發(fā)送緩沖區(qū)和一個接收緩沖區(qū),這個發(fā)送和接收緩沖區(qū)指的就是內存中的一片空間。
2. send原理剖析
send是不是直接把數(shù)據發(fā)給服務端?
不是,要想發(fā)數(shù)據,必須得通過網卡發(fā)送數(shù)據,應用程序是無法直接通過網卡發(fā)送數(shù)據的,它需要調用操作系統(tǒng)接口,也就是說,應用程序把發(fā)送的數(shù)據先寫入到發(fā)送緩沖區(qū)(內存中的一片空間),再由操作系統(tǒng)控制網卡把發(fā)送緩沖區(qū)的數(shù)據發(fā)送給服務端網卡 。
3. recv原理剖析
recv是不是直接從客戶端接收數(shù)據?
不是,應用軟件是無法直接通過網卡接收數(shù)據的,它需要調用操作系統(tǒng)接口,由操作系統(tǒng)通過網卡接收數(shù)據,把接收的數(shù)據寫入到接收緩沖區(qū)(內存中的一片空間),應用程序再從接收緩存區(qū)獲取客戶端發(fā)送的數(shù)據。
4. send和recv原理剖析圖

說明:
- 發(fā)送數(shù)據是發(fā)送到發(fā)送緩沖區(qū)
- 接收數(shù)據是從接收緩沖區(qū) 獲取
到此這篇關于Python網絡編程(二)編寫TCP協(xié)議程序的文章就介紹到這了,更多相關Python TCP協(xié)議程序內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python中openpyxl和xlsxwriter對Excel的操作方法
這篇文章主要介紹了python中openpyxl和xlsxwriter對Excel的操作方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
python pandas實現(xiàn)excel轉為html格式的方法
今天小編就為大家分享一篇python pandas實現(xiàn)excel轉為html格式的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10
Python中pytest命令行實現(xiàn)環(huán)境切換
在自動化測試過程中經常需要在不同的環(huán)境下進行測試驗證,所以寫自動化測試代碼時需要考慮不同環(huán)境切換的情況,本文主要介紹了Python中pytest命令行實現(xiàn)環(huán)境切換,感興趣的可以了解一下2023-07-07
Python+PyQt5實現(xiàn)數(shù)據庫表格動態(tài)增刪改
這篇文章主要為大家介紹如何利用Python中的PyQt5模塊實現(xiàn)對數(shù)據庫表格的動態(tài)增刪改,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2022-03-03
Python實現(xiàn)的樸素貝葉斯算法經典示例【測試可用】
這篇文章主要介紹了Python實現(xiàn)的樸素貝葉斯算法,結合實例形式詳細分析了Python實現(xiàn)與使用樸素貝葉斯算法的具體操作步驟與相關實現(xiàn)技巧,需要的朋友可以參考下2018-06-06
Python使用for實現(xiàn)無限循環(huán)的多種方式匯總
這篇文章主要介紹了Python使用for實現(xiàn)無限循環(huán)的多種方式匯總,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03

