tcp的粘包現象與解決方案
阿新 • • 發佈:2018-11-23
粘包現象:
粘包1:連續的小包,會被優化機制給合併
粘包2:服務端一次性無法完全就收完客戶端傳送的資料,第二再次接收的時候,會接收到第一次遺留的內容
模擬一個粘包現象
服務端
import socket server = socket.socket(type=socket.SOCK_STREAM) ip_port = ('127.0.0.1',8080) server.bind(ip_port) server.listen() conn, addr = server.accept() # 粘包1:連續的小包,會被優化機制給合併 recv_mes1 = conn.recv(1024)# 第二次拿到的就是空資料,因為客戶端關閉,服務端會自動執行後續的recv,如果客戶端沒關閉,服務端就會在recv # 處阻塞 recv_mes2 = conn.recv(1024) print('第一次接收',recv_mes1.decode('utf-8')) print('第二次接收',recv_mes2.decode('utf-8')) conn.close() server.close()
客戶端
import socket client = socket.socket(type=socket.SOCK_STREAM) ip_port = ('127.0.0.1',8080) client.connect(ip_port) client.send('哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈'.encode('utf-8')) client.send('呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵'.encode('utf-8')) client.close()
程式碼執行結果:
客戶端兩次發的內容都被服務端第一個recv接收了,這即為粘包!
粘包解決方案一:
先告訴客戶端,資料資訊的長度,然後等客戶端確認之後,再發送真實內容
程式碼示例:
import socket # 服務端# 讀者可以自行建立一個txt文件,裡面放的內容勁量大於1024位元組,可以放一篇文章進去 with open('test.txt',mode='rb') as f: content = f.read() data_len = len(content) print('文章長度:',data_len) data_len_str = str(data_len) count = 0 server = socket.socket(type=socket.SOCK_STREAM) ip_port = ('127.0.0.1',8080) server.bind(ip_port) server.listen() conn, addr = server.accept() print(addr,'已經連線') conn.send(data_len_str.encode('utf-8')) status = conn.recv(1024) if status == b'ok': while count <data_len: send_mes = content[count:count+1024] len_of_mes = len(send_mes) conn.send(send_mes) count +=len_of_mes server.close()
import socket # 客戶端 content = b'' count = 0 client = socket.socket(type=socket.SOCK_STREAM) ip_port = ('127.0.0.1',8080) client.connect(ip_port) data_len_str = client.recv(1024).decode('utf-8') data_len = int(data_len_str) client.send(b'ok') while count < data_len: recv_mes = client.recv(1024) content +=recv_mes count += 1024 print(content.decode('utf-8')) client.close()
粘包解決方案二:
通過struct模組,將要傳送的真實資料的長度進行打包,打包成4個位元組,和真實資料一起一次性發送個客戶端.客戶端取出前4個位元組,通過struct解包獲得後面真實資料的長度,根據這個長度再進行資料的接受
程式碼示例:
import socket import struct # 服務端 server = socket.socket(type=socket.SOCK_STREAM) ip_port = ('127.0.0.1',8080) server.bind(ip_port) server.listen() conn, addr = server.accept() print(addr,'已經連線') with open('test.txt',mode='rb') as f: for data in f: # data = f.readline() data_len = len(data) data_len_b = struct.pack('i',data_len) conn.send(data_len_b+data) input('輸入任意鍵結束') server.close() ---------------------分割線------------------------ # 客戶端 import socket import struct client = socket.socket(type=socket.SOCK_STREAM) ip_port = ('127.0.0.1',8080) client.connect(ip_port) content = b'' while 1: recv_mes = client.recv(4) data_len = struct.unpack('i',recv_mes)[0] recv_mes = client.recv(data_len) print(recv_mes.decode('utf-8'),end='') client.close()