網絡編程 之粘包問題、使用socketserver實現並發
阿新 • • 發佈:2018-09-15
簡述 3.1 roc json 發送 pip 圖片 RoCE read
一、粘包問題
註意:粘包問題只有tcp協議並且udp協議永遠不會粘包
粘包問題的產生:
簡述:粘包問題的產生主要是由於tcp協議傳輸數據(其內置的nagle算法來進行的)會將數據較小的且發送時間較短的合並成一個包從發送端發送出去,接收端不知道該怎麽去想要的數據拿出來這樣造成了粘包問題,另一方面是由於時間太短接收端沒有及時拿幹凈
傳來的數據造成數據混亂(這是因為tcp協議又叫流氏協議指的是其就像水流一樣傳輸數據)才產生的粘包問題。
1、發送端由於時間太短造成多個包合在一起發送產生粘包問題的實例如下:
服務端:
from socket import * ip_port=(‘127.0.0.1‘,8080) tcp_socket_serverView Code=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()
客戶端:
import socket BUFSIZEView Code=1024 ip_port=(‘127.0.0.1‘,8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send(‘hello‘.encode(‘utf-8‘)) s.send(‘feng‘.encode(‘utf-8‘))
2、由於接收端沒有接收幹凈發送端發來的數據造成的粘包問題的實例如下:
服務端:
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(View Code5) conn,addr=tcp_socket_server.accept() data1=conn.recv(2) #一次沒有收完整 data2=conn.recv(10)#下次收的時候,會先取舊的數據,然後取新的 print(‘----->‘,data1.decode(‘utf-8‘)) print(‘----->‘,data2.decode(‘utf-8‘)) conn.close()
客戶端:
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) s.send(‘hello feng‘.encode(‘utf-8‘))View Code
3、粘包現象出現的實例如下:
3.1基於subprocess模塊產生的粘包現象
服務端:
from socket import * import subprocess ip_port=(‘127.0.0.1‘,8080) BUFSIZE=1024 tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) while True: conn,addr=tcp_socket_server.accept() print(‘客戶端‘,addr) while True: cmd=conn.recv(BUFSIZE) if len(cmd) == 0:break res=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) stderr=act_res.stderr.read() stdout=act_res.stdout.read() conn.send(stderr) conn.send(stdout)View Code
客戶端:
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) while True: msg=input(‘>>: ‘).strip() if len(msg) == 0:continue if msg == ‘quit‘:break s.send(msg.encode(‘utf-8‘)) act_res=s.recv(BUFSIZE) print(act_res.decode(‘utf-8‘),end=‘‘)View Code
3.2使用struct模塊的解決方案
服務端:
import socket,json,struct,subprocess ip_port=(‘127.0.0.1‘,2206) ip_base=1024 server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(ip_port) server.listen(5) while True: conn,adder=server.accept() while True: try: data=conn.recv(ip_base) if len(data)==0: break res=subprocess.Popen(data.decode(‘utf-8‘),shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout=res.stdout.read() stderr=res.stderr.read() head_dic={‘filname‘:‘dir‘,‘md5‘:‘fffff‘,‘head‘:len(stdout)+len(stderr)} head_json=json.dumps(head_dic) head_bytes=head_json.encode(‘utf-8‘) conn.send(struct.pack(‘i‘,len(head_bytes))) conn.send(head_bytes) conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close() server.colse()View Code
客戶端:
import socket,json,struct ip_port=(‘127.0.0.1‘,2206) client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(ip_port) while True: mag=input(‘>>>:‘).strip() if len(mag)==0: continue client.send(mag.encode(‘utf-8‘)) #先拿到報頭的固定長度(4bytes) head_len=struct.unpack(‘i‘,client.recv(4))[0] #再拿到報頭 head_bytes=client.recv(head_len) head_json=head_bytes.decode(‘utf-8‘) head_dic=json.loads(head_json) #拿到報頭的中數據的長度 total=head_dic[‘head‘] rb=b‘‘ stawith=0 while stawith<total: data=client.recv(total) stawith+=len(data) rb+=data print(rb.decode(‘gbk‘)) client.close()View Code
補充:為何udp協議不會發生粘包問題是由於udp協議不是通過連接進行數據傳輸的並且基於udp協議發送的數據都會自帶報頭,
所以接收端可以通過每條數據的報頭去取出數據並且udp協議傳輸數據是傳一個數據就立馬刪除掉這樣不會讓接收端由於接受不
急時而造成數據混亂。(udp協議也可以叫數據報協議)
二、使用socketserver模塊實現並發
1、基於tcp協議通信實現並發
服務端:
import socketserver class Myudpheadler(socketserver.DatagramRequestHandler): def handle(self): while True: data,sock=self.request() sock.sendto(data,self.client_address) if __name__ == ‘__main__‘: sever=socketserver.ThreadingUDPServer((‘127.0.0.1‘,2221),Myudpheadler) sever.serve_forever()View Code
客戶端:
from socket import * ip_port=(‘127.0.0.1‘,2221) client=socket(AF_INET,SOCK_DGRAM) while True: mad=input(‘>>>>:‘).strip() if len(mad)==0: continue client.sendto(mad.encode(‘utf-8‘),ip_port) adder,sock=client.recvfrom(1024) print(adder) print(sock) client.close()View Code
2、基於udp協議通信實現並發
服務端:
import socketserver class mudphead(socketserver.DatagramRequestHandler): def handle(self): while True: data,sock=self.request() sock.sendto(data,self.client_address) if __name__ == ‘__main__‘: server=socketserver.ThreadingUDPServer((‘127.0.0.1‘,2220),mudphead) server.serve_forever()View Code
客戶端:
from socket import * ip_port=(‘127.0.0.1‘,2206) client=socket(AF_INET,SOCK_DGRAM) while True: client.sendto(‘hello‘.encode(‘utf-8‘),ip_port) adder,scok=client.recvfrom(1024) print(adder) client.close()View Code
補充:基於udp協議其自身就可以實現通信實現並發。
網絡編程 之粘包問題、使用socketserver實現並發