本文主要講述的是怎麼去解決tcp黏包問題,其一是分兩次通訊分別傳遞內容大小和內容,其二是一次通訊直接傳遞內容大小和內容。想了解的朋友可以詳細看看本篇文章,希望對你有幫助。
原理解析圖:
##1 socket通訊過程如圖所示:首先客戶端將發送內容透過send()方法將內容傳送到客戶端電腦的核心區,然後由作業系統將內容透過底層路徑傳送到伺服器端的核心區,然後由伺服器程式透過recv()方法從伺服器端電腦核心區取出資料。
2 因此我們可以了解到,send方法並不是直接將內容傳送到伺服器端,recv方法也不是直接將從客戶端發送的內容接收到伺服器程式記憶體中,而是操作自己機器的內核區。
接收方不知道該接收多大的資料才算接收完畢,造成黏包。
第三部分:如何解決上述兩種黏包現象? 想法一:對於第一種黏包產生方式可以在兩次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中文網其他相關文章!