zoukankan      html  css  js  c++  java
  • 解决黏包的问题

    一,黏包的触发

    sk.bind(('127.0.0.1',8090))
    sk.listen()
    conn,addr=sk.accept()
    ret=conn.recv(1)
    ret1=conn.recv(1)
    ret2=conn.recv(1)
    print(ret)
    print(ret1)
    print(ret2)
    conn.close()
    sk.close()
    import socket
    sk=socket.socket()
    sk.connect(('127.0.0.1',8090))
    sk.send(b'hello1234')
    sk.close()
    C:UsershcAppDataLocalProgramsPythonPython36python3.exe C:/s9/day32/网络编程.py
    b'h'
    b'e'
    b'l'
    
    Process finished with exit code 0

    当接受的字节小于发送的字节数,发送一个数据包,可以有多个recv接收,直到接收完了为止

    sk.bind(('127.0.0.1',8090))
    sk.listen()
    conn,addr=sk.accept()
    ret=conn.recv(1024)
    ret1=conn.recv(2)
    ret2=conn.recv(2)
    print(ret)
    print(ret1)
    print(ret2)
    conn.close()
    sk.close()
    import socket
    sk=socket.socket()
    sk.connect(('127.0.0.1',8090))
    sk.send(b'h')
    sk.send(b'1234')
    sk.close()
    C:UsershcAppDataLocalProgramsPythonPython36python3.exe C:/s9/day32/网络编程.py
    b'h1234'
    b''
    b''
    
    Process finished with exit code 0

    多个send小数据连在一起,会发生黏包现象,是tcp协议内部的优化算法造成的

    在发送端有一个缓存机制

    当发送方没有继续发送的时候,接收方还一直有recv在等待接收,此时会返回空的b“   ”的形式

    解决黏包方法一:

    import socket
    sk=socket.socket()
    sk.bind(('127.0.0.1',8090))
    sk.listen()
    conn,addr=sk.accept()
    while 1:
        info=input('>>')
        if info=='q':
            conn.send(b'q')
            break
        conn.send(info.encode('gbk'))
        ret1=conn.recv(1024).decode('utf-8')
        print(ret1)
        conn.send(b'ok')
        ret=conn.recv(int(ret1)).decode('gbk')
        print(ret)
    conn.close()
    sk.close()
    import socket,subprocess
    sk=socket.socket()
    sk.connect(('127.0.0.1',8090))
    while 1:
        ret=sk.recv(1024).decode('gbk')
        if ret=='q':
            break
        res=subprocess.Popen(ret,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        a=res.stdout.read()
        b=res.stderr.read()
        sk.send((str(len(a)+len(b)).encode('utf-8')))
        sk.recv(1024)
        sk.send(a)
        sk.send(b)
    sk.close()

    由于之前是不知道自己发送的数据是多大的,所以现在首先发送过去的是这个数据的大小,然后在按照数据的长度来接收

    好处:确定我们到底要接收多大的数据,

    当我们要发送大数据的时候 ,要明确的告诉接收方要发送多大的数据,以便接收方能够准确的接收到所有数据
    # 多用在文件传输的过程中

    缺点:

    不好的地方:多了一次交互
    方法二:使用模块,struct
    import socket,struct
    
    sk=socket.socket()
    sk.bind(('127.0.0.1',8090))
    sk.listen()
    conn,addr=sk.accept()
    while 1:
        info=input('>>')
        if info=='q':
            conn.send(b'q')
            break
        conn.send(info.encode('gbk'))
        ret1=conn.recv(4)
        ret1=struct.unpack('i',ret1)[0]
        ret=conn.recv(int(ret1)).decode('gbk')
        print(ret)
    conn.close()
    sk.close()
    import socket,subprocess,struct
    sk=socket.socket()
    sk.connect(('127.0.0.1',8090))
    while 1:
        ret=sk.recv(1024).decode('gbk')
        if ret=='q':
            break
        res=subprocess.Popen(ret,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        a=res.stdout.read()
        b=res.stderr.read()
        len_1=(len(a)+len(b))
        len_1=struct.pack('i',len_1)
        sk.send(len_1)
        sk.send(a)
        sk.send(b)
    sk.close()

    在网络上传输的所有数据都叫数据包,数据包里的所有数据都叫报文,

    报文里有的不止是数据,还有ip地址,mac地址,,端口号

    所有的报文都有报头

    报文可以是自己定制的一般在比较复杂的时候使用

    一般有:

    文件的名字,

    文件的大小

    文件的类型

    存储路径

    下面写一个用自己设置的报文来写一个下载的程序

    client:

    import
    os import json import struct import socket sk = socket.socket() sk.connect(('127.0.0.1',8090)) buffer = 1024 # buffer = 1024 # 发送文件 head = {'filepath':r'C:UsershcDesktop', 'filename':r'单词表--01.xlsx', 'filesize':None} file_path = os.path.join(head['filepath'],head['filename']) filesize = os.path.getsize(file_path) head['filesize'] = filesize json_head = json.dumps(head) # 字典转成了字符串 bytes_head = json_head.encode('utf-8') # 字符串转bytes # 计算head的长度 head_len = len(bytes_head) # 报头的长度 pack_len = struct.pack('i',head_len) sk.send(pack_len) # 先发报头的长度 sk.send(bytes_head) # 再发送bytes类型的报头 with open(file_path,'rb') as f: while filesize: print(filesize) if filesize >= buffer: content = f.read(buffer) # 每次读出来的内容 print('===>',len(content)) sk.send(content) filesize -= buffer else: content = f.read(filesize) sk.send(content) filesize = 0 sk.close()
    server:



    import
    time import json import socket import struct sk = socket.socket() sk.bind(('127.0.0.1',8090)) sk.listen() buffer = 1024 # buffer = 1024 conn,addr = sk.accept() # 接收 head_len = conn.recv(4) head_len = struct.unpack('i',head_len)[0] json_head = conn.recv(head_len).decode('utf-8') head = json.loads(json_head) filesize = head['filesize'] print(filesize) with open(head['filename'],'wb') as f: while filesize: if filesize >= buffer: print(filesize) content = conn.recv(buffer) f.write(content) filesize -= buffer else: content = conn.recv(filesize) f.write(content) filesize = 0 print('====>',len(content)) print(filesize) print('服务端。。。。') conn.close() sk.close()
  • 相关阅读:
    30分钟掌握ES6/ES2015核心内容[上和下], 不错的说
    根据HTML5 获取当前位置的经纬度【百度地图】【高德地图】
    vue2 入门 教程 单页应用最佳实战[*****]
    JavaScript如何比较两个数组的内容是否相同【转】
    推荐下:开源ckplayer 网页播放器, 跨平台(html5, mobile),flv, f4v, mp4, rtmp协议. webm, ogg, m3u8 !
    浅谈 Underscore.js 中 _.throttle 和 _.debounce 的差异[转]
    原生JavaScript插件开发[转]
    性能监控之Spotlight
    Jmeter(三十五)聚合报告
    Jmeter(三十四)Jmeter-Question之“Cookie获取”
  • 原文地址:https://www.cnblogs.com/xuguangzong/p/8378042.html
Copyright © 2011-2022 走看看