TCP粘包问题

一、粘包问题

1、粘包问题

  接收方:我不知道我要接收的数据的总长度

  发送方:由于tcp协议的内部优化算法:会将数据量比较小的并且时间间隔比较短的数据一次性打包发送

2、如何解决

  第一步:发送数据直接先告诉对方数据量的大小,让对方知道怎么接收你的数据。

  第二步:利用struct模块定制我们自己的消息传输协议:制作字典报头

3、最终版本解决粘包问题

  客户端:

    ①制作字典报头

    ②发送报头

    ③再发送字典

    ④再发你的真实数据

  服务端:

    ①先收4个长度的报头

    ②解析报头,获取字典长度

    ③接收字典(反序列化) ——>> 获取字典里面所有信息

    ④接收真实数据

 4、代码(简单版)

服务端:

客户端:

 5、代码(终极版)

服务端:

 

 客户端:

 

六、struct模块使用

import struct

data = 'dfsafsagdsfgfafsdafsdafafryuio'

# 服务端
res = struct.pack('i', len(data))     # 打包:把一个数据打包为长度固定为4
print(len(res))   # 4
print(res)        # b'\x1e\x00\x00\x00'

# 客户端
ret = struct.unpack('i', res)[0]      # 解包:数据真实长度
print(ret)        # 30

 

二、大文件上传(解决粘包问题代码)

①客户端

import socket, os, json, struct

client = socket.socket()
client.connect(('127.0.0.1', 8080))
# 文件大小
file_size = os.path.getsize(r'D:\Desktop\wendang.txt')

# 文件名
file_name = '计算机五大.txt'

# 然后定义一个字典
d = {
    'file_name': file_name,
    'file_size': file_size,
    'msg': '成功上传'
}
# 定义好字典后,先去转一下json,然后encode
data_bytes = json.dumps(d).encode('utf-8')

# 制作字典的报头
header = struct.pack('i', len(data_bytes))

# 发送报头
client.send(header)

# 发送字典
client.send(data_bytes)

# 然后发送真实数据:打开文件,一行一行读取出来,一行一行的发送
with open(r'D:\Desktop\wendang.txt', 'rb') as f:
    for line in f:
        client.send(line)

②服务端

import socket, json, struct

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

while True:
    conn, addr = server.accept()
    while True:
        try:
            # 先接收报头
            header = conn.recv(4)

            # 解析报头,获取字典长度
            header_len = struct.unpack('i', header)[0]

            # 接收字典
            header_bytes = conn.recv(header_len)
            header_dic = json.loads(header_bytes.decode('utf_8'))
            print(header_dic)    # 打印字典

            # 循环接收文件,存储到本地
            file_size = header_dic.get('file_size')
            file_name = header_dic.get('file_name')
            recv_size = 0

            # 文件操作
            with open(file_name, 'wb') as f:
                while recv_size < file_size:    # 循环接收
                    data = conn.recv(1024)
                    f.write(data)
                    recv_size += len(data)
            print(header_dic.get('msg'))   # 文件上传成功后的提示信息
          
        except ConnectionResetError:
            break
    conn.close()

 

 

posted @ 2019-05-06 22:43  TianShu  Views(653)  Comments(0Edit  收藏  举报