python-socket编程(三)粘包

在看粘包之前我们先看一个实例

这个实例是在客户端输入指令在服务端执行并返回执行结果

其中subprocess就是将命令交予系统进行运行的模块

代码

  • 服务端
import socket
import subprocess

ip_port=('127.0.0.1',8001)
backlog=5
buffersize=1024

tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(backlog)

while True:
    conn,addr = tcp_server.accept()
    while True:
        cmd = conn.recv(buffersize).decode('utf-8')
        print(cmd)
        if cmd == 'exit':
            break
        res = subprocess.Popen(cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE,stdin=subprocess.PIPE)
#表示使用shell来解释语言,标准输入,标准输出和错误输入都输入到管道,而不是打印到屏幕上
        err = res.stderr.read()
        if err:
            cmd_res = err
        else:
            cmd_res = res.stdout.read()
        conn.send(cmd_res)
    conn.close()
  • 客户端
import socket

buffersize=1024
ip_port = ('127.0.0.1',8001)

tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_client.connect(('127.0.0.1',8001))
while True:
    msg = input('-->:')
    if not msg:continue
    tcp_client.send(msg.encode('utf-8'))
    if msg == 'exit':
        break
    res = tcp_client.recv(buffersize).decode('utf-8')
    print(res)
tcp_client.close()

当我们执行一个命令,它的返回值大于1024字节的时候,就会被截断,而当你执行下一个命令的时候,上次没有展示完的内容会继续输出到屏幕上,看起来就像两次的命令结果粘连到了一起,这就是粘包现象。

产生的原因

首先发送数据和接收数据不是一个实时的过程,发送数据都会首先发送至对方的内核态缓存区中,这个我们前面介绍过。发送方可以一次性发送2048字节的数据,而接收方可以一次收取1024字节数据,分两次接收,这是由buffersize决定的。也就是说应用程序对一段数据有多少字节,对它来说是不可见的,它只负责从流中取走一段,因此粘包现象就产生了。

两次发送消息的大小相加比如是2000字节(1300+700)形成一段流,第一次收取了1024没收取完;第二段收取了第一次消息的200加后一段的700。

而udp就不不同了,udp是面向数据报消息的边界都是标记好的,收和发都是以消息/次为单位,因此不会出现粘包的现象。

解决方法

基础解决的思路

产生粘包的主要原因就是tcp不知道每次消息的边界,因此只要我们每次发送消息时,提前先发送的消息的长度,然后根据消息的长度来接收消息。

  • 服务端
import socket
import subprocess

ip_port=('127.0.0.1',8001)
backlog=5
buffersize=1024

tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(backlog)

while True:
    conn,addr = tcp_server.accept()
    while True:
        cmd = conn.recv(buffersize).decode('utf-8')
        print(cmd)
        if cmd == 'exit':
            break
        res = subprocess.Popen(cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE,stdin=subprocess.PIPE)
        err = res.stderr.read()
        if err:
            cmd_res = err
        else:
            cmd_res = res.stdout.read()
        ##解决粘包
        length = len(cmd_res)
        conn.send(length)
        client_ready = conn.recv(buffer_size)
        if client_ready == b'ready':
        	conn.send(cmd_res)
    conn.close()
  • 客户端
import socket

buffersize=1024
ip_port = ('127.0.0.1',8001)

tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_client.connect(('127.0.0.1',8001))
while True:
    msg = input('-->:')
    if not msg:continue
    tcp_client.send(msg.encode('utf-8'))
    if msg == 'exit':
        break
    ###解决粘包
    length = tcp_client.recv(buffersize)
    ###由于命令长度和命令内容发送间隔近,防止粘连,客户端发送‘ready’字样
    tcp_client.send(b'ready')
    #size统计接收的长度,msg是接收内容的变量,这是收大长度的内容的方法
    recv_size = 0
    recv_msg = b''
    while recv_size < length:
    	recv_msg += tcp_client.recv(buffer_size)
    	recv_size = len(recv_msg)
    res = recv_msg.decode('utf-8')
    print(res)
tcp_client.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值