参考资料:https://www.jb51.net/article/139118.htm
粘包的产生:
1 直接原因
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
2 根本原因
发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据 优化算法 把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
解决粘包:
方法一:(先发)
客户端:
import socket
import json
sock=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 socket
import 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.jpg
action, 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 socket
import json
import timeimport hashlib
import struct
sock = 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_l