首頁  >  文章  >  web前端  >  解決tcp黏包問題的兩種方法

解決tcp黏包問題的兩種方法

little bottle
little bottle轉載
2019-04-30 11:21:003907瀏覽

本文主要講述的是怎麼去解決tcp黏包問題,其一是分兩次通訊分別傳遞內容大小和內容,其二是一次通訊直接傳遞內容大小和內容。想了解的朋友可以詳細看看本篇文章,希望對你有幫助。

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

原理解析圖:

##1 socket通訊過程如圖所示:首先客戶端將發送內容透過send()方法將內容傳送到客戶端電腦的核心區,然後由作業系統將內容透過底層路徑傳送到伺服器端的核心區,然後由伺服器程式透過recv()方法從伺服器端電腦核心區取出資料。

2 因此我們可以了解到,send方法並不是直接將內容傳送到伺服器端,recv方法也不是直接將從客戶端發送的內容接收到伺服器程式記憶體中,而是操作自己機器的內核區。

第二部分:產生黏包的原因(只針對tcp)

產生黏包的情況有兩種:

1 1:連續發送資料時,由於tcp協定的nagle演算法,會將較小的內容拼接成大的內容,一次發送到伺服器端,因此造成黏包2 3 2:當發送內容較大時,由於伺服器端的recv(buffer_size)方法中的buffer_size較小,無法一次完全接收全部內容,因此在下一次請求到達時,接收的內容依然是上一次沒有完全接收完的內容,因此造成黏包現象。

也就是說:

接收方不知道該接收多大的資料才算接收完畢,造成黏包。

 相關教學:TCP/IP影片教學

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

想法一:對於第一種黏包產生方式可以在兩次send()直接使用recv()來阻止連續發送的情況發生。程式碼就不用展示了。

想法二:由於產生

黏包的原因是接收方的無邊界接收,因此發送端可以在發送資料之前向接收端告知發送內容的大小即可。程式碼範例如下:

  方式一:分兩次通訊分別傳遞內容大小與內容

   伺服器端程式碼:

# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *
import subprocess

server = socket(AF_INET, SOCK_STREAM)
server.bind(("127.0.0.1", 8000))
server.listen(5)

while True:
    conn, addr = server.accept()
    print("创建了一个新的连接!")
    while True:
        try:
            data = conn.recv(1024)
            if not data: break
            res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            err = res.stderr.read()
            if err:
                cmd_msg = err
            else:
                cmd_msg = res.stdout.read()
            if not cmd_msg: cmd_msg = "action success!".encode("gbk")
            length = len(cmd_msg)
            conn.send(str(length).encode("utf-8"))
            conn.recv(1024)
            conn.send(cmd_msg)
        except Exception as e:
            print(e)
            break

  

用戶端程式碼:##

# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(("127.0.0.1", 8000))
while True:
    inp = input(">>:")
    if not inp: continue
    if inp == "quit": break
    client.send(inp.encode("utf-8"))
    length = int(client.recv(1024).decode("utf-8"))
    client.send("ready!".encode("utf-8"))
    lengthed = 0
    cmd_msg = b""
    while lengthed < length:
        cmd_msg += client.recv(1024)
        lengthed = len(cmd_msg)
    print(cmd_msg.decode("gbk"))

#  方式二:一次通訊直接傳遞內容大小與內容

  伺服器端:

# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *
import subprocess
import struct

server = socket(AF_INET, SOCK_STREAM)
server.bind(("127.0.0.1", 8000))
server.listen(5)

while True:
    conn, addr = server.accept()
    print("创建了一个新的连接!")
    while True:
        try:
            data = conn.recv(1024)
            if not data: break
            res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            err = res.stderr.read()
            if err:
                cmd_msg = err
            else:
                cmd_msg = res.stdout.read()
            if not cmd_msg: cmd_msg = "action success!".encode("gbk")
            length = len(cmd_msg)
            conn.send(struct.pack("i", length))
            conn.send(cmd_msg)
        except Exception as e:
            print(e)
            break
  

客戶端:#

# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *
import struct

client = socket(AF_INET, SOCK_STREAM)
client.connect(("127.0.0.1", 8000))
while True:
    inp = input(">>:")
    if not inp: continue
    if inp == "quit": break
    client.send(inp.encode("utf-8"))
    length = struct.unpack("i",client.recv(4))[0]
    lengthed = 0
    cmd_msg = b""
    while lengthed < length:
        cmd_msg += client.recv(1024)
        lengthed = len(cmd_msg)
    print(cmd_msg.decode("gbk"))

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

以上是解決tcp黏包問題的兩種方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除