首页 > 其他分享 >粘包

粘包

时间:2024-05-15 14:08:05浏览次数:19  
标签:字节 self 粘包 数据 服务端 客户端

粘包问题
【一】什么是粘包
须知:只有TCP有粘包现象,UDP永远不会粘包
【1】socket收发消息的原理
首先需要掌握一个socket收发消息的原理
发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据
也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的
因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。
而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。
【2】如何定义消息
可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。
例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
此外
发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率
发送方往往要收集到足够多的数据后才发送一个TCP段。
若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
【3】TCP
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。
收发两端(客户端和服务器端)都要有一一成对的socket
因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。
这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
【4】UDP
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。
不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息)
这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
【5】小结
tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略
udp的recvfrom是阻塞的
一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠
tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
两种情况下会发生粘包。
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
【二】什么是粘包问题
客户端发送需要执行的代码
服务端接收到客户端传过来的代码
服务端调用方法执行代码并拿到执行后的结果
服务端将执行后的结果进行返回
客户端接收到服务端返回的结果并做打印输出
【1】服务端
【2】客户端
【3】问题引入
服务端:
执行代码,代码为空会报错
执行代码,返回的数据可能存在空/报错信息
客户端:
输入的指令长度,可能会超出范围
接受到的服务端反馈的结果可能会特别多
如何打印超出数据范围(缓存到系统里)的数据
【4】粘包问题
在 TCP 协议中是流式协议,数据是源源不断的传入到客户端中,但是客户端可以接受到的信息的长度是有限的
当接收到指定长度的信息后,客户端进行打印输出
剩余的其他数据会被缓存到 内存中
当再次执行其他命令时
新的数据的反馈结果,会叠加到上一次没有完全打印完全的信息的后面,造成数据的错乱
当客户端想打印新的命令的数据时,打印的其实是上一次没有打印完的数据
对数据造成的错乱
【5】粘包问题解决思路
拿到数据的总大小 recv_total_size
recv_size = 0 ,循环接收,每接收一次,recv_size += 接收的长度
直到 recv_size = recv_total_size 表示接受信息完毕,结束循环
【三】UDP协议不存在粘包问题
粘包问题出现的原因
TCP 协议是流式协议,数据像水流一样粘在一起,没有任何边界之分
收数据没有接收干净,有残留,就会和下一次的结果混淆在一起
解决粘包问题的核心法门就是
每次都收干净
不造成数据的混淆
【1】UDP协议不存在粘包问题
(1)服务端
(2)客户端
(3)小结
当我们启动udp服务端后,由udp客户端向服务端发送两条数据
但是在udp服务端只接收到了一条数据
这是因为 udp 是报式协议,传送数据过程中会将数据打包直接发走,不会对数据进行拼接操作(没有Nagle算法)
【2】TCP协议存在粘包问题
(1)服务端
(2)客户端
【3】小结
从以上我们可以看到
TCP协议传输过程中将我们的两次发送的数据拼接成了一个发送到服务端
通过比较我们可知,udp协议虽然不存在粘包问题,但是,udp协议的安全性有待考量
【四】TCP协议解决粘包问题基础
【1】解决思路
利用 struct 模块将传输过去的数据的总长度 打包 + 到头部进行发送
【2】服务端
【3】客户端
客户端可以完美的接收到查出额定长度以外的数据
同时这也是 自定义协议的 简单操作
【五】TCP协议解决粘包问题进阶
【0】解决思路
通过json模式 ---- 模版修改参数直接套用
【1】服务端
【2】客户端
【补充】struct模块
struct.pack()是Python内置模块struct中的一个函数
它的作用是将指定的数据按照指定的格式进行打包,并将打包后的结果转换成一个字节序列(byte string)
可以用于在网络上传输或者储存于文件中。
struct.pack(fmt, v1, v2, ...)
其中,fmt为格式字符串,指定了需要打包的数据的格式,后面的v1,v2,...则是需要打包的数据。
这些数据会按照fmt的格式被编码成二进制的字节串,并返回这个字节串。
fmt的常用格式符如下:
x --- 填充字节
c --- char类型,占1字节
b --- signed char类型,占1字节
B --- unsigned char类型,占1字节
h --- short类型,占2字节
H --- unsigned short类型,占2字节
i --- int类型,占4字节
I --- unsigned int类型,占4字节
l --- long类型,占4字节(32位机器上)或者8字节(64位机器上)
L --- unsigned long类型,占4字节(32位机器上)或者8字节(64位机器上)
q --- long long类型,占8字节
Q --- unsigned long long类型,占8字节
f --- float类型,占4字节
d --- double类型,占8字节
s --- char[]类型,占指定字节个数,需要用数字指定长度
p --- char[]类型,跟s一样,但通常用来表示字符串
? --- bool类型,占1字节
具体的格式化规则可以在Python文档中查看(链接)。
【作业】FTP文件传输器
【1】服务端
【2】客户端
Python
复制代码
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import socket
import os

class MYTCPClient:
address_family = socket.AF_INET

socket_type = socket.SOCK_STREAM

allow_reuse_address = False

max_packet_size = 8192

coding='utf-8'

request_queue_size = 5

def __init__(self, server_address, connect=True):
    self.server_address=server_address
    self.socket = socket.socket(self.address_family,
                                self.socket_type)
    if connect:
        try:
            self.client_connect()
        except:
            self.client_close()
            raise

def client_connect(self):
    self.socket.connect(self.server_address)

def client_close(self):
    self.socket.close()

def run(self):
    while True:
        inp=input(">>: ").strip()
        if not inp:continue
        l=inp.split()
        cmd=l[0]
        if hasattr(self,cmd):
            func=getattr(self,cmd)
            func(l)


def put(self,args):
    cmd=args[0]
    filename=args[1]
    if not os.path.isfile(filename):
        print('file:%s is not exists' %filename)
        return
    else:
        filesize=os.path.getsize(filename)

    head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
    print(head_dic)
    head_json=json.dumps(head_dic)
    head_json_bytes=bytes(head_json,encoding=self.coding)

    head_struct=struct.pack('i',len(head_json_bytes))
    self.socket.send(head_struct)
    self.socket.send(head_json_bytes)
    send_size=0
    with open(filename,'rb') as f:
        for line in f:
            self.socket.send(line)
            send_size+=len(line)
            print(send_size)
        else:
            print('upload successful')

client=MYTCPClient(('127.0.0.1',8080))

client.run()

标签:字节,self,粘包,数据,服务端,客户端
From: https://www.cnblogs.com/zenopan101861/p/18193769

相关文章

  • socket 接收数据时粘包处理
    socket在接收数据时,经常会因为网络延迟、缓存区数据处理不及时等原因造成收到的数据是多个包黏在一起的情况。如下图所示 图中红色框部分是通讯的心跳包图中黄色框部分和未框选部分是两包 数据包所以可见此时缓存区里面同时存在了一个心跳包,两个数据包 如何分包(此处仅......
  • 粘包问题
    粘包问题须知:只有TCP有粘包现象,UDP永远不会粘包一、什么是粘包问题什么时候会发生粘包问题?当TCP传输和接收的数据并非我们规定的最大数据量时,就会发生粘包我们日常传输的数据几乎不可能等于我们规定的数据量,所以我们必须要解决这个问题为什么只有TCP会发生粘包问题?T......
  • 关于Socket网络编程粘包问题的思考
      首先,必须说明的是,粘包问题并不是源于通信协议TCP,TCP是基于连接的安全的协议,不会出现所谓的粘包问题,但这也只是对于流式传输是这样的。但我们需要不定长的分段数据包时,粘包问题就出现了。因此粘包问题是源自于我们的需求,而不是TCP。  解决粘包问题,明确来说,就是解决数据定界......
  • golang基于长度解决粘包问题(gnet)
    使用gnet框架处理Socket粘包问题当服务端处理旧业务tcpscoket,旧的业务是NettySocket使用的是2个字节的长度定义数据的大小。官方支持ICodec去处理,但文档不太友好,这里附上使用方法import( "github.com/panjf2000/gnet")typeDTUSocketServerstruct{ *gnet.EventServer......
  • 【网络编程】CS&BS架构_OSI七层、五层模型_三握四挥_Socket编程_粘包
    【一】CS&BS架构(1.1)CS架构产生的历史背景计算机网络的发展:20世纪60年代至70年代,计算机网络开始出现并得到广泛应用。最初的计算机网络主要是用于共享资源和实现远程访问,例如通过终端连接到中央计算机。这种模式下,中央计算机扮演着服务器的角色,而终端则扮演着客户端的角色。......
  • Socket接收粘包处理
    什么是粘包?比如:通过Socket发2条不一样长度的数据,"abc"和"defg"。因为Socket的数据不是你请求发就立即发送的,有时候为了减少网络交互次数,会把几小的个数据凑一凑一起发送。如果前面的被凑到一起发送了"abcdefg",就出现了粘包。 如何解决粘包问题?发送数据的时候,在数据里带上这......
  • 粘包
    socket编程socke编程又称套接字编程TCP套接字编程模板server服务端#导入模块importsocket#获取服务端对象server=socket.socket()#获取IP和端口号IP='127.0.0.1'PORT=9888#将IP和套接字绑定给对象server.bind((IP,PORT))#监听server.listen(5)while......
  • 网络编程之粘包问题
    粘包问题只有TCP有粘包现象,UDP永远不会粘包什么是粘包存在于客户端接收数据时,不能一次性收取全部缓冲区中的数据.当下一次再有数据来时,缓冲区中剩余的数据会和新的数据'粘连'在一起.这就是粘包现象。##什么是粘包?存在于TCP/IP协议中数据粘连在一起。##socket中造成粘......
  • 粘包问题
    粘包问题(1)粘包问题介绍粘包问题是在计算机网络中,特别是在使用面向流的传输协议(例如TCP)时经常遇到的一种情况。它主要涉及到数据在传输过程中的组织和接收问题。当使用TCP协议进行数据传输时,发送方往往会将要传输的数据切分成小的数据块,并通过网络发送给接收方。然而,底层的......
  • 粘包
    粘包【一】什么是粘包须知:只有TCP有粘包现象,UDP永远不会粘包【二】什么是粘包问题客户端发送需要执行的代码服务端接收到客户端传过来的代码服务端调用方法执行代码并拿到执行后的结果服务端将执行后的结果进行返回客户端接收到服务端返回的结果并做打印输出【1】......