tcp粘包
Socket粘包问题
参考博客
https://www.cnblogs.com/nickchen121/p/11031027.html
首先要注意,只有TCP有粘包问题,UDP没有
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
TCP是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略
一般有两种形式的粘包问题
1.发送端需要等缓冲满才发送出去,造成粘包
2.接收端不及时接收缓冲区的包,造成粘包
解决粘包问题
在发送数据的头上,加入数据的相关信息,文件名,文件长度等,接收端接受到数据后先解析数据头,然后根据数据长度读取数据,在已文件名保存指定格式的文件。
文件上传下载,粘包版
服务端
from socket_server import *
import struct
import json
import os
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',9000))
server.listen(5)
print("等待客户端连接")
conn,addr=server.accept()
print(f"{addr}连接成功")
def dow_file(file_name):
file_path=os.path.join("haha",file_name)
if os.path.exists(file_path):
print(f"发送文件{file_name}")
with open(f"haha/{file_name}","rb")as fr:
file_data=fr.read()
#文件字典
file_dic={}
file_dic['file_name']=file_name
file_dic['command'] = "down"
file_dic["file_len"]=len(file_data)
#字典长度 第一个头
head1=struct.pack('i',len(bytes(json.dumps(file_dic),encoding="utf8")))
conn.send(head1)
#文件名和大小 第二个头
head2=bytes(json.dumps(file_dic),encoding="utf8")
conn.send(head2)
#文件内容
conn.send(file_data)
print("文件发送成功")
else:
print(f"{file_name}文件不存在")
head1 = struct.pack('i',0)
conn.send(head1)
def up_file(file_dic):
# 获取文件名和文件长度
file_name = file_dic['file_name']
file_len = file_dic['file_len']
# 文件写入
print(f"{file_name}正在写入")
with open(f"haha/{file_name}", "wb")as fw:
while file_len > 0:
if file_len > 1024:
data = conn.recv(1024)
file_len -= 1024
else:
data = conn.recv(file_len)
file_len = 0
fw.write(data)
print(f"{file_name}写入成功")
if __name__ == '__main__':
while True:
# 获取文件字典的长度
header = conn.recv(4)
head2_len = struct.unpack('i', header)[0]
# 获取文件字典
file_dic = conn.recv(head2_len)
file_dic = json.loads(file_dic.decode('utf8'))
if file_dic["command"]=="down":
dow_file(file_dic["file_name"])
if file_dic["command"]=="up":
up_file(file_dic)
# conn.close()
# server.close()
客户端
import struct
from socket import socket
import json
import os
client=socket()
client.connect(('127.0.0.1',9000))
def down_file():
file_name = input("请输入想要下载的文件:")
data = bytes(file_name, encoding="utf8")
file_dic = {}
file_dic["command"] = "down"
file_dic['file_name'] = file_name
# 字典长度 第一个头
head1 = struct.pack('i', len(bytes(json.dumps(file_dic), encoding="utf8")))
client.send(head1)
# 文件名和大小 第二个头
head2 = bytes(json.dumps(file_dic), encoding="utf8")
client.send(head2)
# 字典长度 第一个头
head1 = struct.pack('i', len(bytes(json.dumps(file_dic), encoding="utf8")))
client.send(head1)
client.send(head2)
# 获取文件字典的长度
header = client.recv(4)
head2_len = struct.unpack('i', header)[0]
if head2_len == 0:
print("文件不存在")
return
# 获取文件字典
file_dic = client.recv(head2_len)
file_dic = json.loads(file_dic.decode('utf8'))
# 获取文件名和文件长度
file_name = file_dic['file_name']
file_len = file_dic['file_len']
# 文件写入
print(f"{file_name}正在写入")
with open(f"zx/{file_name}.py", "wb")as fw:
while file_len > 0:
if file_len > 1024:
data = client.recv(1024)
file_len -= 1024
else:
data = client.recv(file_len)
file_len = 0
fw.write(data)
print(f"{file_name}写入成功")
def up_file():
file_name = input("请输入你想要上传的文件名:")
file_path = os.path.join("zx", file_name)
if os.path.exists(file_path):
print(f"发送文件{file_name}")
with open(f"zx/{file_name}", "rb")as fr:
file_data = fr.read()
file_dic = {}
file_dic['file_name'] = file_name
file_dic["command"] = "up"
file_dic["file_len"] = len(file_data)
# 字典长度 第一个头
head1 = struct.pack('i', len(bytes(json.dumps(file_dic), encoding="utf8")))
client.send(head1)
# 文件名和大小 第二个头
head2 = bytes(json.dumps(file_dic), encoding="utf8")
client.send(head2)
# 文件内容
client.send(file_data)
print("文件发送成功")
else:
print(f"{file_name}文件不存在")
return
fun_dic={
"0":down_file,
"1":up_file
}
if __name__ == '__main__':
while True:
print(
'''
0:下载文件
1:上传文件
'''
)
choice=input("请选择你要的功能:").strip()
if not fun_dic.get(choice):
print("没有此功能")
continue
fun_dic[choice]()
# client.close()