目錄python
問題的根源在於,接收端不知道發送端將要傳送的字節流的長度,因此解決粘包的方法就是圍繞,如何讓發送端在發送數據前,把本身將要發送的字節流總大小讓接收端知曉,而後接收端來一個死循環接收完全部數據。算法
import socket, subprocess server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8000)) server.listen(5) while True: conn, addr = server.accept() print('start...') while True: cmd = conn.recv(1024) print('cmd:', cmd) obj = subprocess.Popen(cmd.decode('utf8'), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdout = obj.stdout.read() if stdout: ret = stdout else: stderr = obj.stderr.read() ret = stderr ret_len = len(ret) conn.send(str(ret_len).encode('utf8')) data = conn.recv(1024).decode('utf8') if data == 'recv_ready': conn.sendall(ret) conn.close() server.close()
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 8000)) while True: msg = input('please enter your cmd you want>>>').strip() if len(msg) == 0: continue client.send(msg.encode('utf8')) length = int(client.recv(1024)) client.send('recv_ready'.encode('utf8')) send_size = 0 recv_size = 0 data = b'' while recv_size < length: data = client.recv(1024) recv_size += len(data) print(data.decode('utf8'))
程序的運行速度遠快於網絡傳輸速度,因此在發送一段字節前,先用send去發送該字節流長度,這種方式會放大網絡延遲帶來的性能損耗shell
import struct import json # 'i'是格式 try: obj = struct.pack('i', 1222222222223) except Exception as e: print(e) obj = struct.pack('i', 1222) print(obj, len(obj))
'i' format requires -2147483648 <= number <= 2147483647 b'\xc6\x04\x00\x00' 4
res = struct.unpack('i', obj) print(res[0])
1222
解決粘包問題的核心就是:爲字節流加上自定義固定長度報頭,報頭中包含字節流長度,而後一次send到對端,對端在接收時,先從緩存中取出定長的報頭,而後再取真實數據。json
import json import struct header_dic = { 'filename': 'a.txt', 'total_size': 111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223131232, 'hash': 'asdf123123x123213x' } header_json = json.dumps(header_dic) header_bytes = header_json.encode('utf-8') print(len(header_bytes)) # 'i'是格式 obj = struct.pack('i', len(header_bytes)) print(obj, len(obj))
223 b'\xdf\x00\x00\x00' 4
res = struct.unpack('i', obj) print(res[0])
223
from socket import * import subprocess import struct import json server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8000)) server.listen(5) print('start...') while True: conn, client_addr = server.accept() print(conn, client_addr) while True: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode('utf8'), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stderr = obj.stderr.read() stdout = obj.stdout.read() # 製做報頭 header_dict = { 'filename': 'a.txt', 'total_size': len(stdout) + len(stderr), 'hash': 'xasf123213123' } header_json = json.dumps(header_dict) header_bytes = header_json.encode('utf8') # 1. 先把報頭的長度len(header_bytes)打包成4個bytes,而後發送 conn.send(struct.pack('i', len(header_bytes))) # 2. 發送報頭 conn.send(header_bytes) # 3. 發送真實的數據 conn.send(stdout) conn.send(stderr) conn.close() server.close()
from socket import * import json import struct client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8000)) while True: cmd = input('please enter your cmd you want>>>') if len(cmd) == 0: continue client.send(cmd.encode('utf8')) # 1. 先收4個字節,這4個字節中包含報頭的長度 header_len = struct.unpack('i', client.recv(4))[0] # 2. 再接收報頭 header_bytes = client.recv(header_len) # 3. 從包頭中解析出想要的東西 header_json = header_bytes.decode('utf8') header_dict = json.loads(header_json) total_size = header_dict['total_size'] # 4. 再收真實的數據 recv_size = 0 res = b'' while recv_size < total_size: data = client.recv(1024) res += data recv_size += len(data) print(res.decode('utf8')) client.close()
1.nagle算法規定,TCP協議會將數據量較小、時間間隔短的數據合併爲一條發送給客戶端緩存
from socket import * server = socket(AF_INET, SOCK_STREAM) server.bind(('127.0.0.1', 8080)) server.listen(5) conn, addr = server.accept() # 正確作法,客戶端製做報頭 # res1 = conn.recv(5) # print('第一次;', res1) # res2 = conn.recv(5) # print('第二次;', res2) # res3 = conn.recv(4) # print('第三次;', res3) # low方法+客戶端的睡眠 res1 = conn.recv(1024) print('第一次;', res1) res2 = conn.recv(1024) print('第二次;', res2) res3 = conn.recv(1024) print('第三次;', res3)
from socket import * import time client = socket(AF_INET, SOCK_STREAM) client.connect(('127.0.0.1', 8080)) client.send(b'hello') client.send(b'world') client.send(b'nick') # 服務端收到b'helloworldnick' client.send(b'hello') time.sleep(0.2) # 服務端收到b'hello' client.send(b'world') time.sleep(0.2) # 服務端收到b'world' client.send(b'nick') # 服務端收到b'nick'
2.接收方不及時接收緩衝區的包,形成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候仍是從緩衝區拿上次遺留的數據,產生粘包)網絡
# _*_coding:utf-8_*_ __author__ = 'nickchen121' from socket import * ip_port = ('127.0.0.1', 8080) TCP_socket_server = socket(AF_INET, SOCK_STREAM) TCP_socket_server.bind(ip_port) TCP_socket_server.listen(5) conn, addr = TCP_socket_server.accept() data1 = conn.recv(2) # 一次沒有收完整 data2 = conn.recv(10) # 下次收的時候,會先取舊的數據,而後取新的 print('----->', data1.decode('utf-8')) print('----->', data2.decode('utf-8')) conn.close()
# _*_coding:utf-8_*_ __author__ = 'nickchen121' import socket BUFSIZE = 1024 ip_port = ('127.0.0.1', 8080) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) res = s.connect_ex(ip_port) s.send('hello feng'.encode('utf-8'))