文章目錄
- 版權聲明
- python3編碼轉換
- socket類的使用
- 創建Socket對象
- Socket對象常用方法和參數
- 使用示例
- 服務器端代碼
- 客戶端代碼
- TCP客戶端程序開發流程
- TCP服務端程序開發流程
- TCP網絡應用程序注意點
- socket之send和recv原理剖析
- send原理剖析
- recv原理剖析
- send和recv原理剖析圖
- 多任務版TCP服務端程序開發
版權聲明
- 本博客的內容基于我個人學習黑馬程序員課程的學習筆記整理而成。我特此聲明,所有版權屬于黑馬程序員或相關權利人所有。本博客的目的僅為個人學習和交流之用,并非商業用途。
- 我在整理學習筆記的過程中盡力確保準確性,但無法保證內容的完整性和時效性。本博客的內容可能會隨著時間的推移而過時或需要更新。
- 若您是黑馬程序員或相關權利人,如有任何侵犯版權的地方,請您及時聯系我,我將立即予以刪除或進行必要的修改。
- 對于其他讀者,請在閱讀本博客內容時保持遵守相關法律法規和道德準則,謹慎參考,并自行承擔因此產生的風險和責任。本博客中的部分觀點和意見僅代表我個人,不代表黑馬程序員的立場。
python3編碼轉換
- 在網絡傳輸數據的時候,數據需要先編碼轉化為二進制(bytes)數據類型
函數名 | 說明 |
---|---|
encode | 編碼 將字符串轉化為字節碼 |
decode | 解碼 將字節碼轉化為字符串 |
- encoed()和decode()函數可以接受參數,encoding是指在編解碼過程中使用的編碼方案。
bytes.decode(encoding=“utf-8”) str.encode(encoding=”utf-8”)
socket類的使用
- Python的
socket
類是Python標準庫的一部分,用于提供對低級網絡接口的訪問。支持IPv4、IPv6、TCP、UDP等協議,允許創建基于網絡的應用程序,如服務器和客戶端。
創建Socket對象
- 要使用
socket
類,首先需要導入socket
模塊,然后創建一個socket
對象。可以通過調用socket.socket()
方法并傳遞相應的參數來創建一個socket。import socket # 創建一個socket對象,默認是IPv4和TCP協議 # socket.socket(AddressFamily, Type) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Socket對象常用方法和參數
方法/參數 | 類型/描述 | 示例/默認值 | 說明 |
---|---|---|---|
socket() | 構造函數 | 用于創建socket對象 | |
family | 參數 | socket.AF_INET | 地址族,確定了socket的類型(IPv4或IPv6) |
type | 參數 | socket.SOCK_STREAM | 指定了socket的通信類型(TCP或UDP) |
proto | 參數 | 0 | 協議編號,默認為0,由系統選擇合適的協議 |
fileno | 參數 | None | 可以指定一個文件描述符 |
bind(address) | 方法 | 綁定地址到socket | |
address | 參數 | ('localhost', 12345) | 一個包含主機名和端口號的元組 |
listen(backlog) | 方法 | 開始監聽傳入連接 | |
backlog | 參數 | 5 | 定義了等待隊列的大小 |
accept() | 方法 | 接受一個連接,返回(conn, address) | |
connect(address) | 方法 | 用于客戶端連接服務器 | |
send(bytes) | 方法 | 發送數據,參數為字節數據 | |
recv(bufsize) | 方法 | 接收數據,指定最大數據量 | |
bufsize | 參數 | 1024 | 接收數據的緩沖區大小 |
close() | 方法 | 關閉socket連接 |
使用示例
服務器端代碼
import socket# 創建socket對象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 綁定地址(host,port)
server_socket.bind(('localhost', 12345))# 開始監聽
server_socket.listen()print("服務器啟動,等待連接...")# 接受連接
connection, address = server_socket.accept()
print(f"連接來自 {address}")# 接收數據
data = connection.recv(1024)
print(f"接收到的數據: {data.decode()}")# 發送數據
connection.send("Hello, client!".encode())# 關閉連接
connection.close()
客戶端代碼
import socket# 創建socket對象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 連接服務器
client_socket.connect(('localhost', 12345))# 發送數據
client_socket.send("Hello, server!".encode())# 接收數據
data = client_socket.recv(1024)
print(f"接收到的數據: {data.decode()}")# 關閉連接
client_socket.close()
這僅僅是一個非常簡單的例子,實際應用中還需要處理錯誤、多線程或多進程處理多個連接、非阻塞IO等復雜情況。
TCP客戶端程序開發流程
-
TCP網絡應用程序開發分為:
- TCP客戶端程序開發,運行在服務器設備上的程序,專門為客戶端提供數據服務。
- TCP服務端程序開發,運行在用戶設備上的程序
-
TCP客戶端程序開發流程
- 創建客戶端套接字對象(買電話)
- 和服務端套接字建立連接(打電話)
- 發送數據(說話)
- 接收數據(接聽)
- 關閉客戶端套接字(掛電話)
-
TCP客戶端開發程序流程
- 創建客戶端套接字對象
- 和服務端套接字建立連接
- 發送數據
- 接收數據
- 關閉客戶端套接字
import socketif __name__ == '__main__':# 1.創建客戶端套接字對象tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2.和服務端套接字建立連接tcp_client_socket.connect(("localhost", 8080))# 3.發送數據tcp_client_socket.send("hello".encode(encoding="utf-8"))# 4.接收數據 recv阻塞等待數據的到來recv_data = tcp_client_socket.recv(1024)print(recv_data.decode())# 5.關閉客戶端套接字tcp_client_socket.close()
TCP服務端程序開發流程
- 創建服務端端套接字對象
- 綁定IP地址和端口號
- 設置監聽
- 等待接受客戶端的連接請求
- 接收數據
- 發送數據
- 關閉套接字
import socketif __name__ == '__main__':# 1.創建服務端套接字對象tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2. 綁定IP地址和端口號# bind中的參數第一個ip地址元素設置為"",默認為本機ip地址tcp_server_socket.bind(("", 8080))# 3.設置監聽 128:代表服務端等待排隊連接的最大數量tcp_server_socket.listen(128)# 4.等待接受客戶端的連接請求 accept阻塞等待 返回一個用以和客戶端通socket,客戶端的地址conn_socket, ip_port = tcp_server_socket.accept()print("客戶端地址:", ip_port)# 5.接收數據recv_data = conn_socket.recv(1024)print("接收到的數據:", recv_data.decode())# 6.發送數據conn_socket.send("客戶端你的數據我收到了".encode())# 7.關閉套接字conn_socket.close()tcp_server_socket.close()
TCP網絡應用程序注意點
- 當TCP客戶端程序想要和TCP服務端程序進行通信的時候必須要先建立連接
- TCP客戶端程序一般不需要綁定端口號,因為客戶端是主動發起建立連接的
- TCP服務端程序必須綁定端口號,否則客戶端找不到這個TCP服務端程序。
- listen后的套接字是被動套接字,只負責接收新的客戶端的連接請求,不能收發消息
- 當TCP客戶端程序和TCP服務端程序連接成功后,TCP服務器端程序會產生一個新的套接字,收發客戶端消息使用該套接字.
- 關閉accept返回的套接字意味著和客戶端已經通信完畢 。 當客戶端的套接字調用close后,服務器端的recv會解阻塞,返回的數據長度為0,服務端可以通過返回數據的長度來判斷客戶端是否已經下線,反之服務端關閉套接字,客戶端的recv也會解阻塞,返回的數據長度也為0。
socket之send和recv原理剖析
- 當創建一個TCP socket對象的時候會有一個發送緩沖區和一個接收緩沖區,這個發送和接收緩沖區指的就是內存中的一片空間。
send原理剖析
- send發送數據給服務端:必須得通過網卡發送數據,應用程序是無法直接通過網卡發送數據的,它需要調用操作系統接口,也就是說,應用程序把發送的數據先寫入到發送緩沖區(內存中的一片空間),再由操作系統控制網卡把發送緩沖區的數據發送給服務端網卡。
recv原理剖析
- recv從客戶端接收數據:應用軟件是無法直接通過網卡接收數據的,它需要調用操作系統接口,由操作系統通過網卡接收數據,把接收的數據寫入到接收緩沖區(內存中的一片空間),應用程序再從接收緩存區獲取客戶端發送的數據。
send和recv原理剖析圖
- 不管是recv還是send都不是直接接收到對方的數據和發送數據到對方,發送數據會寫入到發送緩沖區,接收數據是從接收緩沖區來讀取,發送數據和接收數據最終是由操作系統控制網卡來完成。
多任務版TCP服務端程序開發
- 實現步驟分析:
-
編寫一個TCP服務端程序,循環等待接受客戶端的連接請求
while True:service_client_socket, ip_port = tcp_server_socket.accept()
-
當客戶端和服務端建立連接成功,創建子線程,使用子線程專門處理客戶端的請求,防止主線程阻塞
while True:service_client_socket, ip_port = tcp_server_socket.accept()sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))sub_thread.start()
- 完整代碼:
import socket
import threading# 處理客戶端函數
def handle_client(conn_socket):# 5.接收數據recv_data = conn_socket.recv(1024)print("接收到的數據:", recv_data.decode())# 6.發送數據conn_socket.send("收到客戶端數據:".encode())# 7.關閉套接字conn_socket.close()# 1 使用循環接收客戶端的連接請求
if __name__ == '__main__':# 1.創建服務端套接字對象tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 設置端口號復用,讓程序退出端口號立即釋放tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)# 2. 綁定IP地址和端口號# 如果bind中的參數第一個ip地址元素設置為"",默認為本機ip地址tcp_server_socket.bind(("", 8888))# 3.設置監聽 128:代表服務端等待排隊連接的最大數量tcp_server_socket.listen(128)while True:# 4.等待接受客戶端的連接請求 accept阻塞等待 返回一個用以和客戶端通socket,客戶端的地址conn_socket, ip_port = tcp_server_socket.accept()print("客戶端地址:", ip_port)# 使用多線程去接收多個客戶端的請求 設置守護主線程sub_thread = threading.Thread(target=handle_client, args=(conn_socket,), daemon=True)sub_thread.start()tcp_server_socket.close()