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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

网络编程---黏包

發布時間:2023/12/1 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络编程---黏包 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基于UDP協議的socket

udp的server 不需要進行監聽也不需要建立連接,在啟動服務之后只能被動的等待客戶端發送消息過來。

客戶端發送消息的同時還會 自帶地址信息,消息回復的時候 不僅需要發送消息 還需把對方的地址填上。

udp的client 不需要connect 因為UDP協議是不需要連接的,直接了解到對方的ip和端口信息就發送數據就行了。

sendto和recvfrom的使用方法完全和server端一樣。

udp是無鏈接的,先啟動哪一端都不會報錯

import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #創建一個服務器的套接字 udp_sk.bind(('127.0.0.1',9000)) #綁定服務器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) # 對話(接收與發送) udp_sk.close() # 關閉服務器套接字 server端 import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr) client端

用udp協議的socket寫一個簡易的qq聊天

import socket ip_port=('127.0.0.1',8081) udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_sock.bind(ip_port)while True:qq_msg,addr=udp_server_sock.recvfrom(1024)print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))back_msg=input('回復消息: ').strip()udp_server_sock.sendto(back_msg.encode('utf-8'),addr) server端 import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)qq_name_dic={'sole':('127.0.0.1',8081),'喜羊羊':('127.0.0.1',8081),'灰太狼':('127.0.0.1',8081),'xxx':('127.0.0.1',8081), }while True:qq_name=input('請選擇聊天對象: ').strip()while True:msg=input('請輸入消息,回車發送,輸入q結束和他的聊天: ').strip()if msg == 'q':breakif not msg or not qq_name or qq_name not in qq_name_dic:continueudp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))udp_client_socket.close() client端

時間服務器

import time import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8090)) while True:strf,addr = sk.recvfrom(1024)strf = strf.decode('utf-8')res = time.strftime(strf).encode('utf-8')sk.sendto(res,addr) sk.close() import socket sk = socket.socket(type=socket.SOCK_DGRAM) addr = ('127.0.0.1',8090) info = input('>>>').encode('utf-8') sk.sendto(info,addr) ret,addr = sk.recvfrom(1024) print(ret.decode('utf-8'))sk.close()

socket參數的詳解

創建socket對象的參數說明:

?

family地址系列應為AF_INET(默認值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域實際上是使用本地 socket 文件來通信)
type套接字類型應為SOCK_STREAM(默認值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
SOCK_STREAM?是基于TCP的,有保障的(即能保證數據正確傳送到對方)面向連接的SOCKET,多用于資料傳送。
SOCK_DGRAM?是基于UDP的,無保障的面向消息的socket,多用于在網絡上發廣播信息。
proto協議號通常為零,可以省略,或者在地址族為AF_CAN的情況下,協議應為CAN_RAW或CAN_BCM之一。
fileno如果指定了fileno,則其他參數將被忽略,導致帶有指定文件描述符的套接字返回。
與socket.fromfd()不同,fileno將返回相同的套接字,而不是重復的。
這可能有助于使用socket.close()關閉一個獨立的插座。

黏包

黏包現象

res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) ''' 的結果的編碼是以當前所在的系統為準的,如果是windows,那么res.stdout.read()讀出的就是GBK編碼的,在接收端需要用GBK解碼且只能從管道里讀一次結果同時執行多條命令之后,得到的結果很可能只有一部分,在執行其他命令的時候又接收到之前執行的另外一部分結果,這種顯現就是黏包。 '''

基于tcp協議實現的黏包

import socket sk = socket.socket() sk.bind(('127.0.0.1',8090)) sk.listen()conn,addr = sk.accept() while True:cmd = input('>>>')conn.send(cmd.encode('utf-8'))ret = conn.recv(1024).decode('utf-8')print(ret)conn.close() sk.close() import socket import subprocess sk = socket.socket() sk.connect(('127.0.0.1',8090)) while True:cmd = sk.recv(1024).decode('gbk')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = 'stdout :'+(ret.stdout.read()).decode('gbk')std_err = 'stderr :'+(ret.stderr.read()).decode('gbk')print(std_out)print(std_err)sk.send(std_out.encode('utf-8'))sk.send(std_err.encode('utf-8')) sk.close()

注意:只有TCP有黏包現象但不丟包,UDP永遠不會黏包只會丟包

基于udp協議實現的黏包

import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',8090)) msg,addr = sk.recvfrom(10240) while True:cmd = input('>>>')if cmd == 'q':breaksk.sendto(cmd.encode('utf-8'),addr)msg,addr = sk.recvfrom(10240)print(msg.decode('utf-8'))sk.close() import socket import subprocess sk = socket.socket(type=socket.SOCK_DGRAM) addr = ('127.0.0.1',8090) sk.sendto('吃了么'.encode('utf-8'),addr) while True:cmd,addr = sk.recvfrom(10000)ret = subprocess.Popen(cmd.decode('gbk'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = 'stdout :'+(ret.stdout.read()).decode('gbk')std_err = 'stderr :'+(ret.stderr.read()).decode('gbk')print(std_out)print(std_err)sk.sendto(std_out.encode('utf-8'),addr)sk.sendto(std_err.encode('utf-8'),addr) sk.close()

為什么會出現黏包現象?

發送端可以是一K一K地發送數據,而接收端的應用程序可以兩K兩K地提走數據,當然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據。 也就是說,應用程序所看到的數據是一個整體,或說是一個流(stream),一條消息有多少字節對應用程序是不可見的,因此TCP協議是面向流的協議,這也是容易出現粘包問題的原因。 而UDP是面向消息的協議,每個UDP段都是一條消息,應用程序必須以消息為單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不同的。 怎樣定義消息呢?可以認為對方一次性write/send的數據為一個消息,需要明白的是當對方send一條信息的時候,無論底層怎樣分段分片,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協議:udp的recvfrom是阻塞的,一個recvfrom(x)必須對唯一一個sendinto(y),收完了x個字節的數據就算完成,若是y>x數據就丟失,這意味著udp根本不會粘包,但是會丟數據,不可靠。 可靠黏包的tcp協議:tcp的協議數據不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端總是在收到ack時才會清除緩沖區內容。數據是可靠的,但是會粘包。 recv里指定的1024意思是從緩存里一次拿出1024個字節的數據 send的字節流是先放入己端緩存,然后由協議控制將緩存內容發往對端,如果待發送的字節流大小大于緩存剩余空間,那么數據丟失,用sendall就會循環調用send,數據不會丟失

會發生黏包的兩種情況

情況一 發送方的緩存機制

發送端需要等緩沖區滿才發送出去,造成粘包(發送數據時間間隔很短,數據了很小,會合到一起,產生粘包)

情況二 接收方的緩存機制

接收方不及時接收緩沖區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生粘包)?

轉載于:https://www.cnblogs.com/soleZ/p/8359858.html

總結

以上是生活随笔為你收集整理的网络编程---黏包的全部內容,希望文章能夠幫你解決所遇到的問題。

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