24 TCP黏包问题

黏包问题只发生在TCP传输的时候,UDP不存在黏包问题。因为TCP是面向“流”的协议,在传输的过程中,TCP有缓存机制来避免数据丢失,本质是在接受数据的时候,不知道发送的数据的大小。UDP是面向“包”的传输。

#server端
from socket import *
ip_port=('127.0.0.1',8080)

tcp_socket_server=socket()
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn,addr=tcp_socket_server.accept()
data1=conn.recv(10)
data2=conn.recv(10)
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()
#client端
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'.encode('utf-8'))
s.send('egg'.encode('utf-8'))

输出:(先运行server端,在运行client端)
在这里插入图片描述
可以看到,上述的运行结果为client端发送的两次消息一起发给了server端,这是因为tcp有优化措施,会根据server端能接收的数据大小,将收到的消息整合到一起。

黏包

当client端想server端发消息的时候,server端接收包的大小不能装下client发过来的消息,就会发生黏包现象。黏包的时候,当发送的数据过多,没有装下的时候,TCP有缓存机制,会把没有装下的消息放在缓存中,当server端再次接收数据的时候,会先把上次没有装下的消息发过来,上次传输的消息传输完之后,才会往下面传输新的消息。

解决方法

引入struct模块,struct模块可以将要传输的数据转成固定的长度。
借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节的数字,因此可以利用这个特点来预先发送数据长度。

发送时 接收时
先发送struct转换好的数据长度(4字节) 先接受4个字节,使用struct转换成数字来获取要接受的数据长度
再发数据 再按照长度接收数据
#server端
import socket
import struct
sk = 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)
    struct.unpack('i',num)[0]	#解包,将得到的元组取出第一个元素,即得到数据的大小长度。
    res = conn.recv(int(num)).decode('gbk')
    print(res)
conn.close()
sk.close()
#client端
import socket,subprocess
import struct
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)
    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)	#‘i’表示int,就是将要把一个数字转换成固定长的的bytes,得到一个元组。
    sk.send(num_by)
    sk.send(std_out)
    sk.send(std_err)
sk.close()

猜你喜欢

转载自blog.csdn.net/weixin_43265998/article/details/89742710
24