zoukankan      html  css  js  c++  java
  • 粘包问题以及解决粘包

    只有TCP有粘包现象,UDP永远不会粘包

    当发送网络数据时,tcp协议会根据Nagle算法将时间间隔短,数据量小的多个数据包打包成一个数据包,先发送到自己操作系统的缓存中,然后操作系统将数据包发送到目标程序所对应操作系统的缓存中,最后将目标程序从缓存中取出,而第一个数据包的长度,应用程序并不知道,所以会直接取出数据或者取出部分数据,留部分数据在缓存中,取出的数据可能第一个数据包和第二个数据包粘到一起。 

    拆包的发生情况

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。

    补充问题一:为何tcp是可靠传输,udp是不可靠传输

    基于tcp的数据传输请参考我的另一篇文章http://www.cnblogs.com/linhaifeng/articles/5937962.html,tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的

    而udp发送数据,对端是不会返回确认信息的,因此不可靠

    补充问题二:send(字节流)和recv(1024)及sendall

    recv里指定的1024意思是从缓存里一次拿出1024个字节的数据

    send的字节流是先放入己端缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失

    struct模块 

    该模块可以把一个类型,如数字,转成固定长度的bytes

    粘包解决方案

    由于应用程序自己发送的数据可以进行打包处理,自己制作协议,对数据进行封装添加报头,然后发送数据部分。而报头必须是固定长度,对方接受时可以先接受报头,对报头进行解析,然后根据报头内的封装的数据的长度对数据进行读取,这样收取的数据就是一个完整的数据包

    服务端

    # 服务端必须满足至少三点:
    # 1. 绑定一个固定的ip和port
    # 2. 一直对外提供服务,稳定运行
    # 3. 能够支持并发
    from socket import *
    import subprocess
    import struct

    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)

    # 链接循环
    while True:
    conn, client_addr = server.accept()
    print(client_addr)

    # 通信循环
    while True:
    try:
    cmd = conn.recv(1024) #cmd=b'dir'
    if len(cmd) == 0: break # 针对linux系统
    obj=subprocess.Popen(cmd.decode('utf-8'),
    shell=True,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
    )
    stdout=obj.stdout.read()
    stderr=obj.stderr.read()
    # 1. 先制作固定长度的报头
    header=struct.pack('i',len(stdout) + len(stderr))
    # 2. 再发送报头
    conn.send(header)
    # 3. 最后发送真实的数据
    conn.send(stdout)
    conn.send(stderr)
    except ConnectionResetError:
    break

    conn.close()

    server.close()

    客户端

    from socket import *
    import struct

    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))

    # 通信循环
    while True:
    cmd=input('>>: ').strip()
    if len(cmd) == 0:continue
    client.send(cmd.encode('utf-8'))
    #1. 先收报头,从报头里解出数据的长度
    header=client.recv(4)
    total_size=struct.unpack('i',header)[0]
    #2. 接收真正的数据
    cmd_res=b''
    recv_size=0
    while recv_size < total_size:
    data=client.recv(1024)
    recv_size+=len(data)
    cmd_res+=data

    print(cmd_res.decode('gbk'))

    client.close()

  • 相关阅读:
    nginx实现请求的负载均衡 + Keep Alive实现nginx的高可用
    理解什么是JWT(Json web token)及Python实现
    TCP/UDP协议到底是什么
    Redis实现分布式单点登录
    Python面试题---给定一个字符串 {xxx[xxx{xxx}]xx{x[xxx]xxx{xxx}xx}x} 判断其中的 {}[]() 是否成对出现
    Typora里面如何快捷改变字体颜色?
    基于Docker安装关系型数据库PostgrelSQL替代Mysql
    PEP8-Python编码规范
    欢迎来到我的友链小屋
    windows下lib和dll区别
  • 原文地址:https://www.cnblogs.com/fushaunglin/p/9578856.html
Copyright © 2011-2022 走看看