网络编程 之粘包问题使用socketserver实现并发
Posted ageliu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程 之粘包问题使用socketserver实现并发相关的知识,希望对你有一定的参考价值。
一、粘包问题
注意:粘包问题只有tcp协议并且udp协议永远不会粘包
粘包问题的产生:
简述:粘包问题的产生主要是由于tcp协议传输数据(其内置的nagle算法来进行的)会将数据较小的且发送时间较短的合并成一个包从发送端发送出去,接收端不知道该怎么去想要的数据拿出来这样造成了粘包问题,另一方面是由于时间太短接收端没有及时拿干净
传来的数据造成数据混乱(这是因为tcp协议又叫流氏协议指的是其就像水流一样传输数据)才产生的粘包问题。
1、发送端由于时间太短造成多个包合在一起发送产生粘包问题的实例如下:
服务端:
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()
客户端:
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‘.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(5) 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‘))
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)
客户端:
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=‘‘)
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()
客户端:
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()
补充:为何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()
客户端:
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()
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()
客户端:
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()
补充:基于udp协议其自身就可以实现通信实现并发。
以上是关于网络编程 之粘包问题使用socketserver实现并发的主要内容,如果未能解决你的问题,请参考以下文章