TCP粘包以及解决方案

TCP协议粘包现象的说明:

  1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。

粘包会发生什么?

这时候,接受不知道数据的限界,就没有办法正确的解析对方传输过来的限界。就才去了类似通信协议的解决方案,处理粘包问题。

简单解决问题的方法。根据当前需要发送的数据的大小传输数据的二进制长度先发送给客户端,在根据传输数据的长度来获取的真实的数据。

实现如下服务端

import socket,subprocess,struct
server = socket.socket()
server.bind(("127.0.0.1",54321))
server.listen(5)

while True:
    client, addr = server.accept()
    while True:
        try:

            cmd = client.recv(1024).decode("utf-8")#接受命令
            if not cmd:
                print("client closed")
                client.close()
                break
            p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            res = p.stdout.read()+p.stderr.read()#获得返回的数据
            res_len =len(res)#整形形式的需要转换为固定长度的二进制形式。使用struct模块
            bytes_len = struct.pack("i",res_len)
            client.send(bytes_len)
            client.send(res)
        except ConnectionResetError:
            print("客户端错误退出")
            client.close()
            break
server.close()

客户端

import socket,struct

client =socket.socket()
client.connect(("127.0.0.1",54321))

while True:
    cmd = input(">>>:")
    if not cmd:continue
    client.send(cmd.encode("utf-8"))
    #获取数据的长度的二进制
    bytes_len = client.recv(1024)
    #解压成为整形
    res_len = struct.unpack("i",bytes_len)[0]
    #接受数据的方式
    final_data=b""
    #当前接受数据的长度计数器
    data_len = 0
    while data_len<res_len:
        res = client.recv(1024)
        final_data += res
        data_len += len(res)
    print(final_data.decode("gbk"))

client.close()

其实我们还以定制更加复杂的数据头

可以字典形式表现,实现传输更加复杂文件,甚至可以进行校验

服务端

import socket,struct,json

server = socket.socket()
server.bind(("127.0.0.1",3333))
server.listen(5)
while True:
    client,addr = server.accept()
    while True:
        try:
            ml = client.recv(1024).decode("utf-8")#模拟客户端请求报告
            if not ml:
                client.close()
                break
            if ml != "1":continue
            #开始资质表头
            filename = "总结"
            #计算文件bytes长度
            file_len = 0
            f = open(r"F:\Python_exe\day34\上午总结","rb")
            for line in f:
                line_len = len(line)
                file_len += line_len
            f.close()
            file_dict = {"filename":filename,"length":file_len}
            #将字典装换为json格式
            json_dict = json.dumps(file_dict)
            #再json字符串转为bytes
            byte_dict = json_dict.encode("utf-8")
            #计算字典二进制的长度
            head_len = len(byte_dict)
            #将长度打包成一个数据头
            head = struct.pack("i", head_len)
            client.send(head)
            client.send(byte_dict)
            #标记内容完成
            #传输正式内容
            with open(r"F:\Python_exe\day34\上午总结","rb") as f:
                for line in f:
                    client.send(line)

        except ConnectionResetError:
            print("连接错误连接")
            client.close()
            break

客户端

import socket,struct模块,json

client = socket.socket()
client.connect(("127.0.0.1",3333))

while True:
    ml = input(">>>:")
    if not ml:continue
    client.send(ml.encode("utf-8"))
    #获取字典的二进制
    bytes_len = client.recv(4)
    #还原
    head_len =struct模块.unpack(‘i‘, bytes_len)[0]
    #获取数据报头
    bytes_dict = client.recv(head_len)
    #将bytes类型转化为json格式
    json_dict = bytes_dict.decode("utf-8")
    #将字典json格式还原为成python的字典

    file_dict=json.loads(json_dict)
    #获取文件的名称
    filename = file_dict["filename"]
    #获取文件的传输长度
    file_len = file_dict["length"]
    #打开文件追加写
    f = open(filename,"ab")
    #当前接受长度
    data_len = 0

    while data_len<file_len:
        data = client.recv(1024)
        data_len += len(data)
        f.write(data)
    f.close()
    client.close()

client.close()
解决粘包的方案  自定义报头    1.先用报头传输数据的长度        对于我们远程CMD程序来说 只要先传输长度就能解决粘包的问题        但是如果做得是一个文件上传下载  除了数据的长度 还需要传输文件的名字 md5等等信息        又该如何?

2.自定义复杂报头  完成发送一些额外的信息 例如文件名        1.将要发送的额外数据打包成一个字典        2.将字典转为bytes类型        3.计算字典的bytes长度 并先发送        4.发送字典数据        5.发送真实数据

原文地址:https://www.cnblogs.com/msj513/p/9911596.html

时间: 2024-10-02 07:31:22

TCP粘包以及解决方案的相关文章

netty解决tcp粘包拆包问题

tcp粘包拆包解决方案 1.发送定长的消息 server端:                    EventLoopGroup pGroup = new NioEventLoopGroup(); EventLoopGroup cGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(pGroup, cGroup)  .channel(NioServerSocketChannel.cl

Netty中使用MessagePack时的TCP粘包问题与解决方案

[toc] Netty中使用MessagePack时的TCP粘包问题与解决方案 通过下面的实例代码来演示在Netty中使用MessagPack时会出现的TCP粘包问题,为了学习的连贯性,参考了<Netty权威指南>第7章中的代码,但是需要注意的是,书中并没有提供完整代码,提供的代码都是片段性的,所以我根据自己的理解把服务端的代码和客户端的代码写了出来,可以作为参考. 仍然需要注意的是,我使用的是Netty 4.x的版本. 另外我在程序代码中写了非常详细的注释,所以这里不再进行更多的说明. 在使

netty拆包/粘包的解决方案

netty拆包/粘包的解决方案 刚开始学拆包/粘包的时候确实不太好理解,我反复看了几遍就理解了.写下了加深记忆,也希望对大家有所帮助. 本章只介绍简单的二个工具LineBaseFrameDecoder和StringDecoder. 基础知识 1.首先图解拆包/粘包问题出现的原因 假设现在客户端向服务器端发送数据,在某一时间段上客户端分别发送了D1和D2二个数据包给服务器,由于服务器一次读取到的字节数是不确定的,故存在以下5种情况. 情况1:服务器分二次读取到了二个独立的数据包,分别是D1和D2,

TCP粘包问题分析和解决(全)

TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小.数据量小的数据,合并成一个大的数据块,然后进行封包.这样,接收端,就难于分辨出来了,必须提供科学的拆包机制. 对于UDP,不会使用块的合并优化算法,这样,实际上目前认为,是由于UDP支持的是一对多的模式,

TCP粘包, UDP丢包, nagle算法

一.TCP粘包 1. 什么时候考虑粘包 如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题(因为只有一种包结构,类似于http协议,UDP不会出现粘包现象).关闭连接主要要双方都发送close连接(参考tcp关闭协议).如:A需要发送一段字符串给B,那么A与B建立连接,然后发送双方都默认好的协议字符如"hello give me sth abour yourself",然后B收到报文后,就将缓冲区数据接收,然后关闭连接,这样粘包问

TCP粘包/拆包问题

无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. TCP粘包/拆包问题说明 假设客户

Netty(三)TCP粘包拆包处理

tcp是一个“流”的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 粘包.拆包问题说明 假设客户端分别发送数据包D1和D2给服务端,由于服务端一次性读取到的字节数是不确定的,所以可能存在以下4种情况. 1.服务端分2次读取到了两个独立的包,分别是D1,D2,没有粘包和拆包: 2.服务端一次性接收了两个包,D1和D2粘在一起了,被成为TCP粘包; 3.服务端分2次读取到了两个数据包,第一次读取到了完整的D1和D2包的部

TCP 粘包/拆包问题

简介 TCP 是一个’流’协议,所谓流,就是没有界限的一串数据. 大家可以想想河里的流水,是连成一片的.期间并没有分界线, TCP 底层并不了解上层业务数据的具体含义 ,它会根据 TCP 缓冲区的实际情况进行包得划分,所以在业务上认为,一个完整的包可能会被 TCP 拆分成多个包进行发送 . 也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的 TCP 拆包和粘包. TCP 粘包/拆包问题说明 我们可以通过图解对 TCP 粘包和拆包进行说明.粘包问题示例图: 假设客户端分别发送了两个数据包

Mina框架断包、粘包问题解决方案

Mina框架断包.粘包问题解决方案 Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务.虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能.高扩展性的网络通信应用,Mina 提供了事件驱动.异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型. 在mina中,一般的应用场景用TextLine的Decode和Encode就够用了(Te