欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

粘包

程序员文章站 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 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=0
            while 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 os
import 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 time
import 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 = 0
            while 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 os
import json
import time
import struct
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")
    #
    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())#变成字典可以字节根据字典来的来文件的总大小