解決 TCP_socket 粘包問題




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


根本原因:
粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化算法把這些數據合成一個TCP段后一次發送出去,這樣接收方就收到了粘包數據。

解決方法:

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

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

3、對端 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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM