Python--网络编程-----粘包的底层原理分析

一、send是不是直接把数据发给服务端

不是,要想发数据,必须得通过网卡发送数据,应用软件是无法直接通过网卡发送数据的,它需要调用操作系统接口,

也就是说,应用软件把要发送的数据由应用系统内存copy到操作系统内存,进而由操作系统控制数据的发送,copy到

操作系统内存也意味着send已经发送完毕了,它是无法控制操作系统怎样发送数据的。

二、recv是不是直接从客户端接收数据

不是,与send数据相同,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,应用软件把要接收的数

据由操作系统内存copy到应用系统内存。

三、不管是recv还是send都不是直接接收对方的数据,而是操作自己操作系统内存--------》不是一个send对应一个recv

1、recv:

    wait data:耗时非常长

    copy data

2、send:

    copy data

3、socket 为提高传输效率,发送方往往要收集到足够多的数据后才发送一次数据给对方。若连续几次需要send的数据都很少,

通常TCP socket 会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

4、发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,

合并成一个大的数据块,然后进行封包。

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

四、粘包不一定会发生,在数据量小,时间间隔短的情况下会发生,代码示例如下:

1、客户端粘包

 1 客户端代码:
 2 import socket
 3 
 4 
 5 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 
 7 client.connect(('127.0.0.1', 9903))
 8 
 9 client.send('hello'.encode('utf-8'))
10 client.send('world'.encode('utf-8'))
11 
12 服务端代码:
13 import socket
14 
15 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
16 server.bind(('127.0.0.1', 9903))  # 0-65535:0-1024给操作系统使用
17 server.listen(5)
18 
19 conn, addr = server.accept()
20 
21 res1 = conn.recv(1024)
22 print('第一次结果:', res1)
23 
24 res2 = conn.recv(1024)
25 print('第二次结果:', res2)
26 
27 启动之后服务端结果为:
28 第一次结果: b'helloworld'
29 第二次结果: b''

两次send在客户端已经粘包,一次性发给服务端,所以服务端第一次就从服务端的操作系统内存中取出了两次send内容

如果在客户端两次send中间加一个time.sleep(1),服务端结果为:

1 第一次结果: b'hello'
2 第二次结果: b'world'

可以看出,客户端不会等待1秒钟,而直接把hello发过去了,1秒钟后,再把world发送到服务端,这样客户端和服务端都没有发生粘包

2、服务端粘包

 1 服务端代码为:
 2 
 3 import socket
 4 
 5 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6 server.bind(('127.0.0.1', 9903))  # 0-65535:0-1024给操作系统使用
 7 server.listen(5)
 8 
 9 conn, addr = server.accept()
10 
11 res1 = conn.recv(1)
12 print('第一次结果:', res1)
13 
14 res2 = conn.recv(1024)
15 print('第二次结果:', res2)
16 
17 客户端代码为:
18 
19 import socket
20 
21 
22 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
23 
24 client.connect(('127.0.0.1', 9903))
25 
26 client.send('hello'.encode('utf-8'))
27 client.send('world'.encode('utf-8'))
28 
29 服务端运行结果为:
30 
31 第一次结果: b'h'
32 第二次结果: b'elloworld'

第一次recv只接收了一个byte的数据h,ello四个字节的数据遗留在管道中,会和后面的数据在服务端发生粘包现象,

如果知道发送端的数据量大小,第一次recv5个byte的数据,第二次接收5个byte的数据,也不会发生粘包现象,

或者服务端代码改为如下:

 1 import socket
 2 
 3 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 4 server.bind(('127.0.0.1', 9903))  # 0-65535:0-1024给操作系统使用
 5 server.listen(5)
 6 
 7 conn, addr = server.accept()
 8 
 9 res1 = conn.recv(1)
10 res2 = conn.recv(1)
11 res3 = conn.recv(1)
12 res4 = conn.recv(1)
13 res5 = conn.recv(1)
14 print('第一次结果:', res1+res2+res3+res4+res5)
15 
16 res2 = conn.recv(1024)
17 print('第二次结果:', res2)

服务端也不会发生粘包现象,因为保证了第一次结果把第一次send的数据接收完整,

所以说,要想避免粘包现象,必须知道发送端send的数据量的大小,然后服务端根据send的数据量的大小recv数据,

原文地址:https://www.cnblogs.com/xudachen/p/8735554.html