TCP的粘包问题
TCP 流式协议
基于数据流的协议
接收方产生粘包问题
1. 接收方不清楚数据有多长 只接受了数据的一部分
2. 接收方多读了数据
发送方产生的粘包问题
3.操作系统没有及时发送前一次的数据 导致两次数据黏在了一起
TCP nigle优化机制
如果多次发送数据的数据量小并且时间间隔短:会把这一堆数据聚合在一起一次性发送
目的是为了降低网络传输次数
UDP基于数据报的传输协议 不会发生粘包问题 因为数据是以报文的形式存在
每次发送都是以数据报的形式 对方接收的也是一个数据报
粘包产生的根本原因是接收方不清楚数据的长度
解决粘包的核心思路:就是先通知接收方 要发送的数据的长度
再发送真实数据
问题在于 数据长度 也是不确定的
对方也不清楚 长度信息 到底几个字节
想办法将长度信息所占的字节数固定下来
导入struct模块
q为长整型 8位字节
l为整型 4位字节
import struct
# q表示longlong类型 占八个字节
b = struct.pack("q",10000000000000)
print(b)
print(len(b))
# 解包 把二进制还原为整型
# 返回的是一个元组 第0 个表示原始数据
num = struct.unpack("q",b)
print(num)
import socket,struct c=socket.socket() c.connect(('127.0.0.1',8808)) data='hello 我林霄 see you tomorrow'.encode('utf-8') #给接收方发送数据的长度 length=len(data) #将整型的长度 转为固定长度的字节 len_data=struct.pack('q',length) #发送长度信息 c.send(len_data) #发送真实数据 c.send(data)
import socket,struct s=socket.socket() s.bind(('127.0.0.1',8808)) s.listen() c,addr=s.accept() # 先接收长度 # 有可能 长度信息和真实数据也黏在一起 无法取出长度信息 # 解决方案是 把长度信息 转换为一个固定字节数的二进制数据 # 1 4个字节的bytes # 2 4个字节的bytes # 20000 4个字节的bytes # 接收数据的长度信息 length=struct.unpack('q',c.recv(8))[0] print('长度为:%s'%length) data=c.recv(length).decode('utf-8') print(data)
文件传输:

import hashlib def get_MD5(file_name): # 用于计算某个文件的MD5值 with open(file_name,"rb") as f: m = hashlib.md5() while True: data = f.read(1024) if not data: break m.update(f.read()) return m.hexdigest() if __name__ == '__main__': print(get_MD5(r"1.网络协议回顾.mp4"))

""" 发送文件 1.连接服务器 2.打开文件 3.读取文件的长度信息并发送给服务器 4.读取文件内容 5.发送文件数据 不仅要上传文件 还要传一些文件的相关信息 文件名 MD5 """ import socket import struct import json import os import tool c = socket.socket() c.connect(("127.0.0.1",1688)) path = r"D:python6期视频1.网络协议回顾.mp4" # 待发送的两段数据 file_name = "1.网络协议回顾.mp4" MD5 = "ASAASAS121212" # 发送的文件长度 file_size = os.path.getsize(path) #将数据组装为字典 info_dic = {"file_name":file_name,"MD5":tool.get_MD5(path),"file_size":file_size} # 将字典转为json的字符串 json_str = json.dumps(info_dic) # 获取文件信息的长度 json_len = struct.pack("q",len(json_str.encode("utf-8"))) # 发送长度 c.send(json_len) # 发送文件信息 c.send(json_str.encode("utf-8")) # 循环发送文件数据 with open(path,"rb") as f: while True: data = f.read(1024) if not data: break c.send(data)

import socket import struct import json """ 接收文件 1.建立连接 2.获取文件的长度信息 3.接收文件数据写入文件中 """ import os import tool s = socket.socket() s.bind(("127.0.0.1",1688)) s.listen() c,addr = s.accept() # 先接受文件信息的字典 length = struct.unpack("q",c.recv(8))[0] # 文件信息 info = c.recv(length).decode("utf-8") # 反序列化 info_dic = json.loads(info) print(info_dic) print("文件总长:%s" % info_dic["file_size"]) #已经接收的长度 curren_size = 0 file = open(info_dic["file_name"],"wb") # 接收真实数据 # 建议循环接收 以保证缓冲区不会超出内存 while curren_size < info_dic["file_size"]: data = c.recv(1024) if not data: print("对方下线了") break # 写入数据 file.write(data) curren_size += len(data) # 记录已经接收的长度 # print("当前已经接收:%s" % curren_size) file.close() print("接收完毕!") if info_dic["MD5"] == tool.get_MD5(info_dic["file_name"]): print("MD5校验成功!") else: print("MD5校验失败! 你下载的是盗版的 请到官网下载 www.oldboyedu.com!")