zoukankan      html  css  js  c++  java
  • tcp的粘包现象与解决方案

    粘包现象:

    粘包1:连续的小包,会被优化机制给合并

    粘包2:服务端一次性无法完全就收完客户端发送的数据,第二再次接收的时候,会接收到第一次遗留的内容

    模拟一个粘包现象

    服务端

    import socket
    
    server = socket.socket(type=socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8080)
    server.bind(ip_port)
    
    server.listen()
    conn, addr = server.accept()
    
    # 粘包1:连续的小包,会被优化机制给合并
    recv_mes1 = conn.recv(1024)
    # 第二次拿到的就是空数据,因为客户端关闭,服务端会自动执行后续的recv,如果客户端没关闭,服务端就会在recv
    # 处阻塞
    recv_mes2 = conn.recv(1024)
    
    print('第一次接收',recv_mes1.decode('utf-8'))
    print('第二次接收',recv_mes2.decode('utf-8'))
    
    conn.close()
    server.close()

    客户端

    import socket
    
    client = socket.socket(type=socket.SOCK_STREAM)
    
    
    ip_port = ('127.0.0.1',8080)
    client.connect(ip_port)
    
    client.send('哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈'.encode('utf-8'))
    client.send('呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵'.encode('utf-8'))
    
    client.close()

    代码执行结果:

    客户端两次发的内容都被服务端第一个recv接收了,这即为粘包!

    粘包解决方案一:

    先告诉客户端,数据信息的长度,然后等客户端确认之后,再发送真实内容

    代码示例:

    import socket
    # 服务端
    # 读者可以自行创建一个txt文档,里面放的内容劲量大于1024字节,可以放一篇文章进去
    with open('test.txt',mode='rb') as f: 
        content = f.read()
    
    data_len = len(content)
    print('文章长度:',data_len)
    data_len_str = str(data_len)
    count = 0
    
    server = socket.socket(type=socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8080)
    server.bind(ip_port)
    server.listen()
    conn, addr = server.accept()
    print(addr,'已经连接')
    
    conn.send(data_len_str.encode('utf-8'))
    
    status = conn.recv(1024)
    if status == b'ok':
        while count <data_len:
            send_mes = content[count:count+1024]
            len_of_mes = len(send_mes)
            conn.send(send_mes)
            count +=len_of_mes
    
    
    server.close()
    import socket
    # 客户端
    content = b''
    count = 0
    client = socket.socket(type=socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8080)
    client.connect(ip_port)
    
    data_len_str = client.recv(1024).decode('utf-8')
    data_len = int(data_len_str)
    
    client.send(b'ok')
    
    while count < data_len:
        recv_mes = client.recv(1024)
        content +=recv_mes
        count += 1024
    
    print(content.decode('utf-8'))
    client.close()

    粘包解决方案二:

    通过struct模块,将要发送的真实数据的长度进行打包,打包成4个字节,和真实数据一起一次性发送个客户端.客户端取出前4个字节,通过struct解包获得后面真实数据的长度,根据这个长度再进行数据的接受

     代码示例:

    import socket
    import struct
    # 服务端
    
    server = socket.socket(type=socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8080)
    server.bind(ip_port)
    server.listen()
    conn, addr = server.accept()
    print(addr,'已经连接')
    
    with open('test.txt',mode='rb') as f:
        for data in f:
            # data = f.readline()
            data_len = len(data)
            data_len_b = struct.pack('i',data_len)
            conn.send(data_len_b+data)
    
    input('输入任意键结束')
    server.close()
    
    ---------------------分割线------------------------
    # 客户端
    import socket
    import struct
    
    
    client = socket.socket(type=socket.SOCK_STREAM)
    ip_port = ('127.0.0.1',8080)
    client.connect(ip_port)
    content = b''
    
    while 1:
        recv_mes = client.recv(4)
        data_len = struct.unpack('i',recv_mes)[0]
        recv_mes = client.recv(data_len)
        print(recv_mes.decode('utf-8'),end='')
    
    client.close()
  • 相关阅读:
    linux所有命令失效的解决办法
    第一章 网络基础知识
    RNQOJ 数列
    RNQOJ Jam的计数法
    RNQOJ 开心的金明
    RQNOJ 明明的随机数
    分类讨论的技巧
    Unity 碰撞检测
    Unity --yield return
    Unity 移动方式总结
  • 原文地址:https://www.cnblogs.com/Arvin2018/p/10009459.html
Copyright © 2011-2022 走看看