sticky bag

Reference Click to open the link

The principle of Socket sending and receiving messages


The sender stores the information to be sent in the buffer, and then transmits it to the buffer of the receiver through the physical layer, and then fetches it from its own buffer when the program wants to read the information.

The sender can send data one K to K, and the application program of the receiving end can take away the data in two K and two K. Of course, it is also possible to take away 3K or 6K data at a time, or only take out a few bytes of data at a time. That is to say, the data seen by the application is a whole, or a stream, and how many bytes of a message are invisible to the application, so the TCP protocol is a stream-oriented protocol, which is also easy The reason for the sticky package problem. UDP is a message-oriented protocol, each UDP segment is a message, and the application must extract data in units of messages, and cannot extract any byte of data at a time, which is very different from TCP. How to define a message? It can be considered that the data that the other party writes/sends at one time is a message. What needs to be understood is that when the other party sends a message, no matter how the bottom layer is segmented, the TCP protocol layer will sort the data segments that make up the entire message. Rendered in the kernel buffer.

The so-called sticky packet problem is mainly caused by the fact that the receiver does not know the boundaries between messages and does not know how many bytes of data to extract at one time.

Note: Only TCP has sticky packets, UDP will never stick to packets

The TCP protocol is connection-oriented, stream-oriented, and provides high-reliability services. It will send packets with smaller interval and smaller amount of data, so that the receiver cannot distinguish the boundary of the message. That is, flow-oriented communication has no message protection boundary.

The UDP protocol is connectionless, message-oriented, and provides efficient services. It packs a header for each message so that the receiver can tell the boundaries of the message. That is, message-oriented communication has message protection boundaries.

Two sticky bags

1. The sender needs to wait for the buffer to be full before sending out, resulting in sticky packets (the time interval for sending data is very short, the data is very small, and they are combined together to generate sticky packets)

Server

from socket import *
ip_port=('218.197.223.92',800)#The address and port of the local server
back_log=5 #half chain pool size
buffer_size=1024 #Number of bytes received

tcp_serve=socket(AF_INET,SOCK_STREAM)#Initialize socket, AF_INET is a socket family based on network model, SOCK_STREAM is data stream mode
tcp_serve.bind(ip_port) #Bind IP and port
tcp_serve.listen(back_log)

conn,addr=tcp_serve.accept()
data1=conn.recv(buffer_size)
print('====>',data1)
data2=conn.recv(buffer_size)
print('===>',data2)
client
from socket import *
ip_port=('218.197.223.92',800) #IP and port of the server to connect to
buffer_size=1024 #Number of bytes to read

tcp_client = socket (AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port) #Connect to the server

tcp_client.send(b'hello')
tcp_client.send(b'world')

2. The receiver does not receive the packets in the buffer in time, causing multiple packets to be received (the client sends a piece of data, the server only receives a small part, and the server will take the last leftover from the buffer next time it receives it again. data, resulting in sticky packets) 

Server

from socket import *
ip_port=('218.197.223.92',800)#The address and port of the local server
back_log=5 #half chain pool size

tcp_serve=socket(AF_INET,SOCK_STREAM)#Initialize socket, AF_INET is a socket family based on network model, SOCK_STREAM is data stream mode
tcp_serve.bind(ip_port) #Bind IP and port
tcp_serve.listen(back_log)

conn,addr=tcp_serve.accept()
data1=conn.recv(2)
print('====>',data1)
data2=conn.recv(6)
print('===>',data2)
client
from socket import *
ip_port=('218.197.223.92',800) #IP and port of the server to connect to
buffer_size=1024 #Number of bytes to read

tcp_client = socket (AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port) #Connect to the server

tcp_client.send(b'hello world')

This is not the case with UDP

Server

from socket import *
ip_port=('218.197.223.92',800)
buffer_size=1024

udp_serve=socket(AF_INET, SOCK_DGRAM)#Create a UDP-based server socket, SOCK_DGRAM is datagram mode
udp_serve.bind(ip_port) #Bind host address and port

data1=udp_serve.recvfrom(buffer_size)
print('===>',data1)
data2=udp_serve.recvfrom(buffer_size)
print('===>',data2)
client
from socket import *
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_client = socket (AF_INET, SOCK_DGRAM)

udp_client.sendto(b'hello',ip_port)
udp_client.sendto(b'world',ip_port)

Advantages and disadvantages of the two protocols:

The recvfrom of udp is blocked. A recvfrom(x) must be sentinto(y) to the only one, and it is completed after receiving x bytes of data. If y>x data is lost, which means that udp will not stick packets at all. However, data will be lost. Unreliable

tcp protocol data will not be lost. If the packet is not received, the next time it is received, it will continue to receive the last time. The own end always clears the buffer content when it receives the ack. Data is reliable, but sticky packets.

sticky bag solution

Server

from socket import *
import subprocess
'''Run command remotely'''
ip_port=('218.197.223.92',800)
back_log=5
buffer_size=1024
tcp_shell_serve=socket(AF_INET,SOCK_STREAM)
tcp_shell_serve.bind(ip_port)
tcp_shell_serve.listen(back_log)

while True:
    print('Start connecting...')
    conn,addr=tcp_shell_serve.accept()
    print('Connected to client: %s'%addr[0])
    while True:
        try:
            cmd=conn.recv(buffer_size)
            if not cmd:break #When the client disconnects normally, the input is empty
            res=subprocess.Popen(cmd.decode('utf-8'), shell=True, #Run the command prompt and execute the instructions, and store the output, exception and input in the pipeline (PIPE) of the subprocess
                                 stdout=subprocess.PIPE, #output
                                 stdin=subprocess.PIPE, #input
                                 stderr=subprocess.PIPE) #Exception
            err=res.stderr.read() #Read exceptions from the pipeline
            if err: #If there is an exception, return an exception
                res_cmd=err
            else:
                res_cmd=res.stdout.read() #Otherwise return the output result (the output result is binary, the encoding method is determined by the system, Windows is gbk encoding)
            if not res_cmd:
                res_cmd='executed successfully'.encode('gbk')

            '''Solve the sticky package'''
            length=len(res_cmd) #Get the length to be transmitted
            conn.send(str(length).encode('gbk'))#Pass the length to the client
            sign=conn.recv(buffer_size) #In order to prevent the sticky package of small data and the following data from being merged, let the client send a message
            if sign==b'ready':
                conn.send(res_cmd) #Send the result to the client
        except Exception: #When the client disconnects abnormally, an exception will be triggered
            print('Client %s disconnected'%addr[0])
            break

tcp_shell_serve.close()


client
from socket import *
ip_port=('218.197.223.92',800) #IP and port of the server to connect to
buffer_size=1024 #Number of bytes to read

tcp_client = socket (AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port) #Connect to the server

#Continue to talk to the server
while True:
    cmd=input('>>:').strip()
    if not cmd:continue #When msg is empty, the data will not be sent, and the server will not receive information, which will cause the program to block
    if cmd=='quit':break
    tcp_client.send(cmd.encode('utf-8'))

    '''Solve the sticky package'''
    data_length=tcp_client.recv(buffer_size) #Get the length of the data to be received
    tcp_client.send(b'ready') #Send a message to the server to prevent sticky packets
    length=int(data_length.decode('gbk'))
    res_n=0
    res_cmd=b''
    while res_n<length: #Use a loop to get the value from the cache until the length is met
        res_cmd+=tcp_client.recv(buffer_size)
        res_n = len (res_cmd)
    print('The received information is',res_cmd.decode('gbk'))
tcp_client.close() #Close the client



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325838870&siteId=291194637
Bag