python學習_day30_基於tcp協議的粘包現象

來源:互聯網
上載者:User

標籤:pytho   day   base   pre   res   注意   內容   整數   import   

1、基於遠程執行命令的程式

  需用到subprocess模組

服務端:

#1、執行用戶端發送的指令import socketimport subprocessphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind((‘127.0.0.1‘,8090))phone.listen(5)while True:    conn,addr=phone.accept()    print(‘IP:%s PORT:%s‘ %(addr[0],addr[1]))    while True:        try:            cmd=conn.recv(1024)            if not cmd:break            #執行命令            obj=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True,                                 stdout=subprocess.PIPE,                                 stderr=subprocess.PIPE                                 )            stdout=obj.stdout.read()            stderr=obj.stderr.read()            conn.send(stdout+stderr)        except Exception:            break    conn.close()phone.close()

用戶端:

import socketphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.connect((‘127.0.0.1‘,8090))while True:    cmd=input(‘>>>‘).strip()    if not cmd:continue    phone.send(cmd.encode(‘utf-8‘))    res=phone.recv(1024)    print(res.decode(‘gbk‘))phone.close()

注意注意注意:

res=subprocess.Popen(cmd.decode(‘utf-8‘),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)

的結果的編碼是以當前所在的系統為準的,如果是windows,那麼res.stdout.read()讀出的就是GBK編碼的,在接收端需要用GBK解碼,且只能從管道裡讀一次結果。

2、粘包現象

  只有TCP有粘包現象,TCP協議是面向流的協議,這也是容易出現粘包問題的原因。例如基於tcp的通訊端用戶端往服務端上傳檔案,發送時檔案內容是按照一段一段的位元組流發送的,在接收方看了,根本不知道該檔案的位元組流從何處開始,在何處結束。所謂粘包問題主要還是因為接收方不知道訊息之間的界限,不知道一次性提取多少位元組的資料所造成的。

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

兩種情況下會發生粘包。

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

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

解決辦法1:

服務端:

import socketimport subprocessimport structphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind((‘127.0.0.1‘,8090))phone.listen(5)print(‘starting...‘)while True:    conn,addr=phone.accept()    print(‘IP:%s,PORT:%s‘%(addr[0],addr[1]))    while True:        try:            cmd=conn.recv(1024)            #執行命令cmd            obj=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True,                                 stdout=subprocess.PIPE,                                 stderr=subprocess.PIPE)            stdout=obj.stdout.read()            stderr=obj.stderr.read()            #發送真實資料的描述資訊:長度            header=struct.pack(‘i‘,len(stdout)+len(stderr))  #i模式為針對整數型資料,下輸出的結果為4            conn.send(header)           # 發送真實資料            conn.send(stdout)            conn.send(stderr)        except Exception:            break    conn.close()phone.close()

用戶端:

import socketimport structphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.connect((‘127.0.0.1‘,8090))while True:    cmd=input(‘>>>‘).strip()    phone.send(cmd.encode(‘utf-8‘))    header=phone.recv(4) #指定接收4個位元組    total_size=struct.unpack(‘i‘,header)[0]#對接收的4個位元組資料進行解包得到待接收資料大小的元組:(資料大小,)    #迴圈接收資料    total_data=b‘‘    recv_size=0    while recv_size<total_size:        recv_data=phone.recv(1024)        total_data+=recv_data        recv_size+=len(recv_data)    print(total_data.decode(‘gbk‘))phone.close()

  由於模組struct的pack方法中使用的i類型存在整數無限大時會出現報錯的弊端,故提出如下解決方案:

解決辦法2:

服務端:

import socketimport subprocessimport structimport jsonphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind((‘127.0.0.1‘,8090))phone.listen(5)print(‘starting...‘)while True:    conn,addr=phone.accept()    print(‘IP:%s,PORT:%s‘%(addr[0],addr[1]))    while True:        try:            cmd=conn.recv(1024)            if not cmd:break            obj=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True,                                 stdout=subprocess.PIPE,                                 stderr=subprocess.PIPE)            stdout=obj.stdout.read()            stderr=obj.stderr.read()            #製作前序            header_dic={‘filename‘: ‘a.txt‘,                          ‘total_size‘: len(stdout)+len(stderr),                          ‘md5‘: ‘asdfa123xvc123‘}            header_json=json.dumps(header_dic)            header_bytes=header_json.encode(‘utf-8‘)            #發送前序長度            conn.send(struct.pack(‘i‘,len(header_bytes)))            #發送前序            conn.send(header_bytes)            #發送資料            conn.send(stdout)            conn.send(stderr)        except Exception:            break    conn.close()phone.close()

用戶端:

import socketimport structimport jsonphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.connect((‘127.0.0.1‘,8090))while True:    cmd=input(‘>>>‘).strip()    if not cmd:continue    phone.send(cmd.encode(‘utf-8‘))    # 接收前序內容長度    obj=phone.recv(4)    header_size=struct.unpack(‘i‘,obj)[0]    #接收前序字典    header_bytes=phone.recv(header_size)    header_json=header_bytes.decode(‘utf-8‘)    header_dic=json.loads(header_json)    total_size=header_dic[‘total_size‘]    total_data=b‘‘    recv_size=0    while recv_size<total_size:        recv_data=phone.recv(1024)        total_data+=recv_data        recv_size+=len(recv_data)    print(total_data.decode(‘gbk‘))phone.close()

 

python學習_day30_基於tcp協議的粘包現象

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.