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()
posted @ 2019-09-09 22:37  zx125  阅读(108)  评论(0编辑  收藏  举报