標籤:inf unp 服務 put accept gpo 需要 模組 最佳化演算法
黏包黏包的起因:
連續send多個小資料,會發生黏包現象,這個是tcp協議最佳化演算法造成的
當發送一個資料超過本次接收的最大範圍之後,剩下的資料會留到下次接收時接收
黏包的現象:
import socketsk = socket.socket()sk.bind((‘127.0.0.1‘,8090))sk.listen()conn,addr = sk.accept()ret = conn.recv(2) #在這裡先接收一個兩位元組的內容ret2 = conn.recv(10) #然後在這裡在接收一個長度為10位元組的內容print(ret)print(ret2)conn.close()sk.close()
import socketsk = socket.socket()sk.connect((‘127.0.0.1‘,8090))sk.send(b‘helloeve‘) #在這裡發送一個長度為8的位元組sk.close()
b‘he‘
b‘lloeve‘
可以看見接收的內容被分開了
import socketsk = socket.socket()sk.bind((‘127.0.0.1‘,8090))sk.listen()conn,addr = sk.accept()ret1 = conn.recv(12) #在這裡接收兩次print(ret1)ret2 = conn.recv(12)print(ret2)conn.close()sk.close()
import socketsk = socket.socket()sk.connect((‘127.0.0.1‘,8090))sk.send(b‘hello‘) #這裡連續發送兩次資料sk.send(b‘eve‘)sk.close()
b‘helloeve‘
b‘‘
原本預計的接收兩個內容被合成一個接收了
這就是黏包
所以,黏包的根本原因就是,接收方不知道本次接收的資料具體大小,才會發生黏包
如何解決黏包
竟然知道是什麼原因才發生黏包,那麼,只要每次發送資料前,告訴對方我要發多少資料不就行了
import socketsk = socket.socket()sk.bind((‘127.0.0.1‘,8080))sk.listen()conn,addr = sk.accept()while True: cmd = input(‘>>>‘) if cmd == ‘q‘: conn.send(b‘q‘) break conn.send(cmd.encode(‘gbk‘)) num = conn.recv(1024).decode(‘utf-8‘) #將接到的長度賦給一個變數 conn.send(b‘ok‘) #想使用者端發送訊息表示已經接收到長度 res = conn.recv(int(num)).decode(‘gbk‘) # 將接收到的長度值給接收位元組數 print(res)conn.close()sk.close()
import socketimport subprocess #調用subprocess模組sk = socket.socket()sk.connect((‘127.0.0.1‘,8080))while True: cmd = sk.recv(1024).decode(‘gbk‘) if cmd == ‘q‘: break res = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, #將取到的東西放到管道裡 stderr=subprocess.PIPE) # PIPE 管道 std_out = res.stdout.read() #將管道裡的資料賦給一個變數 std_err = res.stderr.read() #因為管道裡的東西只能取一次 sk.send(str(len(std_out)+len(std_err)).encode(‘utf-8‘)) #叫測量的長度發給服務端 sk.recv(1024) #接收服務端的反饋 sk.send(std_out) # 將資料發過去 sk.send(std_err)sk.close()
這樣每次發送前都將本次要發送的資料位元組數發過去,讓對方對應接收就不會發生黏包現象了
不過,雖然不會黏包,但是每次發送都需要一次互動,增加了代碼的工作
怎麼才能一次互動就解決這些問題呢
這時候就要用到
struct模組
struct模組可以把一個類型轉成固定長度的bytes
我們這次只需要用到int
import structimport socketsk = socket.socket()sk.bind((‘127.0.0.1‘,8080))sk.listen()conn,addr = sk.accept()while True: cmd = input(‘>>>‘) if cmd == ‘q‘: conn.send(b‘q‘) break conn.send(cmd.encode(‘gbk‘)) num = conn.recv(4) #接受用戶端的傳來的 num = struct.unpack(‘i‘,num)[0] #反向得到位元組數 res = conn.recv(int(num)).decode(‘gbk‘) #將位元組數傳給接收端當參數 print(res) conn.close()sk.close()
import struct #這時候就需要用到struct模組import socketimport subprocesssk = socket.socket()sk.connect((‘127.0.0.1‘,8080))while True: cmd = sk.recv(1024).decode(‘gbk‘) if cmd == ‘q‘: break res = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() len_num = len(std_out)+len(std_err) #將計算的位元組數賦給一個變數 num_by = struct.pack(‘i‘,len_num) #在將位元組數用strect模組轉成長度為4的bytes sk.send(num_by) #然後將它發給服務端 sk.send(std_out) sk.send(std_err)sk.close()
在網路上傳輸的所有資料 都叫資料包
資料包裡的所有資料 都叫報文
報文裡不止有你的資料 還有 ip地址 mac地址 連接埠號碼
所有的報文 都有 前序
python 黏包