Python3基础-黏包问题

首先是socket数据传送和数据接收的原理:

 

粘包只会出现在TCP里

UDP在windows下会提示:

  OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小

出现粘包的情况:

1、发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小,会合到一起,产生粘包)

#服务端:
from socket import *
import  struct

ip_port = ('127.0.0.1', 8898)
BUFSIZE=1024

tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)

tcp_client.send('hello'.encode('utf-8'))
tcp_client.send('world'.encode('utf-8'))
tcp_client.send('susu'.encode('utf-8'))

tcp_client.close()


#客户端
from socket import *
import subprocess
import struct

back_log = 5
buffer_size =1024
ip_post = ('127.0.0.1',8898)

sock = socket(AF_INET,SOCK_STREAM)
sock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #加入一条socket配置,重用ip和端口

sock.bind(ip_post)
sock.listen(back_log)

conn,addr = sock.accept()
data1 = conn.recv(buffer_size)
print('data1', data1)

data2 = conn.recv(buffer_size)
print('data1', data2)

data3 = conn.recv(buffer_size)
print('data1', data3)

conn.close()
sock.close()


#server执行结果如下
data1 b'helloworldsusu'
data1 b''
data1 b''
黏包1

2、接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

#服务端
from socket import *
import subprocess
import struct

back_log = 5
buffer_size =1024
ip_post = ('127.0.0.1',8898)

sock = socket(AF_INET,SOCK_STREAM)
sock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #加入一条socket配置,重用ip和端口

sock.bind(ip_post)
sock.listen(back_log)

conn,addr = sock.accept()
data1 = conn.recv(1)
print('data1', data1)

data2 = conn.recv(5)
print('data2', data2)

data3 = conn.recv(4)
print('data3', data3)


conn.close()
sock.close()

#客户端
from socket import *
import  struct

ip_port = ('127.0.0.1', 8898)
BUFSIZE=1024

tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)

tcp_client.send('hellowORDddddddd'.encode('utf-8'))

tcp_client.close()

#server执行结果如下
data1 b'h'
data2 b'ellow'
data3 b'ORDd'
黏包2

解决黏包的方法

服务端

from socket import *
import subprocess
import struct

back_log = 5
buffer_size =1024
ip_post = ('127.0.0.1',8898)

sock = socket(AF_INET,SOCK_STREAM)
sock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #加入一条socket配置,重用ip和端口

sock.bind(ip_post)
sock.listen(back_log)
while True:
    conn,addr = sock.accept()
    while True:
        try:
            data = conn.recv(buffer_size)
            if not data:break
            data = data.decode('utf-8')
            print('服务端收到客户端发来的消息:',data)
            res = subprocess.Popen(data,shell=True, #第一个参数:命令字符串  第二个参数指定由shell处理
                                   stderr=subprocess.PIPE, #将基本的输入、输出以及错误都放入管道
                                   stdin=subprocess.PIPE, #这些在管道里的信息都是字节形式,编码为utf-8
                                   stdout=subprocess.PIPE
                                   )
            err =res.stderr.read() #定义一个err变量接收基本的错误信息
            if err: #如果错误信息不为空
                cmd_res = err #输出的结果为基本的错误信息
            else:
                cmd_res = res.stdout.read() #输出的结果为基本的输出信息

            if not cmd_res: # 有些命令无返回结果,需要进行判断
                cmd_res = '该命令没有返回结果'.encode('gbk')

            #方法1
            # std_size=len(cmd_res) #计算出总长度
            # conn.send(str(std_size).encode('gbk')) #将总长度发给客户端,客户端收到该消息返回一个状态
            # status=conn.recv(buffer_size).decode('utf-8') #将返回的状态赋值
            # if status: #如果该状态成立,那么开始发送所有数据
            #     conn.send(cmd_res) #像客户端发送执行结果

            #方法2
            # std_size=len(cmd_res) #计算出总长度
            # conn.send(str(std_size).encode('gbk'))
            # status = conn.recv(buffer_size).decode('utf-8')  # 将返回的状态赋值
            # if status:
            #     conn.send(cmd_res)

            #方法3  减少网络传输
            std_size = len(cmd_res)
            data_length =struct.pack('i',std_size)#把数字封装在固定一个长度里面

            conn.send(data_length)
            conn.send(cmd_res)



        except Exception:
            break
    conn.close()
sock.close()

 

客户端

from socket import *
import  struct
from  functools import  partial

ip_port = ('127.0.0.1', 8898)
buffer_size =1024

tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
    cmd = input('请输入命令>>>').strip()
    if not cmd: continue
    if cmd == 'quit': break
    tcp_client.send(cmd.encode('utf-8'))

    #方法1
    # std_size = int(tcp_client.recv(BUFSIZE).decode('gbk')) #将接收的数据总长度转换成数字
    # tcp_client.send('TRUE'.encode('utf-8'))  #返回给服务器端一个状态TRUE
    # cmd_res = b''
    # get_size=0
    # while get_size < std_size:
    #     if (std_size-get_size) < BUFSIZE: ##如果总长度比下载的长度小于定义的100,那么就取数据的最小值,否则按100取值
    #         cmd_res += tcp_client.recv(std_size-get_size)
    #     else:
    #         cmd_res += tcp_client.recv(BUFSIZE)
    #     get_size +=BUFSIZE  #每取一次值加100,最后一次的值肯定大于总长度

    #方法2  1024byte*8  =
    # std_size = int(tcp_client.recv(buffer_size).decode('gbk'))  # 将接收的数据总长度转换成数字
    # print("std_size:",std_size)
    # tcp_client.send('TRUE'.encode('utf-8'))  # 返回给服务器端一个状态TRUE
    #
    # recv_size=0
    # recv_msg=b''
    # while recv_size < std_size:
    #     recv_msg +=tcp_client.recv(buffer_size)
    #     recv_size = len(recv_msg)
    #     print("recv_size:",recv_size)

    #方法3
    length_data=tcp_client.recv(4) #收4个字节 包含收到的总长度
    length =struct.unpack('i',length_data)[0]
    #print(length)

    recv_size = 0
    recv_msg=b''
    while recv_size < length:
        recv_msg +=tcp_client.recv(buffer_size)
        recv_size = len(recv_msg) #1024

    print('命令执行的结果是', recv_msg.decode('gbk'))

tcp_client.close()

 

posted @ 2020-04-26 16:22  槑槑DE  阅读(570)  评论(0)    收藏  举报