解決 TCP_socket 粘包問題



所謂粘包問題主要仍是C/S兩端數據傳輸時 由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的


根本緣由:
粘包是由TCP協議自己形成的,TCP爲提升傳輸效率,發送方每每要收集到足夠多的數據後才發送一個TCP段。若連續幾回須要send的數據都不多,一般TCP會根據優化算法把這些數據合成一個TCP段後一次發送出去,這樣接收方就收到了粘包數據。

解決方法:

一、自定義字典類型 的數據報頭{文件名:a,文件的size:1090}計算出該報頭的長度(len(字節)),

二、使用
struct.pack('i',報頭長度(一個數字))把一個數字壓縮成固定的size 4個字節,發送給對端。

三、對端 struct.unpack(‘i’,recv(4))接收固定大小4個字節;這就是接收到了 報頭的長度。

4.recv(報頭長度)這就是發送過來的報頭信息了






import struct
a='您好'
a=len(a.encode('utf-8')) #字節的長度=====這個數據有多大字節
# 1英文字母utf-8編碼後=1字節
# #1中文字符 utf-8編碼後=3個字節
#
a=struct.pack('i',a) #struct.pack把數字轉換成 固定大小 4個字節的 字節
print(len(a)) #4個字節
print(struct.unpack('i',a)[0]) #struct.unpack[0] 把成本身解包會數字




服務端
# import socket
# import subprocess
# iphon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #(創建一個socket對象)
# iphon.bind(('127.0.0.1',8080))  #綁定到 IP+端口上 成爲惟一的socket
# iphon.listen(5)                   #設置鏈接池的個數
# print('starting........')
# while True:  #鏈接循環
#     conn,addr=iphon.accept()  #等待電話鏈接
#     print('電話線路是',conn)
#     print('客戶手機號:',addr)
#     while True: #通訊循環 發送和接收
#         try:
#             data=conn.recv(1024) #接受消息 最大從內存裏接受1024MB數據
#             print('客戶端發來的消息是%s'%data)
#             data=data.decode('utf-8') #從客戶端發來的數據是通過編碼的字節數據 因此須要解碼 成Unicode
#             res=subprocess.Popen( data, shell=True, stdout=subprocess.PIPE)  #解碼後傳進subprocess.Popen去cmd執行
#             data1 = res.stdout.read().decode('gbk')      #cmd是gbk編碼因此須要gbk解碼
#             print(data1)                                  #打印解碼後的結果
#             data1=data1.encode('gbk')                   #編碼
#             conn.send(data1)  #發送消息                  #編碼後再傳輸給客戶端
#         except Exception:
#             break
#     conn.close()
# iphon.close()

import socket
import struct
import json
phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
phon.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
phon.bind(("127.0.0.1",8090)) 
phon.listen(80)                 #
while True:
    conn,addr=phon.accept()
    while True:
        try:
            data=conn.recv(4)      #接受4個字節的報頭
            data=struct.unpack('i',data)[0]
            data1=conn.recv(data).decode('utf-8')  #接受 字節類型的報頭信息
                                                    #{"file_name": "\u4f60\u597d", "file_size": 6}
            data1=json.loads(data1)
            print(data1)
            da=conn.recv(data1['file_size']).decode('utf-8') #接收真實數據
            print(da)





            # print("接受到來自客戶端發來的消息",data)
            conn.send('sss'.encode('utf-8'))    #發送接受的消息 給某一個客戶端
        except Exception:
            break
    conn.close()
phon.close()

  










客戶端
import socket
import struct
import json
phon=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phon.connect(('127.0.0.1',8090))     #客戶端的phon.connect正好對於服務端的phon1.accept()
while True: #循環通訊
    mes=input('---->: '.strip())
    if not mes:
        continue
    mes = mes.encode('utf-8')
    mes_len = len(mes)
    head = {'file_name': mes.decode('utf-8'), 'file_size': mes_len}
    head_json = json.dumps(head)  # {"file_name": "a", "file_size": 1}
    head_bytes = head_json.encode('utf-8')
    head_bytes_len = len(head_bytes)  # 51
    struct1 = struct.pack('i',head_bytes_len)
    send_=phon.send(struct1)  #發送4個字節的 報頭長度
    data=phon.send(head_bytes) #發送報頭
    da=phon.send(mes)          #發送真實數據


phon.close()
相關文章
相關標籤/搜索