socket粘包最終解決方案(配置報頭)

2021-10-10 19:39:15 字數 4775 閱讀 6151

套接字:

就是將傳輸層以下的協議封裝成子介面

對於應用程式來說只需呼叫套接字的介面,寫出的程式自然是遵循tcp或udp協議的

實現第乙個功能個:

實現:通過客戶端向服務端傳送命令,調取windows下面的cmd視窗,將服務端執行命令的結構,返回並顯示在

客戶端視窗上。

subprocess:

1.可以將執行結果返回

2.返回值是bytes型別

(基於這兩點,可以應用在server端,將服務端的返回直接以bytes的格式直接send給客戶端,

實現在客戶端的顯示)

問題1:粘包問題

粘包問題:實際是由tcp協議在傳輸資料時的2大特性來的

tcp協議又叫流式協議,在資料傳輸上,只要客戶端傳送相應的資料請求,

服務端會將資料像流水一樣不斷的send給客戶端

基於這個特點,就會存在乙個問題,當客戶端給服務端傳送一條命令,服務端成功接收並將命令的

結果返回到客戶端的時候,由於客戶端recv()的數量限制,可以一次不能完全取出,

這個時候就會存在,下次輸入命令客戶端首先拿到的返回值就是上次殘留的沒有收完的資料

基於粘包問題的解決思路就是:

發資料之前先把報頭髮給對方,讓對方先知道要收的報頭的長度,後面再傳資料檔案

自定義報頭:

為甚麼要自定義報頭:

因為struck path(『i』,3443242)

1.』i『:型別不同,後面數字的長度大小也不同,大小是有限的(當超出範圍時會報錯)

2.因為報頭裡面含有的內容可能不僅僅只有total_siz還有filename、hash等等,知識單純的把total_size

當做報頭傳入不合理,所以我們要自定義報頭

server端配置

from socket import *

import socket,subprocess,struct,json

server = socket.socket(af_inet, sock_stream)

server.bind(('127.0.0.1', 8080))

server.listen(5)

while true:

conn,client = server.accept()

print(client)

while true:

try:

cmd = conn.recv(1024)

if len(cmd) == 0: break

obj=subprocess.popen(

cmd.decode('utf-8'),

shell=true,

stdout=subprocess.pipe,

stderr=subprocess.pipe,

)out=obj.stdout.read()

err=obj.stderr.read()

#製作報頭

header_dic=

#對包頭進行序列化

header_json=json.dumps(header_dic) #字串格式

header_bytes=header_json.encode('utf-8')

#1.先髮型報頭的長度 len(header_bytes) struck為固定的4個位元組

conn.send(struct.pack('i',len(header_bytes)))

#2.傳送報頭

conn.send(header_bytes)

#3.傳送真是資料

conn.send(out)

conn.send(err)

except connectionreseterror:

break

conn.close()

server.close()

client端配置

from socket import *

import socket,struct,json

client=socket.socket(af_inet,sock_stream)

client.connect(('127.0.0.1',8080))

while true:

cmd=input('輸入你要操作的命令:')

client.send(cmd.encode('utf-8'))

if len(cmd) == 0:continue

#1.先收報頭的四個位元組,首先拿到報頭傳來的長度-》bytes

header=client.recv(4) #i型別足夠了 header為bytes型別

header_size=struct.unpack('i',header)[0] #拿到元祖形式,取第乙個就是整個報頭的長度

print(header_size) #為報頭的長度值

#2.再收報頭(對應服務端的conn.send(header_bytes))

header_bytes=client.recv(header_size) #根據報頭的固定長度去收接收

#3.解析包頭(就是將header_bytes檔案先解碼成json格式)

header_str=header_bytes.decode('utf-8')

header_dic=json.loads(header_str)

print(header_dic)

total_size=header_dic['total_size']

print(total_size)

recv_size=0 #定義乙個初始的接收變數為0,只是個計數變數,為了統計與總的total_size的len大小

res=b''

while recv_size < total_size:

recv_data=client.recv(1024) #每次傳過來的recv_data是bytes型別

res+=recv_data

recv_size+=len(recv_data) #迴圈增加每次接收值的長度

# cmd=client.recv(1024)

print(res.decode('gbk'))

client.close()

粘包問題的最終解決方案,分析:

服務端:

目的為了自定義報頭(報頭中不僅包含長度,可能還有檔名等資訊)

subprocess

…1.製作報頭(字典形式)

header_dic={

『filename』:『a.txt』,

『total_size』:len(out)+len(err),

『hash』:『abc32i5o24』

2.通過json將報頭序列化再encode為bytes型別

header_json=json.dumps(header_dic) #字串型別

header_bytes=header_json.encode(『utf-8』) #bytes型別

3.傳送報頭的長度,struck目的是固定封裝好的報頭為4個位元組長度 *****對應客戶端剛開始 header=client.recv(4)

struck: 1.將數字轉為bytes型別,保證傳送過去的是bytes型別 2.固定4個位元組

第一次發:conn.send(struck.pack(『i』,len(header_bytes)))

先傳給客戶端固定了收的時候報頭的長度,不至於報頭和其他內容粘在一起

4.傳送報頭 *****==對應客戶端接收報頭 header_bytes=client.recv(header_size)

第二次發:conn.send(header_bytes)

5.傳送真實的資料

conn.send(out)

conn.send(err)

客戶端:(bytes–int)

1.通過服務端返回的位元組,拿到報頭的的長度

header=client.recv(4) #header是4個bytes位元組

header_size=struck.unpack(『i』,header)[0] #位元組頭-拿到int大小

2.再收報頭

header_bytes=client.recv(header_size) #bytes型別

3.解析報頭(1.先把記憶體中存放的bytes型別,用decode(『utf-8』)解碼為字串)

header_json=header_bytes.decode(『utf-8』)

header_dic=json.loads(header_json) #json反序列化出字典格式 (對應server第2步)

print(header_dic)

4.拿到字典中的報頭大小

total_size=header_dic[『total_size』]

print(total_size)

struck功能輔助理解:所以客戶端剛開始接收4 大小是足夠把報頭接收完的。

struck工鞥理解:

import struct

#將整型轉成bytes

res=struct.pack(『i』,1232435436)

print(res,len(res)) #res:為bytes型別,還是固定長度4(一般情況已經可以包含很多)

#將bytes轉成整型

aa=struct.unpack(『i』,res)

print(aa,aa[0],type(aa)) #

print(len(res)) #4 一般情況多數bytes 4個長度足夠了

「」「結果

b』\xecxui』 4

(1232435436,) 1232435436

4「」」

Socket程式設計 TCP粘包問題及解決方案

tcp是個流協議,它存在粘包問題 tcp是乙個基於位元組流的傳輸服務,流 意味著tcp所傳輸的資料是沒有邊界的。這不同於udp提供基於訊息的傳輸服務,其傳輸的資料是有邊界的。tcp的傳送方無法保證對等方每次接收到的是乙個完整的資料報。主機a向主機b傳送兩個資料報,主機b的接收情況可能是 產生粘包問題...

Socket程式設計 TCP粘包問題及解決方案

tcp是個流協議,它存在粘包問題 tcp是乙個基於位元組流的傳輸服務,流 意味著tcp所傳輸的資料是沒有邊界的。這不同於udp提供基於訊息的傳輸服務,其傳輸的資料是有邊界的。tcp的傳送方無法保證對等方每次接收到的是乙個完整的資料報。主機a向主機b傳送兩個資料報,主機b的接收情況可能是 產生粘包問題...

粘包現象與解決方案

粘包是指兩次輸出結果粘到一起,它的發生主要是因為socket緩衝區導致的,粘包只在tcp中產生,不在udp產生 使用struct模組,先報頭長度進行打包發給客戶端,客戶端收到之後先解包報頭長度,再接收真實的資料 例子 服務端 usr bin env python3 coding utf 8 impo...