淺談tcp粘包問題

第一部分:簡介tcp socket通訊的底層原理

原理解析圖:算法

 

1 socket通訊過程如圖所示:首先客戶端將發送內容經過send()方法將內容發送到客戶端計算機的內核區,而後由操做系統將內容經過底層路徑發送到服務器端的內核區,而後由服務器程序經過recv()方法從服務器端計算機內核區取出數據。
2 所以咱們能夠了解到,send方法並非直接將內容發送到服務器端,recv方法也並非直接將從客戶端發來的內容接收到服務器程序內存中,而是操做本身機器的內核區。

 

第二部分:產生粘包的緣由(只針對tcp)

產生粘包的狀況有兩種:

1 1:當連續發送數據時,因爲tcp協議的nagle算法,會將較小的內容拼接成大的內容,一次性發送到服務器端,所以形成粘包
2 
3 2:當發送內容較大時,因爲服務器端的recv(buffer_size)方法中的buffer_size較小,不能一次性徹底接收所有內容,所以在下一次請求到達時,接收的內容依然是上一次沒有徹底接收完的內容,所以形成粘包現象。

也就是說:接收方不知道該接收多大的數據纔算接收完畢,形成粘包。shell

 

第三部分:如何解決上述兩種粘包現象?

思路一:對於第一種粘包產生方式能夠在兩次send()直接使用recv()來阻止連續發送的狀況發生。代碼就不用展現了。服務器

思路二:因爲產生粘包的緣由是接收方的無邊界接收,所以發送端能夠在發送數據以前向接收端告知發送內容的大小便可。代碼示例以下:socket

  方式一:分兩次通信分別傳遞內容大小和內容tcp

  服務器端代碼:ui

 1 # __author__:Kelvin
 2 # date:2019/4/28 21:36
 3 from socket import *
 4 import subprocess
 5 
 6 server = socket(AF_INET, SOCK_STREAM)
 7 server.bind(("127.0.0.1", 8000))
 8 server.listen(5)
 9 
10 while True:
11     conn, addr = server.accept()
12     print("建立了一個新的鏈接!")
13     while True:
14         try:
15             data = conn.recv(1024)
16             if not data: break
17             res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
18                                    stderr=subprocess.PIPE)
19             err = res.stderr.read()
20             if err:
21                 cmd_msg = err
22             else:
23                 cmd_msg = res.stdout.read()
24             if not cmd_msg: cmd_msg = "action success!".encode("gbk")
25             length = len(cmd_msg)
26             conn.send(str(length).encode("utf-8"))
27             conn.recv(1024)
28             conn.send(cmd_msg)
29         except Exception as e:
30             print(e)
31             break

  客戶端代碼:spa

 1 # __author__:Kelvin
 2 # date:2019/4/28 21:36
 3 from socket import *
 4 
 5 client = socket(AF_INET, SOCK_STREAM)
 6 client.connect(("127.0.0.1", 8000))
 7 while True:
 8     inp = input(">>:")
 9     if not inp: continue
10     if inp == "quit": break
11     client.send(inp.encode("utf-8"))
12     length = int(client.recv(1024).decode("utf-8"))
13     client.send("ready!".encode("utf-8"))
14     lengthed = 0
15     cmd_msg = b""
16     while lengthed < length:
17         cmd_msg += client.recv(1024)
18         lengthed = len(cmd_msg)
19     print(cmd_msg.decode("gbk"))

  方式二:一次通信直接傳遞內容大小和內容操作系統

  服務器端:code

 1 # __author__:Kelvin
 2 # date:2019/4/28 21:36
 3 from socket import *
 4 import subprocess
 5 import struct
 6 
 7 server = socket(AF_INET, SOCK_STREAM)
 8 server.bind(("127.0.0.1", 8000))
 9 server.listen(5)
10 
11 while True:
12     conn, addr = server.accept()
13     print("建立了一個新的鏈接!")
14     while True:
15         try:
16             data = conn.recv(1024)
17             if not data: break
18             res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
19                                    stderr=subprocess.PIPE)
20             err = res.stderr.read()
21             if err:
22                 cmd_msg = err
23             else:
24                 cmd_msg = res.stdout.read()
25             if not cmd_msg: cmd_msg = "action success!".encode("gbk")
26             length = len(cmd_msg)
27             conn.send(struct.pack("i", length))
28             conn.send(cmd_msg)
29         except Exception as e:
30             print(e)
31             break

  客戶端:server

 1 # __author__:Kelvin
 2 # date:2019/4/28 21:36
 3 from socket import *
 4 import struct
 5 
 6 client = socket(AF_INET, SOCK_STREAM)
 7 client.connect(("127.0.0.1", 8000))
 8 while True:
 9     inp = input(">>:")
10     if not inp: continue
11     if inp == "quit": break
12     client.send(inp.encode("utf-8"))
13     length = struct.unpack("i",client.recv(4))[0]
14     lengthed = 0
15     cmd_msg = b""
16     while lengthed < length:
17         cmd_msg += client.recv(1024)
18         lengthed = len(cmd_msg)
19     print(cmd_msg.decode("gbk"))

上述兩種方式都可以解決粘包問題。

相關文章
相關標籤/搜索