网络编程 之粘包问题使用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()
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)


s.send(hello.encode(utf-8))
s.send(feng.encode(utf-8))
View Code

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()
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)


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实现并发的主要内容,如果未能解决你的问题,请参考以下文章

网络编程之粘包

socket网络编程之粘包问题详解

Learning-Python29:网络编程之粘包

网络编程基础之粘包现象

网络编程之粘包

socket之粘包