Sticky package phenomenon: multiple packages stick together, that is, the result received this time is the result of the last time
bug1: The server uses the "+" sign when replying to the data
bug2: client specified to receive 1024 bytes
The principle of sticky bag generation :
- Neither recv nor send directly receives the other party's data, but the operating system memory, not a send corresponding to a recv
- recv: wait data takes a long time, send: copy data
The sticky packet problem only occurs in TCP, not in UDP
The system will combine the small amount of data and the interval time interval through the algorithm to send together
Simple way to solve the sticky package problem:
Before sending a question, send the data message to the other party so that the other party knows how to receive the data.
Customize the fixed-length data header, and the other party will receive the fixed-length data
Supplementary knowledge: data packaging
import struct #packed data
'l' mode has no rules for packed data res=struct.pack( ' i ' ,11111 ) print (res,type(res),len(res)) #unpacked data obj=struct.unpack( ' i ' ,res) print (obj)
Server:
import socket import subprocess import struct phone = socket.socket (socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) phone.listen(5) while True: conn,client_addr = phone.accept() print (client_addr) while True: try : # 1. Receive the command cmd=conn.recv(1024 ) if not cmd: break # 2. Execute the command and get the result obj=subprocess.Popen (cmd.decode( ' gbk ' ),shell=True,stdout=subprocess.PIPE,stderr= subprocess.PIPE) stdout=obj.stdout.read() stderr = obj.stderr.read() #First step : make a fixed-length header total_size=len(stdout)+ len(stderr) header =struct.pack( ' i ' ,total_size) #The second step, send the header conn.send(header) # b, and then send the real data conn.send(stdout+stderr) # The "+" sign needs to be optimized except ConnectionResetError: break conn.close() #Connection close phone.close() #Socket close
- Make a fixed-length header
- send header to client
- send real data
Client:
import socket import struct client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) client.connect(( ' 127.0.0.1 ' ,8080 )) #communication loop while True: cmd =input( ' >> ' ).strip() if not cmd: continue #Avoid the user entering an empty string and cause the client to freeze client.send(cmd.encode( ' utf-8 ' )) # 1. Take it first To the data length (packet header), get valid data header=client.recv(4 ) total_size=struct.unpack('i',header)[0] recv_size=0 recv_data =b '' #Exit the loop until it is received in the loop, and receive data again while recv_size < total_size: res=client.recv(1024) recv_data+=res recv_size += len(res) print (recv_data.decode( ' gbk ' )) #Close the connection client.close()
- header first
- Parse out the contempt information (data length) of the real data from the header
- receive real data
The ultimate solution to the sticky package problem:
server:
- Customize a header in the form of a dictionary, containing all the information of the file
- Use the struct module to pack file information into a fixed-length header
- Send fixed-length file information
- send real data
import socket,os import subprocess import struct import json phone = socket.socket (socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) phone.listen(5) while True: conn,client_addr = phone.accept() print (client_addr) while True: try : # 1. Receive the command cmd=conn.recv(1024 ) if not cmd: break # 2. Execute the command and get the result obj=subprocess.Popen (cmd.decode( ' gbk ' ),shell=True,stdout=subprocess.PIPE,stderr= subprocess.PIPE) stdout=obj.stdout.read() stderr = obj.stderr.read() #First step : make a fixed-length header total_size=len(stdout)+ len(stderr) header_dic={'filename':"a.txt", 'md5':'xxxxxxx', 'total_size':len(stdout)+len(stderr)} header_json=json.dumps(header_dic) header_bytes=header_json.encode('utf-8') header =struct.pack( ' i ' ,len(header_bytes)) #The second step, first send the length of the header conn.send(header) #The third step, send the header conn.send(header_bytes) #The fourth step, send Real data conn.send(stdout+stderr) # The "+" sign needs to be optimized except ConnectionResetError: break conn.close() #Connection close phone.close() #Socket close
client:
- Receive a fixed-length header
- Get the file information packet size from the header
- Get file information data
- Get real data size from file info data
- receive real data
import socket # client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # client.connect(('127.0.0.1',8080)) # client.send('hello'.encode('gbk')) # client .send('world'.encode('gbk')) # # while True: # # #1, send command # # cmd=input('>> ').strip() # # if not cmd:continue # # client.send(cmd.encode('gbk')) # # #2, get the result and print it # # data=client.recv(1024).decode('gbk')# 1024 is the pit # # print(data) # client.close() import socket import struct import json client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) client.connect(( ' 127.0.0.1 ' ,8080 )) #communication loop while True: cmd =input( ' >> ' ).strip() if not cmd: continue #Avoid the user entering an empty string and cause the client to freeze client.send(cmd.encode( ' utf-8 ' )) # 1. Take it first To the data length (packet header), get valid data header=client.recv(4 ) header_size =struct.unpack( ' i ' ,header)[0] # 2. Receive header header_bytes= client. recv(header_size) # 3. Get useful data from header header_json=header_bytes.decode( ' utf-8 ' ) header_dic=json.loads(header_json) print(header_json) total_size=header_dic['total_size'] recv_size=0 recv_data =b '' #Exit the loop until it is received in the loop, and receive data again while recv_size < total_size: res=client.recv(1024) recv_data+=res recv_size += len(res) print (recv_data.decode( ' gbk ' )) #Close the connection client.close()