socket網路程式設計-粘包
阿新 • • 發佈:2019-01-01
1.什麼是粘包
只要tcp有粘包現象,udp不會粘包
粘包主要問題是接收方不知道訊息之間的界限,不知道一次性提取多少位元組的資料而造成的
tcp和dup的區別
1.tcp是基於資料流的,收發的訊息不能為空,這酒需要在客戶端和服務端都新增空訊息的處理機制,防止程式卡主
2.udp是基於資料報,輸入傳送空內容(直接回車),那也不是空訊息,udp協議會幫你封裝一個訊息頭(訊息來源地址,埠等資訊)即面向訊息的通訊是有訊息保護邊界的。
為什麼出現粘包
tcp的協議資料不會丟失,是因為沒有收完的資料會在基於上次繼續接受,已端總是在收到ack時才清楚緩衝區內容,所以資料是可靠,但會出現粘包現象
udp的recvfrom是阻塞的,一個recvfrom(x)必須對應一個sendinto(y),收完x個位元組是資料就算完成,
若是y>x那就意味著資料丟失,這意味著udp不會出現粘包,但是資料會丟失,不可靠
3.tcp(傳輸控制協議) 為什麼可靠?
tcp是面向連線,面向資料流,提供可靠性服務
tcp傳輸資料時候先把資料傳輸到自己快取,然後通過協議控制將快取中資料發往對端,
對端返回一個ack=1,傳送端則清理快取中資料
對端返回一個ack=0,則重新發送,所以tcp可靠
2.udp(使用者資料報協議)為什麼不可靠?
udp.是面向無連線的,面向訊息流,提供高效率服務
只管把資料傳送給對端,不管對端是否收到
對端不會返回確認收到資訊,所以不可靠
#粘包終極解決方案
from socket import *
import subprocess
import struct
import json
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,client_addr=server.accept()
print('新的客戶端',client_addr)
while True:
try:
cmd=conn.recv(1024) #cmd=b'dir'
if len(cmd) == 0:break
# 執行系統命令
obj=subprocess.Popen(cmd.decode('utf-8'), #使用者輸入命令可以直接執行
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
#先製作報頭
header_dic={
'filename':'a.txt',
'total_size':len(stdout) + len(stderr),
'hash':'xasf123213123'
}
header_json=json.dumps(header_dic) #json轉換成字串
header_bytes=header_json.encode('utf-8') #在把字串轉換成bytes型別
#1、先把報頭的長度len(header_bytes)打包成4個bytes,然後傳送
conn.send(struct.pack('i',len(header_bytes)))
#2、傳送報頭
conn.send(header_bytes)
#3、再發送真實的資料
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
#1、先收4個位元組,該4個位元組中包含報頭的長度
header_len=struct.unpack('i',client.recv(4))[0]
#2、再接收報頭
header_bytes=client.recv(header_len)
#從報頭中解析出想要的內容
header_json=header_bytes.decode('utf-8') #bytes轉換成utf-8字串
header_dic=json.loads(header_json) #再把用json轉換出原來的字典格式
print(header_dic)
total_size=header_dic['total_size'] #檔案大小
#3、再收真實的資料
recv_size=0
res=b''
while recv_size < total_size :
data=client.recv(1024)
res+=data
recv_size+=len(data)
print(res.decode('gbk'))