生活不易 我用pythonPython自学与资料分享程序员

Python3.6:socket数据传输解决粘包问题valuee

2018-02-12  本文已影响6人  james_chang

在上一篇socket实现简单ssh客户端中,有的时候会出现这个错误


这是因为长度和字符串一起发送了,长度在转整形的时候里面包含了字符串所出现的错误,这是由于数据传输过程中出现粘包导致的,两条send语句紧挨着,导致数据一起发送过去了

两个send语句紧挨:

    # 发送返回值长度
    conn.send(str(len(response.encode())).encode())  # len里面的response也要加一个.encode()
    
    # 发送返回值
    conn.send(response.encode())

那我们应该如何避免这种情况的发生呢?
可以看到,一共发送两次数据,我们可以在第一次发送数据是让客户端随便返回一些数据,然后让服务器在第一次发送完数据之后接受一次数据当作第一次数据发送成功的确认,也就是:

1.在客户端第一次接收到数据大小之后立刻给服务器发送一次任意简短数据当作确认信息
2.在服务器发送完第一次数据之后等待收取一次确认信息,收到确认信息后再进行第二次的数据发送

服务器端

import socket
import os

# 生成socket实例
server = socket.socket()

# 绑定端口
server.bind(('localhost', 6969))

# 监听端口
server.listen()

# 等待链接
conn, addr = server.accept()

# 进入循环
while True:

    print('等待指令')

    # 接收指令
    data = conn.recv(1024)

    # 判断data是否存在
    if not data:
        print('断开')
        break

    # os模块获取指令
    response = os.popen(data.decode()).read()  # 获得返回 接受字符串,返回结果也是字符串

    # popen无法获得错误返回值,所以加一步判断
    if len(response) == 0:
        response = 'Error'

    # 发送返回值长度
    conn.send(str(len(response)).encode())  # 如果在win下len里面的response就必须也要加一个encode()

    # 放置粘包
    conn.recv(1024)

    # 发送返回值
    conn.send(response.encode())

    print('send done')

server.close()

客户端

import socket

# 生成socket实例
client = socket.socket()

# 链接指定ip端口
client.connect(('localhost', 6969))

# 进入循环
while True:

    # 获得指令
    cmd = input('请输入指令:').strip()

    # 指令为空继续等待输入
    if len(cmd) == 0:
        continue

    # 发送指令
    client.send(cmd.encode())

    # 获得返回值长度
    res_size = client.recv(1024).decode()

    # 发送确认收到信息,防治粘包
    client.send(b'111')

    print(res_size)

    # 字符串转整型
    i_res_size = int(res_size)

    # 一次收1024,判断收多少次,每收一次用总长度减去收到的长度,只要剩余长度大于0就一直收
    while i_res_size > 0:

        # 收取返回值,这里虽然写的1024但是每次收的有可能小于1024
        data = client.recv(1024)

        # 减去这一次收取的长度
        i_res_size -= len(data)  # 这里不能-1024 因为recv不一定每次都收1024 有可能小于1024

        # 打印返回值
        print(data.decode())

    # 假如剩余长度小于等于0了说明收完了
    else:

        # 打印收取返回值的总长度
        print('response recv done...', res_size)

客户端服务器端各加一行,就能有效的避免粘包的出现,粘包的情况会在数据传输中经常出现,所以要记住这个方法

转载请注明出处

python自学技术互助扣扣群:670402334

上一篇下一篇

猜你喜欢

热点阅读