粘包这个鬼东西

忽略底层的协议,编程语言暴露给我们的接口都是socket,通过socket我们可以进行设备之间的信息传输

简单的客户端代码实现如下

import socket

host = 'localhost'
port = 10086
address = host,port

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
code = client.connect_ex(address)

if code == 0:
    send_message = bytes(input('input your message'),encoding='utf-8')
    client.send(send_message)
    recv_data = str(client.recv(1024),encoding='utf-8')
    print(recv_data)
    client.close()

对应的客户端代码

import socket

host = 'localhost'
port = 10086
address = host,port

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(address)
server.listen()

conn,addr = server.accept()
recv_message = conn.reccv(1024)

print(str(recv_message),encoding='utf-8')

conn.send(recv_message)

conn.close()

这只是设备进行一次性间简单信息传输的代码实现,如果想要多次的传输可以加上循环

不过其中有两点值得注意的事情

1. 在传输过程中,数据的类型必须为字节码,也就是说传输的信息必须转换成字节类型

2. 打印的时候,需要的是字符串类型,字节类型打印会出现错误

所以一般进行传输之前,要注意信息的转码操作,完成传输后,也要根据对应的操作进行转码

关于字符类型和字节类型,这里有两个比较好的转换方式

1. 字节类型转换字符类型

str(b'byte',encoding='utf-8')
直接强转成字符类型,同时可以进行编码的指定

2. 字符类型转换字节类型

'string'.encode()

bytes('string',encoding='utf-8')
字符类型转换成字节类型有两种方法

1. 直接进行编码,默认就是转换成字节类型数据
2. 强转成字节类型,也可以指定编码格式


接下来开始进入正题,什么叫做粘包

粘包:在多次进行信息传输过程中,后续定义传输的数据会在前面的传输操作中和之前的传输信息一起进行传输

也就是说,我们进行数据传输的时候,数据的大小可能不像我们定义的那么严格

前面传输的数据的可能会变多,因为后续的数据会‘粘’过来

这对于一些精细的操作来说是致命性的,同时也会破坏我们代码的严谨逻辑


为什么会产生这种现象呢,具体的因素有很多,如果只能说一个的话,可以归咎于缓冲区这个东西

在我们进行文件传输的时候,一般都是先经过缓冲区的,然后刷出去,就开始了传输

所以就会出现两种情况

1. 数据太小,浪费空间

数据太小,但是缓冲区是占据一定空间的,这就会造成资源的浪费,所以为了节省空间资源,后面的数据就会先来填充这空闲的区间

这个是协议本身就具有的特性,也是为了提升性能的设定,不过有时候真的算是好心办坏事

2. 数据太大,缓冲溢出

当数据量过于庞大,超过了缓冲区的容量,socket就会进行分包传输操作。

填满一个缓冲区以后,开始传输这一个包,然后清空缓冲区开始传输接下来的数据


就是因为这两种机制,我们不得不进行两个操作

1. 重复接收

为了应对数据量比较大的传输,虽然发送端代码表明进行的只是一次发送操作,但是底层会进行多次的分包传输

同时,接收端接收的数据,只是一个缓冲区的大小,接收的只是总信息量的一部分

所以进行大量数据传输时,我们在接收端必须进行多次的接收操作,然后整合每一次接收的数据,才能获得完整的数据

2. 避免粘包

作为缓冲区,其实时有一个刷的操作的,我们可以自定义的刷出数据,不过这种操作并不太严谨,效率也不高,很少使用

常用的方法,就是进行缓冲区功能的切换,也就是收发功能的转换,因为转换功能时,缓冲区必定会进行清空操作

而且,为了确定重复接收操作的准确度,我们也的确应该知道数据量的总大小,才能够具体的决定重复接收的次数

所以,进行大信息量传输操作的时候,发送端会先发送一个数据量大小的信息

接收端也对应的发送一个信息,不仅是进行功能的切换,避免粘包,还可以作为确认信息,决定后续的操作,两全齐美


我们利用命令行执行命令返回结果进行测试

命令输入端

import socket

host = 'localhost'
port = 10086
address = host, port

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
code = client.connect_ex(address)

if code == 0 :
    while True:
        cmd = bytes(input('cmd:\t'),'utf-8')
        client.send(cmd)
        result_length = int(client.recv(1024))
        client.send(b'ok')
        recv_length = 0
        data = ''
        while recv_length < result_length:
            recv_data = client.recv(1024)
            recv_length += len(recv_data)
            data += str(recv_data,encoding='utf-8')
        print(data)

命令执行端

import socket
import os

host = 'localhost'
port = 10086
address = host,port

sever = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sever.bind(address)
sever.listen()

while True:
    conn,addr = sever.accept()
    while conn:
        cmd = str(conn.recv(1024),encoding='utf-8')
        exec_result = bytes(os.popen(cmd).read(),encoding='utf-8')
        data_length = bytes(str(len(exec_result)),encoding='utf-8')
        conn.send(data_length)
        if conn.recv(1024):
            conn.send(exec_result)


这样就解决了粘包的问题


为了试验方便,代码中并没有严谨的进行socket文件流的关闭和异常的捕获操作

垃圾代码,见谅!见谅!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值