引言
在互聯網時代,網絡編程
已經成為開發人員
必備的技能之一
。無論是Web
開發、實時通信
還是分布式計算
,都離不開網絡編程
的支持
。Python
提供的socket
模塊為我們提供了簡潔而強大
的接口
,可以輕松實現客戶端
和服務器
之間的通信
。
Socket編程
是網絡編程
的重要部分
,主要用于在不同計算機
之間進行通信
。Python
提供了一個非常強大
的socket庫
,使得網絡編程
變得簡單和靈活
。本篇博文將詳細介紹Python
的socket
編程,包括基礎概念、核心組件、常用功能
,并附上一個綜合的示例及其運行結果。
什么是socket
Socket
是網絡通信的端點,它允許不同的計算機之間進行數據交換
。在Python
中,socket
是通過socket模塊
來實現的。Socket
的通信過程
主要包括創建socket對象
、綁定地址
、監聽連接
、發送和接收數據
等步驟。
socket
套接字類型
在 Python
中,socket
類型主要有以下幾種:
流式套接字stream
(SOCK_STREAM
):提供面向連接
的、可靠的字節流服務
。常用于TCP
協議。數據報套接字dgram
(SOCK_DGRAM
):提供無連接
的、不可靠的數據報服務
。常用于UDP
協議。
TCP
和UDP
TCP
(Transmission Control Protocol
):面向連接的、可靠的協議
,適用于需要保證數據準確傳輸
的場景。UDP
(User Datagram Protocol
):無連接的、不可靠的協議
,適用于需要快速傳輸數據
但不要求數據準確
到達的場景。
socket
服務端核心組件
1.創建socket
對象
使用socket.socket()
函數可以創建一個 socket對象
。該函數的兩個參數分別指定了地址族
和套接字類型
。
import socket#創建名為server的一個socket對象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.AF_INET
:表示使用IPv4
地址。socket.SOCK_STREAM
:表示套接字類型
為流式套接字
,即TCP
。
2.綁定地址
和端口
服務端
需要綁定
一個 IP地址
和端口號
,使得客戶端
可以找到并連接到服務器
。
- 使用
對象名.bind()
進行綁定,bind()
括號中是一個元組類型
的
#綁定ip地址和服務的端口號
server.bind(("127.0.0.1", 10086))
3.監聽連接
服務器端
需要監聽
來自客戶端
的連接請求
。
- 使用
對象名.listen()
函數進行監聽,listen()
括號中是一個int
類型,表示允許
連接的最大連接數
#監聽的最大連接數是3
server.listen(3)
4.接受連接
服務器端
使用 對象名.accept()
函數接受客戶端的連接請求
。
- 該函數會發生
阻塞
,直到有客戶端連接
。 - 如果有
客戶端連接
了,accept()
返回2
個值,第一個是client的socket對象
,第二個是client的ip地址和端口
#通過接受到的客戶端連接,來獲取client的socket對象,從而獲取client發送過來的消息
client_sock, client_address = server.accept()
client_sock
是一個客戶端socket對象
client_address
是客戶端的地址信息
,是一個元組(ip地址, 端口號)
5.接受client端
消息client_sock.revc(1024)
- 使用返回的
client_sock對象
與客戶端
進行數據交換
。可以使用client_sock.recv()
方法接收客戶端
發送的數據, recv()
能接受的最大字節數
為1024
個字節,所以recv()
括號中填寫int類型
的數字
表示字節數
,如果接受的字節數
大于1024
會發生粘包
recv()
方法在接受數據時會發生阻塞
recv()
會返回一個編碼后
的數據
,所以我們在查看數據
的時候,需要進行data.decode("utf-8")
進行解碼
data = client_sock.recv(1024)
print(data.decode("utf-8"))
6.發送響應
給client端
6.1client_sock.send()
發送時
,需要對數據進行編碼
,常使用encode("utf-8")
對數據進行編碼
send()
可能會發送數據的全部
或部分
。如果緩沖區已滿
或網絡狀況不佳
,它可能只會發送部分數據
。send()
方法返回一個整數
,表示實際發送
的字節數
response = "收到了"
byte_number = client_sock.send(response.encode("utf-8"))
print(byte_number) #此時byte_number值為9,因為一個漢字在utf-8中占3個字節
6.2client_sock.sendall()
發送時
,需要對數據進行編碼
,常使用encode("utf-8")
對數據進行編碼
- 如果由于
網絡擁塞
或其他原因
,數據
不能立即發送完畢
,sendall()
會繼續
嘗試發送數據
,直到所有數據
都發送完畢
。
client_sock.sendall("接受到了".encode("utf-8"))
7.關閉client端
連接
使用close()
函數可以關閉socket連接
。
client_sock.close()
8.關閉server
端連接
使用close()
函數可以關閉socket連接
。
server.close()
socket
客戶端核心組件
1.創建socket
對象
使用socket.socket()
函數可以創建一個 socket對象
。該函數的兩個參數分別指定了地址族
和套接字類型
。
import socket#創建名為client的一個socket對象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.AF_INET
:表示使用IPv4
地址。socket.SOCK_STREAM
:表示套接字類型
為流式套接字
,即TCP
。
2.連接server端
client端
需要使用client對象.connect()
函數連接到服務端
。connect()
括號中是一個元組類型
,第一個參數表示服務端ip
,第二個參數表示服務端開發的端口
client.connect(("127.0.0.1", 10086))
3.發送請求
給server端
3.1client.send()
發送時
,需要對數據進行編碼
,常使用encode("utf-8")
對數據進行編碼
send()
可能會發送數據的全部
或部分
。如果緩沖區已滿
或網絡狀況不佳
,它可能只會發送部分數據
。send()
方法返回一個整數
,表示實際發送
的字節數
request = "你好服務端"
byte_number = client.send(request.encode("utf-8"))
print(byte_number) #此時byte_number值為15,因為一個漢字在utf-8中占3個字節
3.2client.sendall()
發送時
,需要對數據進行編碼
,常使用encode("utf-8")
對數據進行編碼
- 如果由于
網絡擁塞
或其他原因
,數據
不能立即發送完畢
,sendall()
會繼續
嘗試發送數據
,直到所有數據
都發送完畢
。
client.sendall("你好服務端".encode("utf-8"))
4.接受server端
響應client.revc(1024)
- 使用
client對象
,可以使用client.recv(1024)
方法接收server端
響應的數據, recv()
能接受的最大字節數
為1024
個字節,所以recv()
括號中填寫int類型
的數字
表示字節數
,如果接受的字節數
大于1024
會發生粘包
recv()
方法在接受數據時會發生阻塞
recv()
會返回一個編碼后
的數據
,所以我們在查看數據
的時候,需要進行data.decode("utf-8")
進行解碼
data = client.recv(1024)
print(data.decode("utf-8"))
5.關閉client端
連接
使用close()
函數可以關閉socket連接
。
client.close()
示例
server端
import socketclass TCPSERVER:def __init__(self, ip, port):self.ip = ipself.port = portself.number = 5 #設置服務端監聽個數def serverTCP(self):server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #tcpserver.bind((self.ip, self.port))server.listen(self.number)client_sock, client_address = server.accept()while True:try: #tcp中不允許client先關機,如果先關機,服務端會報錯,所以這里使用try異常捕獲# 接受client消息data = client_sock.recv(1024)print(data.decode("utf-8"))# 響應clientclient_sock.sendall("響應:".encode("utf-8")+data)except ConnectionAbortedError:breakclient_sock.close()server.close()if __name__ == '__main__':s = TCPSERVER("127.0.0.1", 10080)s.serverTCP()
client端
import socketclass TCPCLIENT:def __init__(self, ip, port):self.server_ip = ipself.server_port = portdef clientTCP(self):client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #tcpclient.connect((self.server_ip, self.server_port))while True:msg = input(">>(quit退出):").strip()if not msg: #tcp不能發送空白消息continueif msg == "quit":break# client端發送數據client.sendall(msg.encode("utf-8"))# client端接受響應data = client.recv(1024)print(data.decode("utf-8"))client.close()if __name__ == '__main__':c = TCPCLIENT("127.0.0.1", 10080)c.clientTCP()
測試
必須先啟動server端
,再啟動client端