python学习_day30_基于tcp协议的粘包现象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python学习_day30_基于tcp协议的粘包现象相关的知识,希望对你有一定的参考价值。

1、基于远程执行命令的程序

  需用到subprocess模块

服务端:

#1、执行客户端发送的指令
import socket
import subprocess
phone=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 socket
phone=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 socket
import subprocess
import struct
phone=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 socket
import struct
phone=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 socket
import subprocess
import struct
import json

phone=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 socket
import struct
import json
phone=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协议的粘包现象的主要内容,如果未能解决你的问题,请参考以下文章

解决TCP协议的粘包问题

[网络知识]TCP协议中的粘包与拆包

python 基于tcp协议的文件传输3_解决粘包问题

处理tcp里的粘包问题

python入行037(粘包问题)

TCP协议下的粘包问题