1. 程式人生 > >socket網路程式設計-粘包

socket網路程式設計-粘包

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'))