python3 粘包

1.黏包的原因?

那是因为 一条消息有多少字节对应用程序是不可见的,不知道的。每次接收端不知道要接受多少数据,可能下一次接受的数据还有上次遗留的数据,

因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。

而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

udp永远不会出现粘包,因为其只是接收一次,不收的就丢弃,收的少了也不会重复收。

2.

下面内容转自https://www.cnblogs.com/linhaifeng/articles/6129246.html#_label2

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

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

(2)UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。

(3)tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头。

(4)

两种情况下会发生粘包。

(1)发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

 1 __author__ = 'Administrator'
 2 from socket import *
 3 ip_port=('127.0.0.1',8080)
 4 back_log=5
 5 tcp_server=socket(AF_INET,SOCK_STREAM)
 6 tcp_server.bind(ip_port)
 7 tcp_server.listen(back_log)
 8 conn,addr=tcp_server.accept()
 9 da1=conn.recv(5)
10 print(da1)
11 da2=conn.recv(5)
12 print(da2)
13 da3=conn.recv(5)
14 print(da3)
15 da4=conn.recv(5)
16 print(da4)
View Code
 1 __author__ = 'Administrator'
 2 from socket import *
 3 ip_port=('127.0.0.1',8080)
 4 back_log=5
 5 buffer_size=1024
 6 tcp_client=socket(AF_INET,SOCK_STREAM)
 7 tcp_client.connect(ip_port)
 8 tcp_client.send('h'.encode('utf-8'))
 9 tcp_client.send('ell'.encode('utf-8'))
10 tcp_client.send('hello'.encode('utf-8'))
11 tcp_client.send('qwe'.encode('utf-8'))
View Code

黏包:

b'hell'
b'hello'
b'qwe'
b''

 

(2)接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

数据量少的时候:

 1 __author__ = 'Administrator'
 2 import time
 3 from socket import *
 4 ip_port=('127.0.0.1',8080)
 5 back_log=5
 6 buffer_size=1024
 7 tcp_client=socket(AF_INET,SOCK_STREAM)
 8 tcp_client.connect(ip_port)
 9 tcp_client.send('h'.encode('utf-8'))
10 tcp_client.send('ell'.encode('utf-8'))
11 tcp_client.send('hello'.encode('utf-8'))
12 tcp_client.send('qwe'.encode('utf-8'))
13 
14 time.sleep(100)
View Code
 1 __author__ = 'Administrator'
 2 from socket import *
 3 ip_port=('127.0.0.1',8080)
 4 back_log=5
 5 buffer_size=1024
 6 tcp_server=socket(AF_INET,SOCK_STREAM)
 7 tcp_server.bind(ip_port)
 8 tcp_server.listen(back_log)
 9 conn,addr=tcp_server.accept()
10 da1=conn.recv(buffer_size)
11 print(da1)
12 da2=conn.recv(buffer_size)
13 print(da2)
14 da3=conn.recv(buffer_size)
15 print(da3)
16 da4=conn.recv(buffer_size)
17 print(da4)
View Code

#########结果:

b'hell'
b'helloqwe'

数据量多的时候:

 1 __author__ = 'Administrator'
 2 import time
 3 from socket import *
 4 ip_port=('127.0.0.1',8080)
 5 back_log=5
 6 buffer_size=1024
 7 tcp_client=socket(AF_INET,SOCK_STREAM)
 8 tcp_client.connect(ip_port)
 9 tcp_client.send('hqwsdgthbsdfhgsefgdsfgg'.encode('utf-8'))
10 tcp_client.send('aaaaccccbbbbb'.encode('utf-8'))
View Code
 1 __author__ = 'Administrator'
 2 from socket import *
 3 ip_port=('127.0.0.1',8080)
 4 back_log=5
 5 buffer_size=1024
 6 tcp_server=socket(AF_INET,SOCK_STREAM)
 7 tcp_server.bind(ip_port)
 8 tcp_server.listen(back_log)
 9 conn,addr=tcp_server.accept()
10 da1=conn.recv(2)
11 print(da1)
12 da2=conn.recv(3)
13 print(da2)
14 da3=conn.recv(10)
15 print(da3)
16 da4=conn.recv(buffer_size)
17 print(da4)
View Code

 

 #######################

结果:

b'hq'
b'wsd'
b'gthbsdfhgs'
b'efgdsfggaaaaccccbbbbb'

 【注意】tcp的发和收的数量不是一一对应的

3.udp不会粘包

1 from socket import *
2 ip_port=('127.0.0.1',8080)
3 back_log=5
4 buffer_size=1024
5 udp_server=socket(AF_INET,SOCK_DGRAM)
6 udp_server.bind(ip_port)
7 data1=udp_server.recvfrom(buffer_size)
8 print(data1)
View Code
 1 __author__ = 'Administrator'
 2 from socket import *
 3 ip_port=('127.0.0.1',8080)
 4 back_log=5
 5 buffer_size=1024
 6 udp_client=socket(AF_INET,SOCK_DGRAM)
 7 
 8 udp_client.sendto('asdhen'.encode('utf-8'),ip_port)
 9 udp_client.sendto('asdhensdfv'.encode('utf-8'),ip_port)
10 udp_client.sendto('asdsaffghen'.encode('utf-8'),ip_port)
View Code

 

一次只是接收一个,发和收一一对应

 1 __author__ = 'Administrator'
 2 from socket import *
 3 ip_port=('127.0.0.1',8080)
 4 back_log=5
 5 buffer_size=1024
 6 udp_client=socket(AF_INET,SOCK_DGRAM)
 7 
 8 udp_client.sendto('asdhen'.encode('utf-8'),ip_port)
 9 udp_client.sendto('asdhensdfv'.encode('utf-8'),ip_port)
10 udp_client.sendto('asdsaffghen'.encode('utf-8'),ip_port)
11 
12 
13 
14 from socket import *
15 ip_port=('127.0.0.1',8080)
16 back_log=5
17 buffer_size=1024
18 udp_server=socket(AF_INET,SOCK_DGRAM)
19 udp_server.bind(ip_port)
20 data1=udp_server.recvfrom(buffer_size)################################[注意]
21 print(data1)
22 data2=udp_server.recvfrom(buffer_size)
23 print(data2)
24 data3=udp_server.recvfrom(buffer_size)
25 print(data3)
View Code

 

####################结果

(b'asdhen', ('127.0.0.1', 51385))
(b'asdhensdfv', ('127.0.0.1', 51385))
(b'asdsaffghen', ('127.0.0.1', 51385))

当接收数据量小于发的时候:

 1 from socket import *
 2 ip_port=('127.0.0.1',8080)
 3 back_log=5
 4 buffer_size=1024
 5 udp_server=socket(AF_INET,SOCK_DGRAM)
 6 udp_server.bind(ip_port)
 7 data1=udp_server.recvfrom(5)################################[注意]
 8 print(data1)
 9 
10 
11 
12 
13 
14 
15 __author__ = 'Administrator'
16 from socket import *
17 ip_port=('127.0.0.1',8080)
18 back_log=5
19 buffer_size=1024
20 udp_client=socket(AF_INET,SOCK_DGRAM)
21 
22 udp_client.sendto('asdhen'.encode('utf-8'),ip_port)
23 udp_client.sendto('asdhensdfv'.encode('utf-8'),ip_port)
24 udp_client.sendto('asdsaffghen'.encode('utf-8'),ip_port)
View Code

OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小。

【注意】:tcp不会丢数据

                 udp会丢数据

 

posted @ 2018-10-06 23:28  小猪猪猪  阅读(208)  评论(0)    收藏  举报