tcp協議又叫流式協議,在我們利用tcp協議傳輸資料時,tcp協議有乙個優化機制,比如我們在極短的時間內連續輸入10次,每次只有乙個位元組,他會在我們記憶體中開闢乙個快取區,一次性傳輸在對方機器的快取區之中,當對方需要取資料時,再到自己的快取區取。就是因為這個原因產生粘包問題。
如昨天的學習筆記中,我們一次取1024位元組的資料,但在真實傳輸中不可能只有這麼少的資料,超過1024位元組的資料會留在快取區中,等待下次列印時候再取出,但是下次我們可能已經輸入了新的命令,但列印的是老命令的內容。
我們的解決思路是一次性取完每次的資料,但是又會有乙個新問題,我們如何知道每次傳輸過來的資料長度?如果不知道資料長度我們肯定無無法一次迴圈取完。所以我們需要在傳送這個正文資料前,先傳送一段報頭,報頭來標記後面正文的長度。
當然這只是乙個簡單的思路,我們的報頭不可能只包含資料長度,可能還需要包括資料的加密方法,資料的標題等,那麼我們又產生了乙個新問題,報頭的長度又不固定了。所以我們可以再在報頭前先發一段記錄報頭長度的資料,依次傳送,解決該問題。
須知:只有tcp有粘包現象,udp永遠不會粘包,為何,且聽我娓娓道來
首先需要掌握乙個socket收發訊息的原理
傳送端可以是一k一k地傳送資料,而接收端的應用程式可以兩k兩k地提走資料,當然也有可能一次提走3k或6k資料,或者一次只提走幾個位元組的資料,也就是說,應用程式所看到的資料是乙個整體,或說是乙個流(stream),一條訊息有多少位元組對應用程式是不可見的,因此tcp協議是面向流的協議,這也是容易出現粘包問題的原因。而udp是面向訊息的協議,每個udp段都是一條訊息,應用程式必須以訊息為單位提取資料,不能一次提取任意位元組的資料,這一點和tcp是很不同的。怎樣定義訊息呢?可以認為對方一次性write/send的資料為乙個訊息,需要明白的是當對方send一條資訊的時候,無論底層怎樣分段分片,tcp協議層會把構成整條訊息的資料段排序完成後才呈現在核心緩衝區。
例如基於tcp的套接字客戶端往服務端上傳檔案,傳送時檔案內容是按照一段一段的位元組流傳送的,在接收方看了,根本不知道該檔案的位元組流從何處開始,在何處結束
所謂粘包問題主要還是因為接收方不知道訊息之間的界限,不知道一次性提取多少位元組的資料所造成的。
此外,傳送方引起的粘包是由tcp協議本身造成的,tcp為提高傳輸效率,傳送方往往要收集到足夠多的資料後才傳送乙個tcp段。若連續幾次需要send的資料都很少,通常tcp會根據優化演算法把這些資料合成乙個tcp段後一次傳送出去,這樣接收方就收到了粘包資料。
tcp(transport control protocol,傳輸控制協議)是面向連線的,面向流的,提供高可靠性服務。收發兩端(客戶端和伺服器端)都要有一一成對的socket,因此,傳送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(nagle演算法),將多次間隔較小且資料量小的資料,合併成乙個大的資料塊,然後進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通訊是無訊息保護邊界的。
udp(user datagram protocol,使用者資料報協議)是無連線的,面向訊息的,提供高效率服務。不會使用塊的合併優化演算法,, 由於udp支援的是一對多的模式,所以接收端的skbuff(套接字緩衝區)採用了鏈式結構來記錄每乙個到達的udp包,在每個udp包中就有了訊息頭(****位址,埠等資訊),這樣,對於接收端來說,就容易進行區分處理了。即面向訊息的通訊是有訊息保護邊界的。
tcp是基於資料流的,於是收發的訊息不能為空,這就需要在客戶端和服務端都新增空訊息的處理機制,防止程式卡住,而udp是基於資料報的,即便是你輸入的是空內容(直接回車),那也不是空訊息,udp協議會幫你封裝上訊息頭,實驗略
udp的recvfrom是阻塞的,乙個recvfrom(x)必須對唯一乙個sendinto(y),收完了x個位元組的資料就算完成,若是y>x資料就丟失,這意味著udp根本不會粘包,但是會丟資料,不可靠
tcp的協議資料不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端總是在收到ack時才會清除緩衝區內容。資料是可靠的,但是會粘包。
兩種情況下會發生粘包。
傳送端需要等緩衝區滿才傳送出去,造成粘包(傳送資料時間間隔很短,資料了很小,會合到一起,產生粘包)
服務端
import subprocess
import os
import struct
import json
from socket import *
server = socket(af_inet, sock_stream)
# print(server)
server.bind(('127.0.0.1', 8082))
server.listen(5)
while true:
conn, client_addr = server.accept()
print(conn)
print(client_addr)
while true:
try:
msg = conn.recv(1024).decode('utf-8')
cmd,file_path=msg.split()
if cmd == "get":
# 一、製作報頭
header_dic=
header_json=json.dumps(header_dic)
header_json_bytes=header_json.encode('utf-8')
# 二、傳送資料
# 1、先傳送報頭的長度
header_size=len(header_json_bytes)
conn.send(struct.pack('i',header_size))
# 2、再傳送報頭
conn.send(header_json_bytes)
# 3、最後傳送真實的資料
with open(r'%s' %file_path,mode='rb') as f:
for line in f:
conn.send(line)
except exception:
break
conn.close()
server.close()
客戶端
import struct
import json
from socket import *
client = socket(af_inet, sock_stream)
# print(client)
client.connect(('127.0.0.1', 8082))
while true:
cmd = input(">>: ").strip() # get 檔案路徑
if len(cmd) == 0:
continue
client.send(cmd.encode('utf-8'))
# 1、先接收報頭的長度
res=client.recv(4)
header_size=struct.unpack('i',res)[0]
# 2、再接收報頭
header_json_bytes=client.recv(header_size)
header_json=header_json_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
# 3、最後接收真實的資料
total_size=header_dic['total_size']
filename=header_dic['filename']
recv_size = 0
with open(r"d:\python全棧15期\day32\**\03 定製複雜的報頭\版本2\download\%s" %filename, mode='wb') as f:
while recv_size < total_size:
data = client.recv(1024)
f.write(data)
recv_size += len(data)
client.close()
tcp粘包問題
什麼是粘包問題 粘包問題的起因是socket的快取機制。簡而言之 粘包問題就是如何將連續的資料按照不同的資料幀截斷,以及如何處理殘包情況。分割資料需要按需分配。處理殘包也很簡單 等 等它發來下一包資料,不管他發來多少資料,先拿來512,接到上次那512後面。湊成完整的資料幀。當然也有可能你發現這次來...
TCP粘包問題
原因 tcp提供的是一種位元組流服務,沒有訊息保護邊界。傳送端需要等緩衝區滿才傳送出去,造成粘包 接收方不及時接收緩衝區的包,造成多個包接收 解決方法 一是對於傳送方引起的粘包現象,使用者可通過程式設計設定來避免,tcp提供了強制資料立即傳送的操作指令push,tcp軟體收到該操作指令後,就立即將本...
TCP粘包問題
tcp粘包是指傳送方傳送的若干包資料到接收方接收時粘成一包,從接收緩衝區看,後一包資料的頭緊接著前一包資料的尾。出現粘包現象的原因是多方面的,它既可能由傳送方造成,也可能由接收方造成。傳送方引起的粘包是由tcp協議本身造成的,tcp為提高傳輸效率,傳送方往往要收集到足夠多的資料後才傳送一包資料。若連...