日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

粘包问题

發布時間:2024/4/17 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 粘包问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.什么是粘包?

粘包指的是數據與數據之間沒有明確的分界線,導致不能正確讀取數據!

TCP、UDP協議傳輸數據時的具體流程,TCP協議也稱之為流式協議,UDP稱之為數據報協議

?

應用程序無法直接操作硬件,應用程序想要發送數據則必須將數據交給操作系統,而操作系統需要需要同時為所有應用程序提供數據傳輸服務,也就意味著,操作系統不可能立馬就能將應用程序的數據發送出去,就需要為應用程序提供一個緩沖區,用于臨時存放數據,具體流程如下:

發送方:

當應用程序調用send函數時,應用程序會將數據從應用程序拷貝到操作系統的緩存區,再由操作系統從緩存區讀取數據并發送出去.

接收方:

對方計算機操作系統先收到數據,先將數據存放到操作系統的緩沖區中,當應用程序調用recv時,實際上是從操作系統緩沖區將數據拷貝到應用程序的過程.

上述過程對于TCP和UDP都是相同的,不同之處在于:

UDP:

UDP在收發數據時是基于數據包的,即一個包一個包的發送,包與包之間有著明確的分界,到達操作系統緩存區后也是一個一個獨立的數據包,接收方從操作系統緩沖區中將數據包拷貝到應用程序.

這種方式存在的問題:

1.發送方發送的數據長度每個操作系統會有不同的限制,數據超過限制則無法發送

2.接收方接受數據時如果應用程序的提供的緩存容量小于數據包的長度將造成數據丟失,而緩存區大小不可能無限放大.

TCP:

當我們需要傳輸較大的數據,或需要保證數據的完整性時 ,最簡單的方式就是使用TCP協議

與UDP不同TCP會增加一套校驗的規則來保證數據的完整性,會將超過TCP最大長度的數據拆分成多個TCP包,并在傳輸數據時為每一個TCP數據包指定一個順序號,接收方在收到TCP數據包后按照順序將數據包進行重組,重組后的數據全都是二進制數據,且每次收到的二進制數據之間沒有明顯的分界.

基于這種工作機制TCP在三種情況下會發送粘包問題

1.當單個數據包較小時接收方可能一次性讀取多個包的數據

2.當整體數據較大時接收方可能一次僅讀取一個包的一部分內容

3.TCP為了提高效率,會將數據較小且發送間隔較短的數據合并發送,該機制也會導致發送方將兩個數據包粘在一起發送.

二.粘包的解決方案

1.基礎解決方案

服務器:
import socket,subprocess
server = socket.socket()
server.bind(('192.168.12.207',4396))
server.listen()
while True:
  client,addr = server.accept()
  while True:
    try:
      cmd = client.recv(1024).decode('utf-8')
      p = subprocess.Popen(cmd,shell=True,\
      stdout=subprocess.PIPE,stderr=subprocess.PIPE)
      data = p.stdout.read()
      err_data = p.stderr.read()
      print('數據長度:%s'%(len(data) + len(err_data)))
      length = len(data) + len(err_data)
      len_str = str(length).encode('utf-8')
      #先發送長度數據再發送真實數據,長度數據可能和真實數據粘在一起,而接收方不知道長度數據的字節數,導致粘包
      client.send(len_str)
      client.send(data)
      client.send(err_data)
    except ConnectionResetError:
      print('連接異常')
      client.close()
      break
客戶端:
import socket
client = socket.socket()
client.connect(('192.168.12.207',4396))
while True:
  cmd = input('>>:').strip()
  client.send(cmd.encode('utf-8'))
  length = client.recv(1024)
  len_data =int(length.decode('utf-8'))
  print('數據長度為%s'%len_data)
  data_all= b''
  data_size = 0
  while data_size < len_data:
    data = client.recv(len_data)
    data_size += len(data)
    data_all += data
  print('接受長度為%s'%data_size)
  print(data_all.decode('GBK'))

為了解決長度數據和真實數據的粘包問題,我們要用到:struct結構體 可以將python中的數據類型轉換成C中的結構體(轉換成bytes)

import struct
num = 100
#該函數 將一個python中的數據類型轉換成bytes 第一個參數通常是i 其能轉換的數據范圍是C語言中的int范圍
#如果int不夠,那就使用q 表示的是long long型
res = struct.pack('i',num)
print(res)
print(len(res))

#該函數將bytes類型轉換
res2 = struct.unpack('i',res)
print(res2) #得到一個元組(100,)
print(res2[0]) #得到整型 100

修正版本:

服務器:
import socket,subprocess,struct
server = socket.socket()
server.bind(('192.168.12.207',4396))
server.listen()
while True:
  client,addr = server.accept()
  while True:
    try:
      cmd = client.recv(1024).decode('utf-8')
      p = subprocess.Popen(cmd,shell=True,\
      stdout=subprocess.PIPE,stderr=subprocess.PIPE)
      data = p.stdout.read()
      err_data = p.stderr.read()
      print('數據長度:%s'%(len(data) + len(err_data)))
      length = len(data) + len(err_data)
      len_data = struct.pack('i',length)
      client.send(len_data)
      client.send(data)
      client.send(err_data)
    except ConnectionResetError:
      print('連接異常')
      client.close()
      break
客戶端:
import socket,struct
client = socket.socket()
client.connect(('192.168.12.207',4396))
while True:
  cmd = input('>>:').strip()
  client.send(cmd.encode('utf-8'))
  #接受一個長度為4的固定字節
  length = client.recv(4)
  len_data = struct.unpack('i',length)[0]
  print('數據長度為%s'%len_data)
  data_all= b''
  data_size = 0
  while data_size < len_data:
    data = client.recv(1024)
    data_size += len(data)
    data_all += data
  print('接受長度為%s'%data_size)
  print(data_all.decode('GBK'))

2.自定義報頭解決粘包

上述方案已經完美的解決了粘包的問題,但是擴展性不高,例如我們要實現文件上傳下載,不光要傳輸文件的數據,還需要傳輸文件名字,md5值等等,如何實現?

具體思路:

發送端:

1.先將所有的額外信息打包到一個頭中

2.然后先發送頭部數據

3.最后發送真實數據

接收端:

1.接受固定長度的頭部數據長度

2.根據長度數據獲取頭部數據

3,根據頭部數據獲取真實數據

服務器:
import socket,subprocess,struct,datetime,json
server = socket.socket()
server.bind(('192.168.12.207',4396))
server.listen()
while True:
  client,addr = server.accept()
  while True:
    try:
      cmd = client.recv(1024).decode('utf-8')
      p = subprocess.Popen(cmd,shell=True,\

stdout=subprocess.PIPE,stderr=subprocess.PIPE)

      data = p.stdout.read()
      err_data = p.stderr.read()
      print('數據長度:%s'%(len(data) + len(err_data)))
      length = len(data) + len(err_data)
      #先發送額外數據,將要發送的真實數據長度先存到字典中
      t = {}
      t['time'] = str(datetime.datetime.now())
      t['size'] = length
      #將字典轉換成json格式
      t_json = json.dumps(t)
      #將字典轉換成字節
      t_data = t_json.encode('utf-8')
      t_length = struct.pack('i',len(t_data))
      #1.先發送額外數據長度
      client.send(t_length)
      #2.發送額外信息
      client.send(t_data)
      #3.發送真實數據
      client.send(data)
      client.send(err_data)
    except ConnectionResetError:
      print('連接異常')
      client.close()
      break
客戶端:
import socket,struct,json
client = socket.socket()
client.connect(('192.168.12.207',4396))
while True:
  cmd = input('>>:').strip()
  if not cmd:
    print('命令不能為空!')
    continue
  client.send(cmd.encode('utf-8'))
  #1.先接受額外信息長度
  length = client.recv(4)
  len_data = struct.unpack('i',length)[0]
  #2.接受額外信息
  t_data = client.recv(len_data)
  print(t_data.decode('utf-8'))
  json_dic = json.loads(t_data.decode('utf-8'))
  print('執行時間為%s'%json_dic['time'])
  data_size = json_dic['size']
  #3,接受真實信息
  data_all= b''
  rcv_size = 0
  while rcv_size < data_size:
    data = client.recv(1024)
    rcv_size += len(data)
    data_all += data
  print('接受長度為%s'%rcv_size)
  print(data_all.decode('GBK'))

?

轉載于:https://www.cnblogs.com/lizeqian1994/p/10191875.html

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的粘包问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。