Python socket庫(kù)實(shí)現(xiàn)原生TCP請(qǐng)求
一、前言
我們平時(shí)調(diào)用接口使用requests、aiohttp,底層全部都是TCP協(xié)議。HTTP只是構(gòu)建在TCP之上的應(yīng)用層協(xié)議。
很多運(yùn)維、后端場(chǎng)景需要直接操作TCP裸連接:端口連通性探測(cè)、自定義報(bào)文通信、服務(wù)心跳、Socket長(zhǎng)連接。
TCP是面向連接的可靠傳輸協(xié)議,通信分為三步:建立連接 → 收發(fā)數(shù)據(jù) → 斷開(kāi)連接。
本文使用Python內(nèi)置socket庫(kù),不依賴任何第三方庫(kù),從零實(shí)現(xiàn)TCP客戶端與服務(wù)端通信。
二、TCP與UDP核心區(qū)別
- TCP需要先三次握手建立連接,UDP直接發(fā)包;
- TCP保證數(shù)據(jù)有序、不丟失;UDP只管發(fā)送,不保障送達(dá);
- TCP使用SOCK_STREAM,UDP使用SOCK_DGRAM;
- TCP適合文件傳輸、接口調(diào)用;UDP適合廣播、實(shí)時(shí)數(shù)據(jù)流。
三、基礎(chǔ)TCP客戶端:短連接請(qǐng)求
短連接:連接建立,收發(fā)一次數(shù)據(jù),立刻關(guān)閉。
核心方法:
- socket() 創(chuàng)建套接字
- connect() 連接服務(wù)端IP端口
- send() 發(fā)送字節(jié)數(shù)據(jù)
- recv() 接收返回?cái)?shù)據(jù)
import socket
# 創(chuàng)建TCP套接字 SOCK_STREAM = TCP
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 目標(biāo)服務(wù)端地址
host = "127.0.0.1"
port = 9000
try:
# 發(fā)起三次握手,建立TCP連接
client.connect((host, port))
# 發(fā)送二進(jìn)制數(shù)據(jù)
client.send("Hello TCP Server".encode("utf-8"))
# 阻塞等待接收服務(wù)端返回?cái)?shù)據(jù),緩沖區(qū)1024字節(jié)
recv_data = client.recv(1024)
print("收到服務(wù)端返回:", recv_data.decode("utf-8"))
except ConnectionRefusedError:
print("端口未開(kāi)放,TCP連接失敗")
finally:
# 關(guān)閉連接,釋放資源
client.close()
四、TCP服務(wù)端:監(jiān)聽(tīng)端口并處理連接
服務(wù)端固定端口監(jiān)聽(tīng),等待客戶端接入。
關(guān)鍵函數(shù):
- bind() 綁定IP和端口
- listen() 開(kāi)啟監(jiān)聽(tīng)
- accept() 阻塞等待新客戶端接入
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定0.0.0.0,允許局域網(wǎng)所有設(shè)備訪問(wèn)
server.bind(("0.0.0.0", 9000))
# 開(kāi)啟監(jiān)聽(tīng),最大掛起隊(duì)列5
server.listen(5)
print("TCP服務(wù)端已啟動(dòng),監(jiān)聽(tīng)9000端口")
while True:
# 阻塞等待客戶端連接
conn, addr = server.accept()
print(f"新客戶端接入:{addr}")
# 接收客戶端消息
data = conn.recv(1024)
print("收到消息:", data.decode())
# 回復(fù)數(shù)據(jù)
conn.send("消息已收到".encode("utf-8"))
# 關(guān)閉本次客戶端連接(短連接模式)
conn.close()
運(yùn)行順序:先啟動(dòng)服務(wù)端,再運(yùn)行客戶端,即可完成一次TCP通信。
五、TCP長(zhǎng)連接:不斷開(kāi)持續(xù)收發(fā)數(shù)據(jù)
短連接每次通信都要重新握手,開(kāi)銷很大。長(zhǎng)連接建立一次連接,反復(fù)收發(fā)多條消息。
客戶端長(zhǎng)連接代碼
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 9000))
# 循環(huán)發(fā)送多條數(shù)據(jù),連接不斷開(kāi)
for i in range(3):
msg = f"第{i+1}條消息".encode("utf-8")
client.send(msg)
res = client.recv(1024)
print("接收:", res.decode())
client.close()
服務(wù)端改造(持續(xù)讀取數(shù)據(jù))
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("0.0.0.0", 9000))
server.listen(5)
while True:
conn, addr = server.accept()
print("客戶端已連接")
# 持續(xù)讀取客戶端消息
while True:
data = conn.recv(1024)
if not data:
# 客戶端關(guān)閉連接,recv會(huì)拿到空字節(jié)
break
print(data.decode())
conn.send("ok".encode())
conn.close()
六、實(shí)戰(zhàn)場(chǎng)景1:TCP端口存活檢測(cè)
運(yùn)維最常用場(chǎng)景:批量探測(cè)服務(wù)器端口是否開(kāi)放,本質(zhì)就是嘗試建立TCP連接。
import socket
def check_tcp_port(host, port, timeout=2):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
try:
sock.connect((host, port))
return True, "端口開(kāi)放"
except Exception:
return False, "端口關(guān)閉或無(wú)法連接"
finally:
sock.close()
# 測(cè)試
print(check_tcp_port("www.baidu.com", 80))
print(check_tcp_port("127.0.0.1", 9999))
這個(gè)端口探測(cè)比UDP探測(cè)更加可靠,廣泛用于內(nèi)網(wǎng)資產(chǎn)掃描、節(jié)點(diǎn)健康檢測(cè)。
七、實(shí)戰(zhàn)場(chǎng)景2:手動(dòng)發(fā)送原始HTTP報(bào)文(裸TCP訪問(wèn)網(wǎng)頁(yè))
HTTP協(xié)議就是文本TCP報(bào)文,我們不用requests,直接用TCP套接字訪問(wèn)網(wǎng)頁(yè):
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("www.baidu.com", 80))
# 原始HTTP GET報(bào)文
http_msg = b"GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n"
client.send(http_msg)
# 接收網(wǎng)頁(yè)響應(yīng)
response = client.recv(4096)
print(response.decode("utf-8", errors="ignore"))
client.close()
運(yùn)行就能拿到百度的HTTP響應(yīng)頭與網(wǎng)頁(yè)正文,可以直觀理解HTTP是封裝在TCP之上的應(yīng)用層協(xié)議。
八、TCP開(kāi)發(fā)常見(jiàn)問(wèn)題與避坑
recv阻塞卡死:recv默認(rèn)無(wú)限等待,可以設(shè)置sock.settimeout(3)超時(shí)斷開(kāi)。
粘包問(wèn)題:TCP是流式字節(jié)流,沒(méi)有數(shù)據(jù)包邊界,多條數(shù)據(jù)會(huì)黏在一起。自定義通信協(xié)議時(shí),必須加數(shù)據(jù)長(zhǎng)度分隔符。
客戶端沒(méi)有正常close,導(dǎo)致端口被占用(TIME_WAIT):程序一定要正常關(guān)閉套接字;服務(wù)端可以開(kāi)啟端口復(fù)用:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
數(shù)據(jù)必須為bytes字節(jié)串:字符串一定要encode,不能直接發(fā)送str字符串。
單線程服務(wù)端只能處理一個(gè)客戶端:上面的服務(wù)端是單線程,一次只能服務(wù)一個(gè)連接,高并發(fā)場(chǎng)景需要搭配多線程/多協(xié)程處理每一條TCP連接。
九、異步TCP(asyncio高級(jí)用法)
高并發(fā)大量TCP探測(cè)時(shí),同步socket效率太低,可以使用asyncio創(chuàng)建異步TCP連接,上萬(wàn)條連接輕松調(diào)度,占用內(nèi)存極低,非常適合批量端口巡檢。
十、總結(jié)
- TCP核心流程:創(chuàng)建套接字 → connect建立連接 → send/recv收發(fā)數(shù)據(jù) → close斷開(kāi);
- 短連接一發(fā)一關(guān),長(zhǎng)連接復(fù)用TCP通道減少握手開(kāi)銷;
- 原生Socket TCP可以實(shí)現(xiàn)端口探測(cè)、自定義報(bào)文、裸HTTP請(qǐng)求;
- requests、urllib、爬蟲(chóng)框架底層全部都是對(duì)TCP Socket的封裝。
原生TCP網(wǎng)絡(luò)編程是后端、運(yùn)維、網(wǎng)絡(luò)爬蟲(chóng)的基本功,掌握裸Socket通信,才能真正理解互聯(lián)網(wǎng)數(shù)據(jù)傳輸?shù)牡讓舆壿嫛?/p>
如果你想,我可以再續(xù)寫一版:多線程TCP服務(wù)端 + 異步批量TCP端口檢測(cè)代碼。
到此這篇關(guān)于Python socket庫(kù)實(shí)現(xiàn)原生TCP請(qǐng)求的文章就介紹到這了,更多相關(guān)Python socket實(shí)現(xiàn)TCP請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python 基于隊(duì)列實(shí)現(xiàn) tcp socket 連接池的方法
- python的tkinter、socket庫(kù)開(kāi)發(fā)tcp的客戶端和服務(wù)端詳解
- python socket發(fā)送TCP數(shù)據(jù)方式
- Python?socket之TCP通信及下載文件的實(shí)現(xiàn)
- Python socket如何實(shí)現(xiàn)服務(wù)端和客戶端數(shù)據(jù)傳輸(TCP)
- Python基于socket實(shí)現(xiàn)TCP客戶端和服務(wù)端
- python用socket實(shí)現(xiàn)協(xié)議TCP長(zhǎng)連接框架
- Python實(shí)現(xiàn)Socket通信建立TCP反向連接
相關(guān)文章
在PyCharm中控制臺(tái)輸出日志分層級(jí)分顏色顯示的方法
今天小編就為大家分享一篇在PyCharm中控制臺(tái)輸出日志分層級(jí)分顏色顯示的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07
python如何實(shí)現(xiàn)excel數(shù)據(jù)添加到mongodb
本文介紹了python是如何實(shí)現(xiàn)excel數(shù)據(jù)添加到mongodb,為了將數(shù)據(jù)導(dǎo)入mongodb,引入了pymongo,xlrd包,需要的朋友可以參考下2015-07-07
Python?Matplotlib繪制箱線圖boxplot()函數(shù)詳解
箱線圖一般用來(lái)展現(xiàn)數(shù)據(jù)的分布(如上下四分位值、中位數(shù)等),同時(shí)也可以用箱線圖來(lái)反映數(shù)據(jù)的異常情況,下面這篇文章主要給大家介紹了關(guān)于Python?Matplotlib繪制箱線圖boxplot()函數(shù)的相關(guān)資料,需要的朋友可以參考下2022-07-07
Python3簡(jiǎn)單實(shí)現(xiàn)串口通信的方法
今天小編就為大家分享一篇Python3簡(jiǎn)單實(shí)現(xiàn)串口通信的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
回歸預(yù)測(cè)分析python數(shù)據(jù)化運(yùn)營(yíng)線性回歸總結(jié)
本文主要介紹了python數(shù)據(jù)化運(yùn)營(yíng)中的線性回歸一般應(yīng)用場(chǎng)景,常用方法,回歸實(shí)現(xiàn),回歸評(píng)估指標(biāo),效果可視化等,并采用了回歸預(yù)測(cè)分析的數(shù)據(jù)預(yù)測(cè)方法2021-08-08
Python自動(dòng)化實(shí)現(xiàn)Excel文件與CSV文件的互相轉(zhuǎn)換
本文將深入探討如何利用Python,特別是借助spire.xls for python庫(kù),實(shí)現(xiàn)Excel文件到CSV文件的自動(dòng)化轉(zhuǎn)換,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-09-09
以SortedList為例詳解Python的defaultdict對(duì)象使用自定義類型的方法
這篇文章主要介紹了以SortedList為例詳解Python的defaultdict對(duì)象使用自定義類型的方法,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07

