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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python D28 粘包

發布時間:2024/7/19 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python D28 粘包 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、兩種粘包:

MTU簡單解釋:

MTU是Maximum Transmission Unit的縮寫。意思是網絡上傳送的最大數據包。MTU的單位是字節。 大部分網絡設備的MTU都是1500個字節,也就是1500B。
如果本機一次需要發送的數據比網關的MTU大,大的數據包就會被拆開來傳送,這樣會產生很多數據包碎片,增加丟包率,降低網絡速度

超出緩沖區大小會報下面的錯誤,或者udp協議的時候,你的一個數據包的大小超過了你一次recv能接受的大小,也會報下面的錯誤,tcp不會,但是超出緩存區大小的時候,肯定會報這個錯誤。

subprocess import subprocess cmd = input('請輸入指令>>>') res = subprocess.Popen(cmd, #字符串指令:'dir','ipconfig',等等shell=True, #使用shell,就相當于使用cmd窗口stderr=subprocess.PIPE, #標準錯誤輸出,凡是輸入錯誤指令,錯誤指令輸出的報錯信息就會被它拿到stdout=subprocess.PIPE, #標準輸出,正確指令的輸出結果被它拿到 ) print(res.stdout.read().decode('gbk')) print(res.stderr.read().decode('gbk')) subprocess

注意:

如果是windows,那么res.stdout.read()讀出的就是GBK編碼的,在接收端需要用GBK解碼

且只能從管道里讀一次結果,PIPE稱為管道。

下面是subprocess和windows上cmd下的指令的對應示意圖:subprocess的stdout.read()和stderr.read(),拿到的結果是bytes類型,所以需要轉換為字符串打印出來看。

    

    好,既然我們會使用subprocess了,那么我們就通過它來模擬一個粘包

tcp粘包演示(一):

先從上面粘包現象中的第一種開始:接收方沒有及時接收緩沖區的包,造成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,
服務端下次再收的時候還是從緩沖區拿上次遺留的數據,產生粘包)
server端代碼示例?
Server端

client端代碼示例

import socket ip_port = ('127.0.0.1',8080) size = 1024 tcp_sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = tcp_sk.connect(ip_port) while True:msg=input('>>: ').strip()if len(msg) == 0:continueif msg == 'quit':breaktcp_sk.send(msg.encode('utf-8'))act_res=tcp_sk.recv(size)print('接收的返回結果長度為>',len(act_res))print('std>>>',act_res.decode('gbk')) #windows返回的內容需要用gbk來解碼,因為windows系統的默認編碼為gbk tcp_client端

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

server端

from socket import * ip_port=('127.0.0.1',8080)tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(10) data2=conn.recv(10)print('----->',data1.decode('utf-8')) print('----->',data2.decode('utf-8'))conn.close() tcp_server

client端

import socket BUFSIZE=1024 ip_port=('127.0.0.1',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # res=s.connect_ex(ip_port) res=s.connect(ip_port) s.send('hi'.encode('utf-8')) s.send('meinv'.encode('utf-8')) tcp_client

udp:是面向包的,且包和包之間存在包邊界保護所以不會產生粘包。

在udp的代碼中,我們在server端接收返回消息的時候,我們設置的recvfrom(1024),那么當我輸入的執行指令為‘dir’的時候,dir在我當前文件夾下輸出的內容大于1024,
然后就報錯了,報的錯誤也是下面這個:

解釋原因:是因為udp是面向報文的,意思就是每個消息是一個包,你接收端設置接收大小的時候,必須要比你發的這個包要大,不然一次接收不了就會報這個錯誤,
而tcp不會報錯,這也是為什么ucp會丟包的原因之一,這個和我們上面緩沖區那個錯誤的報錯原因是不一樣的。


粘包原因 發送端可以是一K一K地發送數據,而接收端的應用程序可以兩K兩K地提走數據,當然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據,也就是說,應用程序所看到的數據是一個整體,或說是一個流(stream),一條消息有多少字節對應用程序是不可見的,因此TCP協議是面向流的協議,這也是容易出現粘包問題的原因。而UDP是面向消息的協議,每個UDP段都是一條消息,應用程序必須以消息為單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不同的。怎樣定義消息呢?可以認為對方一次性write/send的數據為一個消息,需要明白的是當對方send一條信息的時候,無論底層怎樣分段分片,TCP協議層會把構成整條消息的數據段排序完成后才呈現在內核緩沖區。例如基于tcp的套接字客戶端往服務端上傳文件,發送時文件內容是按照一段一段的字節流發送的,在接收方看了,根本不知道該文件的字節流從何處開始,在何處結束所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。此外,發送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的數據后才發送一個TCP段。若連續幾次需要send的數據都很少,通常TCP會根據優化算法把這些數據合成一個TCP段后一次發送出去,這樣接收方就收到了粘包數據。1.TCP(transport control protocol,傳輸控制協議)是面向連接的,面向流的,提供高可靠性服務。收發兩端(客戶端和服務器端)都要有一一成對的socket,因此,發送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將多次間隔較小且數據量小的數據,合并成一個大的數據塊,然后進行封包。這樣,接收端,就難于分辨出來了,必須提供科學的拆包機制。 即面向流的通信是無消息保護邊界的。2.UDP(user datagram protocol,用戶數據報協議)是無連接的,面向消息的,提供高效率服務。不會使用塊的合并優化算法,, 由于UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩沖區)采用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對于接收端來說,就容易進行區分處理了。 即面向消息的通信是有消息保護邊界的。3.tcp是基于數據流的,于是收發的消息不能為空,這就需要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,而udp是基于數據報的,即便是你輸入的是空內容(直接回車),那也不是空消息,udp協議會幫你封裝上消息頭,實驗略 udp的recvfrom是阻塞的,一個recvfrom(x)必須對唯一一個sendinto(y),收完了x個字節的數據就算完成,若是y>x數據就丟失,這意味著udp根本不會粘包,但是會丟數據,不可靠tcp的協議數據不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端總是在收到ack時才會清除緩沖區內容。數據是可靠的,但是會粘包。 粘包機制

tcp和udpb比較

補充問題一:為何tcp是可靠傳輸,udp是不可靠傳輸tcp在數據傳輸時,發送端先把數據發送到自己的緩存中,然后協議控制將緩存中的數據發往對端,對端返回一個ack=1,發送端則清理緩存中的數據,對端返回ack=0,則重新發送數據,所以tcp是可靠的。而udp發送數據,對端是不會返回確認信息的,因此不可靠補充問題二:send(字節流)和sendallsend的字節流是先放入己端緩存,然后由協議控制將緩存內容發往對端,如果待發送的字節流大小大于緩存剩余空間,那么數據丟失,用sendall就會循環調用send,數據不會丟失,一般的小數據就用send,因為小數據也用sendall的話有些影響代碼性能,簡單來講就是還多while循環這個代碼呢。用UDP協議發送時,用sendto函數最大能發送數據的長度為:65535- IP頭(20) – UDP頭(8)=65507字節。用sendto函數發送數據時,如果發送數據長度大于該值,則函數會返回錯誤。(丟棄這個包,不進行發送) 用TCP協議發送時,由于TCP是數據流協議,因此不存在包大小的限制(暫不考慮緩沖區的大小),這是指在用send函數時,數據長度參數不受限制。而實際上,所指定的這段數據并不一定會一次性發送出去,如果這段數據比較長,會被分段發送,如果比較短,可能會等待和下一次數據一起發送。 tcp_udp比較

粘包的原因:主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的

二、粘包現象的解決
解決方案(一): 問題的根源在于,接收端不知道發送端將要傳送的字節流的長度,所以解決粘包的方法就是圍繞,如何讓發送端在發送數據前,把自己將要發送的字節流總大小讓接收端知曉,然后接收端發一個確認消息給發送端,然后發送端再發送過來后面的真實內容,接收端再來一個死循環接收完所有數據。

    看代碼示例:

server端代碼 復制代碼 import socket,subprocess ip_port=('127.0.0.1',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind(ip_port) s.listen(5)while True:conn,addr=s.accept()print('客戶端',addr)while True:msg=conn.recv(1024)if not msg:breakres=subprocess.Popen(msg.decode('utf-8'),shell=True,\stdin=subprocess.PIPE,\stderr=subprocess.PIPE,\stdout=subprocess.PIPE)err=res.stderr.read()if err:ret=errelse:ret=res.stdout.read()data_length=len(ret)conn.send(str(data_length).encode('utf-8'))data=conn.recv(1024).decode('utf-8')if data == 'recv_ready':conn.sendall(ret)conn.close() tcp_server端

      client端代碼示例

import socket,time s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',8080))while True:msg=input('>>: ').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8'))length=int(s.recv(1024).decode('utf-8'))s.send('recv_ready'.encode('utf-8'))send_size=0recv_size=0data=b''while recv_size < length:data+=s.recv(1024)recv_size+=len(data)print(data.decode('utf-8')) tcp_client

解決方案(二):

通過struck模塊將需要發送的內容的長度進行打包,打包成一個4字節長度的數據發送到對端,對端只要取出前4個字節,然后對這四個字節的數據進行解包,拿到你要發送的內容的長度,然后通過這個長度來繼續接收我們實際要發送的內容。

關于struck的介紹: 了解c語言的人,一定會知道struct結構體在c語言中的作用,不了解C語言的同學也沒關系,不影響,其實它就是定義了一種結構,里面包含不同類型的數據(int,char,bool等等),方便對某一結構對象進行處理。而在網絡通信當中,大多傳遞的數據是以二進制流(binary data)存在的。當傳遞字符串時,不必擔心太多的問題,而當傳遞諸如int、char之類的基本數據的時候,就需要有一種機制將某些特定的結構體類型打包成二進制流的字符串然后再網絡傳輸,而接收端也應該可以通過某種機制進行解包還原出原始的結構體數據。python中的struct模塊就提供了這樣的機制,該模塊的主要作用就是對python基本類型值與用python字符串格式表示的C struct類型間的轉化(This module performs conversions between Python values and C structs represented as Python strings.)。 struck模塊的使用:struct模塊中最重要的兩個函數是pack()打包, unpack()解包。

    pack():#我在這里只介紹一下'i'這個int類型,上面的圖中列舉除了可以打包的所有的數據類型,并且struck除了pack和uppack兩個方法之外還有好多別的方法和用法,大家以后找時間可以去研究一下,這里我就不做介紹啦,網上的教程很多

import struct a=12 # 將a變為二進制 bytes=struct.pack('i',a)

pack方法圖解:

    unpack():

# 注意,unpack返回的是tuple !!a,=struct.unpack('i',bytes)
struck解決粘包問題
先看一段偽代碼: import json,struct #假設通過客戶端上傳1T:1073741824000的文件a.txt#為避免粘包,必須自定制報頭 header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T數據,文件路徑和md5值#為了該報頭能傳送,需要序列化并且轉為bytes,因為bytes只能將字符串類型的數據轉換為bytes類型的,所有需要先序列化一下這個字典,字典不能直接轉化為bytes head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并轉成bytes,用于傳輸#為了讓客戶端知道報頭的長度,用struck將報頭長度這個數字轉成固定長度:4個字節 head_len_bytes=struct.pack('i',len(head_bytes)) #這4個字節里只包含了一個數字,該數字是報頭的長度#客戶端開始發送 conn.send(head_len_bytes) #先發報頭的長度,4個bytes conn.send(head_bytes) #再發報頭的字節格式 conn.sendall(文件內容) #然后發真實內容的字節格式#服務端開始接收 head_len_bytes=s.recv(4) #先收報頭4個bytes,得到報頭長度的字節格式 x=struct.unpack('i',head_len_bytes)[0] #提取報頭的長度 head_bytes=s.recv(x) #按照報頭長度x,收取報頭的bytes格式 header=json.loads(json.dumps(header)) #提取報頭#最后根據報頭的內容提取真實的數據,比如 real_data_len=s.recv(header['file_size']) s.recv(real_data_len) 偽代碼

正式代碼

server端代碼示例:報頭:就是消息的頭部信息,我們要發送的真實內容為報頭后面的內容。

import socket,struct,json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #忘了這是干什么的了吧,地址重用?想起來了嗎~ phone.bind(('127.0.0.1',8080)) phone.listen(5) while True:conn,addr=phone.accept()while True:cmd=conn.recv(1024)if not cmd:breakprint('cmd: %s' %cmd)res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)err=res.stderr.read()if err:back_msg=errelse:back_msg=res.stdout.read()conn.send(struct.pack('i',len(back_msg))) #先發back_msg的長度conn.sendall(back_msg) #在發真實的內容#其實就是連續的將長度和內容一起發出去,那么整個內容的前4個字節就是我們打包的后面內容的長度,對吧 conn.close() server端

client端

import socket,time,struct s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',8080)) while True:msg=input('>>: ').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8')) #發送給一個指令l=s.recv(4) #先接收4個字節的數據,因為我們將要發送過來的內容打包成了4個字節,所以先取出4個字節x=struct.unpack('i',l)[0] #解包,是一個元祖,第一個元素就是我們的內容的長度print(type(x),x)# print(struct.unpack('I',l))r_s=0data=b''while r_s < x: #根據內容的長度來繼續接收4個字節后面的內容。r_d=s.recv(1024)data+=r_dr_s+=len(r_d)# print(data.decode('utf-8'))print(data.decode('gbk')) #windows默認gbk編碼 自定制報頭

精進代碼示例:

server端

import socket,struct,json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)phone.bind(('127.0.0.1',8080)) phone.listen(5)while True:conn,addr=phone.accept()while True:cmd=conn.recv(1024)if not cmd:breakprint('cmd: %s' %cmd)res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)err=res.stderr.read()print(err)if err:back_msg=errelse:back_msg=res.stdout.read()headers={'data_size':len(back_msg)}head_json=json.dumps(headers)head_json_bytes=bytes(head_json,encoding='utf-8')conn.send(struct.pack('i',len(head_json_bytes))) #先發報頭的長度conn.send(head_json_bytes) #再發報頭conn.sendall(back_msg) #在發真實的內容 conn.close() server端

client端:

from socket import * import struct,jsonip_port=('127.0.0.1',8080) client=socket(AF_INET,SOCK_STREAM) client.connect(ip_port)while True:cmd=input('>>: ')if not cmd:continueclient.send(bytes(cmd,encoding='utf-8'))head=client.recv(4)head_json_len=struct.unpack('i',head)[0]head_json=json.loads(client.recv(head_json_len).decode('utf-8'))data_len=head_json['data_size']recv_size=0recv_data=b''while recv_size < data_len:recv_data+=client.recv(1024)recv_size+=len(recv_data)#print(recv_data.decode('utf-8'))print(recv_data.decode('gbk')) #windows默認gbk編碼 client端 整個流程的大致解釋:
我們可以把報頭做成字典,字典里包含將要發送的真實數據的描述信息(大小啊之類的),然后json序列化,然后用struck將序列化后的數據長度打包成4個字節。 我們在網絡上傳輸的所有數據 都叫做數據包,數據包里的所有數據都叫做報文,報文里面不止有你的數據,還有ip地址、mac地址、端口號等等,其實所有的報文都有報頭,這個報頭是協議規定的,看一下
發送時: 先發報頭長度 再編碼報頭內容然后發送 最后發真實內容接收時: 先手報頭長度,用struct取出來 根據取出的長度收取報頭內容,然后解碼,反序列化 從反序列化的結果中取出待取數據的描述信息,然后去取真實的數據內容? FTB上傳下載文件的代碼(簡易版) import socket import struct import json sk = socket.socket() # buffer = 4096 # 當雙方的這個接收發送的大小比較大的時候,就像這個4096,就會丟數據,這個等我查一下再告訴大家,改小了就ok的,在linux上也是ok的。 buffer = 1024 #每次接收數據的大小 sk.bind(('127.0.0.1',8090)) sk.listen()conn,addr = sk.accept() #接收 head_len = conn.recv(4) head_len = struct.unpack('i',head_len)[0] #解包 json_head = conn.recv(head_len).decode('utf-8') #反序列化 head = json.loads(json_head) filesize = head['filesize'] with open(head['filename'],'wb') as f:while filesize:if filesize >= buffer: #>=是因為如果剛好等于的情況出現也是可以的。content = conn.recv(buffer)f.write(content)filesize -= bufferelse:content = conn.recv(buffer)f.write(content)breakconn.close() sk.close() tpc_server_ftb import os import json import socket import struct sk = socket.socket() sk.connect(('127.0.0.1',8090)) buffer = 1024 #讀取文件的時候,每次讀取的大小 head = {'filepath':r'D:\打包程序', #需要下載的文件路徑,也就是文件所在的文件夾'filename':'xxx.mp4', #改成上面filepath下的一個文件'filesize':None,}file_path = os.path.join(head['filepath'],head['filename']) filesize = os.path.getsize(file_path) head['filesize'] = filesize # json_head = json.dumps(head,ensure_ascii=False) #字典轉換成字符串 json_head = json.dumps(head) #字典轉換成字符串 bytes_head = json_head.encode('utf-8') #字符串轉換成bytes類型 print(json_head) print(bytes_head)#計算head的長度,因為接收端先接收我們自己定制的報頭,對吧 head_len = len(bytes_head) #報頭長度 pack_len = struct.pack('i',head_len) print(head_len) print(pack_len) sk.send(pack_len) #先發送報頭長度 sk.send(bytes_head) #再發送bytes類型的報頭#即便是視頻文件,也是可以按行來讀取的,也可以readline,也可以for循環,但是讀取出來的數據大小就不固定了,影響效率,有可能讀的比較小,也可能很大,像視頻文件一般都是一行的二進制字節流。 #所有我們可以用read,設定一個一次讀取內容的大小,一邊讀一邊發,一邊收一邊寫 with open(file_path,'rb') as f:while filesize:if filesize >= buffer: #>=是因為如果剛好等于的情況出現也是可以的。content = f.read(buffer) #每次讀取出來的內容 sk.send(content)filesize -= buffer #每次減去讀取的大小else: #那么說明剩余的不夠一次讀取的大小了,那么只要把剩下的讀取出來發送過去就行了content = f.read(filesize)sk.send(content)breaksk.close() tpc_client_ftb

三、驗證客戶端的鏈接合法性

首先,我們來探討一下,什么叫驗證合法性,?舉個例子:有一天,我開了一個socket服務端,只想讓咱們這個班的同學使用,但是有一天,隔壁班的同學過來問了一下我開的這個服務端的ip和端口,然后他是不是就可以去連接我了啊,那怎么辦,我是不是不想讓他連接我啊,我需要驗證一下你的身份,這就是驗證連接的合法性,再舉個例子,就像我們上面說的你的windows系統是不是連接微軟的時間服務器來獲取時間的啊,你的mac能到人家微軟去獲取時間嗎,你愿意,人家微軟還不愿意呢,對吧,那這時候,你每次連接我來獲取時間的時候,我是不是就要驗證你的身份啊,也就是你要帶著你的系統信息,我要判斷你是不是我微軟的windows,對吧,如果是mac,我是不是不讓你連啊,這就是連接合法性。如果驗證你的連接是合法的,那么如果我還要對你的身份進行驗證的需求,也就是要驗證用戶名和密碼,那么我們還需要進行身份認證。連接認證>>身份認證>>ok你可以玩了。

如果你想在分布式系統中實現一個簡單的客戶端鏈接認證功能,又不像SSL那么復雜,那么利用hmac+加鹽的方式來實現,直接看代碼!(SSL,我們都)

from socket import * import hmac,ossecret_key=b'Jedan has a big key!' def conn_auth(conn):'''認證客戶端鏈接:param conn::return:'''print('開始驗證新鏈接的合法性')msg=os.urandom(32)#生成一個32字節的隨機字符串 conn.sendall(msg)h=hmac.new(secret_key,msg) digest=h.digest()respone=conn.recv(len(digest))return hmac.compare_digest(respone,digest)def data_handler(conn,bufsize=1024):if not conn_auth(conn):print('該鏈接不合法,關閉')conn.close()returnprint('鏈接合法,開始通信')while True:data=conn.recv(bufsize)if not data:breakconn.sendall(data.upper())def server_handler(ip_port,bufsize,backlog=5):'''只處理鏈接:param ip_port::return:'''tcp_socket_server=socket(AF_INET,SOCK_STREAM)tcp_socket_server.bind(ip_port)tcp_socket_server.listen(backlog)while True:conn,addr=tcp_socket_server.accept()print('新連接[%s:%s]' %(addr[0],addr[1]))data_handler(conn,bufsize)if __name__ == '__main__':ip_port=('127.0.0.1',9999)bufsize=1024server_handler(ip_port,bufsize) 服務端 from socket import * import hmac,ossecret_key=b'Jedan has a big key!' def conn_auth(conn):'''驗證客戶端到服務器的鏈接:param conn::return:'''msg=conn.recv(32)h=hmac.new(secret_key,msg)digest=h.digest()conn.sendall(digest)def client_handler(ip_port,bufsize=1024):tcp_socket_client=socket(AF_INET,SOCK_STREAM)tcp_socket_client.connect(ip_port)conn_auth(tcp_socket_client)while True:data=input('>>: ').strip()if not data:continueif data == 'quit':breaktcp_socket_client.sendall(data.encode('utf-8'))respone=tcp_socket_client.recv(bufsize)print(respone.decode('utf-8'))tcp_socket_client.close()if __name__ == '__main__':ip_port=('127.0.0.1',9999)bufsize=1024client_handler(ip_port,bufsize) 客戶端

介紹代碼中使用的兩個方法:

1、os.urandom(n) 其中os.urandom(n) 是一種bytes類型的隨機生成n個字節字符串的方法,而且每次生成的值都不相同。再加上md5等加密的處理,就能夠成內容不同長度相同的字符串了。 os.urandom(n)函數在python官方文檔中做出了這樣的解釋函數定位: Return a string of n random bytes suitable for cryptographic use. 意思就是,返回一個有n個byte那么長的一個string,然后很適合用于加密。然后這個函數,在文檔中,被歸結于os這個庫的Miscellaneous Functions,意思是不同種類的函數(也可以說是混種函數) 原因是: This function returns random bytes from an OS-specific randomness source. (函數返回的隨機字節是根據不同的操作系統特定的隨機函數資源。即,這個函數是調用OS內部自帶的隨機函數的。有特異性)

?    使用方法:

import os from hashlib import md5for i in range(10):print md5(os.urandom(24)).hexdigest() 2、hmac: 我們完全可以用hashlib來實現,但是學個新的嗎,沒什么不好的,這個操作更方便一些。 Python自帶的hmac模塊實現了標準的Hmac算法,我們首先需要準備待計算的原始消息message,隨機key,哈希算法,這里采用MD5,使用hmac的代碼如下: import hmac message = b'Hello world' key = b'secret' h = hmac.new(key,message,digestmod='MD5') print(h.hexdigest())
比較兩個密文是否相同,可以用hmac.compare_digest(密文、密文),然會True或者False。

?    可見使用hmac和普通hash算法非常類似。hmac輸出的長度和原始哈希算法的長度一致。需要注意傳入的key和message都是bytes類型,str類型需要首先編碼為bytes。

def hmac_md5(key, s):return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'MD5').hexdigest()class User(object):def __init__(self, username, password):self.username = usernameself.key = ''.join([chr(random.randint(48, 122)) for i in range(20)])self.password = hmac_md5(self.key, password) OVER







轉載于:https://www.cnblogs.com/z520h123/p/10008200.html

總結

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

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