粘包
程序员文章站
2022-04-10 20:16:36
参考资料:https://www.jb51.net/article/139118.htm 粘包的产生: 1 直接原因 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的 2 根本原因 发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要... ......
参考资料:
粘包的产生:
1 直接原因
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
2 根本原因
发送方引起的粘包是由tcp协议本身造成的,tcp为提高传输效率,发送方往往要收集到足够多的数据后才发送一个tcp段。若连续几次需要send的数据都很少,通常tcp会根据 优化算法 把这些数据合成一个tcp段后一次发送出去,这样接收方就收到了粘包数据。
解决粘包:
方法一:(先发)
客户端:
import socketimport jsonsock=socket.socket()sock.bind(('127.0.0.1',9000,))sock.listen(5)while 1:print("server is working....")conn,addr=sock.accept()while 1:data=conn.recv(1024).decode("utf8")#接收的是json的字符串fileinfo=json.loads(data) #把接受接收的json类型的字符串loads成字典print("fileinfo",fileinfo) #打印出fileinfo {'action': 'put', 'filename': '1.jpg', 'filesize': 16256}#文件信息action = fileinfo.get("action")filename = fileinfo.get("filename")filesize = fileinfo.get("filesize")conn.send(b'quick')#接收文件数据with open('put/'+filename,"wb") as f:recv_data_length=0while recv_data_length <filesize:data=conn.recv(1024)recv_data_length+=len(data) #len(data)是每次接收数据的长度f.write(data)print("文件总大小:%s,文件成功加收:%s" % (filesize, recv_data_length))print("接收成功....")服务端:
import socketimport osimport json'''客户端思路:1.创建socket的对象2.连接服务器3.实现可以循环的额发送命令3.1让用户输入命令3.2分解命令3.3利用os模块得到文件的大小3.4发送文件的信息(action,filename,filesize,)可以将信息放到字典中3.5向服务器发送文件的信息 利用json将字典变成字符串encode成字节发送到服务器3.6服务器接收文件信息发送一个确认信息,客户端接收数据3.7循环发送文件'''sock = socket.socket()sock.connect(('127.0.0.1', 9000))while 1:cmd = input("请输入命令>>>") # put 1.jpgaction, filename = cmd.strip().split(" ") # action 命令 filename 文件名filesize = os.path.getsize(filename)fileinfo = {"action": action,"filename": filename,"filesize": filesize,}fileinfo_json=json.dumps(fileinfo).encode("utf8")sock.send(fileinfo_json)#确认服务端接收到了文件信息haha=sock.recv(1024).decode("utf8")if haha =='quick':#发送文件with open(filename,"rb") as f:for line in f:sock.send(line)else:print("服务器异常")方法二:(利用struct模块防止粘包产生的报错)
服务端:
import socketimport jsonimport timeimport hashlibimport structsock = socket.socket()sock.bind(('127.0.0.1', 9000,))sock.listen(5)while 1:print("server is working....")conn, addr = sock.accept()while 1:##得到fileinfo_json打包的结果fileinfo_json_lengthpack=conn.recv(4)###########得到的是fileinfo_json的长度 但是unpack的结果是一个元组(57,)fileinfo_json_length=struct.unpack("i",fileinfo_json_lengthpack)[0]# print(fileinfo_json_length)#接收fileinfo_json的字符串fileinfo_json=conn.recv(fileinfo_json_length).decode("utf8")#用loads得到字典fileinfo=json.loads(fileinfo_json)# fileinfo_json = conn.recv(1024).decode("utf8") # 接收的是json的字符串# fileinfo = json.loads(fileinfo_json) # 用json的loads fileinfo_json 使其变成字典# print("fileinfo_json", fileinfo_json)# 文件信息action = fileinfo.get("action")filename = fileinfo.get("filename")filesize = fileinfo.get("filesize")# 循环接收文件with open('put/' + filename, "wb") as f:recv_data_length = 0while recv_data_length < filesize:data = conn.recv(1024)recv_data_length += len(data) # len(data)是每次接收数据的长度f.write(data)print("文件总大小:%s,文件成功加收:%s" % (filesize, recv_data_length))print("接收成功....")客户端:
import socketimport osimport jsonimport timeimport structsock = socket.socket()sock.connect(('127.0.0.1', 9000))while 1:cmd = input("请输入命令>>>") # put 1.jpgaction, filename = cmd.strip().split(" ") # action 命令 filename 文件名filesize = os.path.getsize(filename)fileinfo = {"action": action,"filename": filename,"filesize": filesize,}fileinfo_json=json.dumps(fileinfo).encode("utf8")#ret=struct.pack("i",len(fileinfo_json))#发送fileinfo_json的打包长度sock.send(ret)#发送的是json的数据字节串sock.send(fileinfo_json)#发送文件#发送文件信息的时候字节过小可能与下面的内容一起发送过去导致粘包with open(filename,"rb") as f: #这边的数据for line in f:sock.send(line)#好处先发送打包好的fileinfo_json打包长度,服务器接收的时候先接收其打包的长度#再根据打包的长度得到文件信息的json的长度#再利用json得到文件信息的字典形式,得到第三步发送的文件的总大小,服务器在循环的接收#把信息打包(pack)成一个数字4为字节串 ,代表文件信息的长度# res=struct.pack("i",len({"action": action,"filename": filename,"filesize": filesize,}))#先接收一个4为的字节串,得到文件信息ret=sock.recv(4) #ret表示的是接收的是pack后的文件信息#再利用unpack解开打包了的信息的长度,先把文件的信息接收过来,用json把其变成字典file_info=json.loads(sock.recv(ret).decode())#变成字典可以字节根据字典来的来文件的总大小
上一篇: 代码整洁之道(重构)
下一篇: AI怎么制作立方体的风景贴图?