zoukankan      html  css  js  c++  java
  • socket网络编程-粘包

    1.什么是粘包

    只要tcp有粘包现象,udp不会粘包

    粘包主要问题是接收方不知道消息之间的界限,不知道一次性提取多少字节的数据而造成的

    tcp和dup的区别

    1.tcp是基于数据流的,收发的消息不能为空,这酒需要在客户端和服务端都添加空消息的处理机制,防止程序卡主

    2.udp是基于数据报,输入发送空内容(直接回车),那也不是空消息,udp协议会帮你封装一个消息头(消息来源地址,端口等信息)即面向消息的通信是有消息保护边界的。

    为什么出现粘包

    tcp的协议数据不会丢失,是因为没有收完的数据会在基于上次继续接受,已端总是在收到ack时才清楚缓冲区内容,所以数据是可靠,但会出现粘包现象

    udp的recvfrom是阻塞的,一个recvfrom(x)必须对应一个sendinto(y),收完x个字节是数据就算完成,

    若是y>x那就意味着数据丢失,这意味着udp不会出现粘包,但是数据会丢失,不可靠

    3.tcp(传输控制协议)  为什么可靠?

        tcp是面向连接,面向数据流,提供可靠性服务

        tcp传输数据时候先把数据传输到自己缓存,然后通过协议控制将缓存中数据发往对端,

            对端返回一个ack=1,发送端则清理缓存中数据

            对端返回一个ack=0,则重新发送,所以tcp可靠

    2.udp(用户数据报协议)为什么不可靠?

        udp.是面向无连接的,面向消息流,提供高效率服务

              只管把数据发送给对端,不管对端是否收到

             对端不会返回确认收到信息,所以不可靠

    #粘包终极解决方案
    from socket import *
    import subprocess
    import struct
    import json
    
    server=socket(AF_INET,SOCK_STREAM)
    server.bind(('127.0.0.1',8080))
    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
    
                # 运行系统命令
                obj=subprocess.Popen(cmd.decode('utf-8'),         #用户输入命令可以直接执行
                                 shell=True,
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE
                                 )
    
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
    
                #先制作报头
                header_dic={
                    'filename':'a.txt',
                    'total_size':len(stdout) + len(stderr),
                    'hash':'xasf123213123'
                }
                header_json=json.dumps(header_dic)               #json转换成字符串
                header_bytes=header_json.encode('utf-8')         #在把字符串转换成bytes类型
    
                #1、先把报头的长度len(header_bytes)打包成4个bytes,然后发送
                conn.send(struct.pack('i',len(header_bytes)))
                #2、发送报头
                conn.send(header_bytes)
                #3、再发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
    
        conn.close()   # 运行系统命令
                obj=subprocess.Popen(cmd.decode('utf-8'),         #用户输入命令可以直接执行
                                 shell=True,
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE
                                 )
    
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
    
                #先制作报头
                header_dic={
                    'filename':'a.txt',
                    'total_size':len(stdout) + len(stderr),
                    'hash':'xasf123213123'
                }
                header_json=json.dumps(header_dic)               #json转换成字符串
                header_bytes=header_json.encode('utf-8')         #在把字符串转换成bytes类型
    
                #1、先把报头的长度len(header_bytes)打包成4个bytes,然后发送
                conn.send(struct.pack('i',len(header_bytes)))
                #2、发送报头
                conn.send(header_bytes)
                #3、再发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
    
        conn.close()
    from socket import *
    import struct
    import json
    
    client=socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    
    while True:
        cmd=input('>>: ').strip()
        if len(cmd) == 0:continue
        client.send(cmd.encode('utf-8'))
    
        #1、先收4个字节,该4个字节中包含报头的长度
        header_len=struct.unpack('i',client.recv(4))[0]
    
        #2、再接收报头
        header_bytes=client.recv(header_len)
    
        #从报头中解析出想要的内容
        header_json=header_bytes.decode('utf-8')       #bytes转换成utf-8字符串
        header_dic=json.loads(header_json)             #再把用json转换出原来的字典格式
        print(header_dic)
        total_size=header_dic['total_size']           #文件大小
    
        #3、再收真实的数据
        recv_size=0
        res=b''
        while recv_size < total_size :
            data=client.recv(1024)
            res+=data
            recv_size+=len(data)
    
        print(res.decode('gbk')) #1、先收4个字节,该4个字节中包含报头的长度
        header_len=struct.unpack('i',client.recv(4))[0]
    
        #2、再接收报头
        header_bytes=client.recv(header_len)
    
        #从报头中解析出想要的内容
        header_json=header_bytes.decode('utf-8')       #bytes转换成utf-8字符串
        header_dic=json.loads(header_json)             #再把用json转换出原来的字典格式
        print(header_dic)
        total_size=header_dic['total_size']           #文件大小
    
        #3、再收真实的数据
        recv_size=0
        res=b''
        while recv_size < total_size :
            data=client.recv(1024)
            res+=data
            recv_size+=len(data)
    
        print(res.decode('gbk'))
  • 相关阅读:
    VC6中用DOM遍历网页中的元素
    在ActiveX控件中如何动态调用DLL函数之"准"一体化方法
    利用WM_GETMESSAGE做键盘记录
    Web创业的10条戒律
    CRectTracker(橡皮筋)类的使用
    商业计算中Java高精度计算BigDecimal类
    Web常用Filter
    字符串操作
    角度和弧度
    io 文件操作
  • 原文地址:https://www.cnblogs.com/Marcki/p/10111935.html
Copyright © 2011-2022 走看看