• day 29


    tcp 下的粘包问题

    半连接数

    半连接数的产生有两个方面原因组成的。

    1 是由客户端恶意发起洪水攻击造成的,在三次握手操作的时候,客户端刻意不往服务器发出第三次握手指令。这样的操作如果过多,会导致内存空间占满,这时候如果有正常数据进来的话,服务器无法处理。这样程序就会崩溃掉。

    2 是因为请求过多,服务无法及时处理,这样也是造成半连接数的一个原因。

    在socket中listen可以让我们自行规定最大的半连接数。保护程序的正常运行。

    自定义报头

    当需要在传输数据

    发送端

    1.发送报头长度 2 发送报头数据  3 发送文件内容。

    接收端:

    接收报头长度  接收报头信息   接收文件内容。

    粘包问题

    数据粘包是只有在TCP下才会有,因为TCP是流式协议,,数据之间并没有一个明显的分割线,但数据长度可以看作一个数据之间分割线,别的再无其他。数据长度也是我们用来处理粘包的问题入手点。

    首先说一下粘包问题出现的原因,造成粘包问题是双向的,首先在客户端往服务器发送数据时,当数据小并且间隔时间断,nagle的算法优化机制就会将这些短且小的数据进行打包一起发送,又因为TCP是流式协议,数据之间并无分割,数据就会混在一起,就像一杯雪碧倒入了一盆自来水中,鬼见愁。

    而服务如果没有及时处理数据,即使数据从客户端发送过来时候没有发生粘包问题,也会在服务器的缓存区发生粘包问题。

       #服务器
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    client,addr = server.accept()
    data = client.recv(1024).decode('utf-8')
    data1 = client.recv(1024).decode('utf-8')
    print(data)
    print(data1)

    #客户端代码
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    client.send('hello'.encode('utf-8'))
    client.send('world'.encode('utf-8'))

    #打印结果

     helloworld





    可以出两次向服务器发送的信息被一起打印了出来,出现了粘包问题。

    解决办法就是从数据长度上去入手,先向服务器发送每条数据的长度,再发送数据的真实信息,以数据长度来作为接收数据的参照。这样就可以解决粘包问题。

     #服务器代码
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    client,addr = server.accept()
    data = client.recv(5).decode('utf-8')
    data1 = client.recv(6).decode('utf-8')
    print(data)
    print(data1)
    
    client.close()
    
     #客户端代码
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    client.send('hello'.encode('utf-8'))
    client.send('world'.encode('utf-8'))
    
     #打印结果
      hello
      world
        

    这里是将信息的字节长度发送给服务器,通过字节长度来接收数据这样就解决了粘包问题。

    有了这个解决思路就可以用来完成文件的上传下载



    #客户端下载代码
    import
    socket import struct import json client = socket.socket() try: client.connect(('127.0.0.1', 8080)) print('连接成功') # 接收报头长度 head_size = struct.unpack('q',client.recv(8))[0] # 接收报头数据 head_str = client.recv(head_size).decode('utf-8') file_info = json.loads(head_str) print('报头数据:',file_info) file_size = file_info.get('file_size') print(type(file_size)) file_name = file_info.get('file_name') recv_size = 0 buffer_size = 2048 f = open(file_name,'wb') while True: if int(file_size) - recv_size >= buffer_size: a = client.recv(buffer_size) else: a = client.recv(int(file_size) - recv_size) f.write(a) recv_size += len(a) print('已下载%s%%'%(recv_size/file_size*100)) if recv_size == file_size: break f.close() except Exception as e: print( '错误信息', e)


    #服务器提供给客户端下载数据代码
    import socket
    import os
    import json
    import struct
    
    
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    f = None
    while True:
        client,addr = server.accept()
        try:
            path = r"C:Users胡心亚Desktop2.今日内容.mp4"                  #拼接出文件的绝对路径
            file_path = os.path.getsize(path)          #通过getsize方法可以获取文件的字节大小
            print(file_path)
    
            file_info = {"file_name":r"F:day183.time模块.mp4","file_size":file_path}      #创建报头,包头中包含文件名,和文件字节数
            json_str = json.dumps(file_info).encode('utf-8')                 #使用json模块将报头信息序列化成json格式的字符串,用于传输,主要是为了方便传输
            # 发送报头长度
            client.send(struct.pack('q',len(json_str)))           #在文件下载时为了防止粘包的情况发生,要先将文件的字节长度传到客户端,规定好客户端需要接收多少字节的内容
    
            # 发送报头
            client.send(json_str)                     #再将文件信息传给客户端
            # 发送文件
            f = open(path,'rb')
            while True:
                temp = f.read(2048)
                # 文件大小不一致,有可能会超过缓存区的大小,
                # 这时候为了保证文件可以正常传输,可以采用循环传输的方式传输,
                # 一次传输2048个字节,当文件信息传输完了之后做出判断,停止进程
                #
                if not temp:
                    break
                client.send(temp)
            print('文件发送成功')
        except Exception as e :
            print('出错了',e)
        finally:
            if f:
                f.close()
      # 在最后做一个异常处理,是判断文件是否一开始就是走不通的,
    # 先定义了一个f 为None 如果f 走的同就保证f是有值的,没有值就做异常处理
        client.close()

    总结解决粘包办法总结

    1.使用struct 将真实数据的长度转为固定的字节数据

    2.发送长度数据

    3.发送真实数据

    接收端

    1.先收长度数据 字节数固定

    2.再收真实数据 真实可能很长 需要循环接收

    发送端和接收端必须都处理粘包 才算真正的解决了

     

  • 相关阅读:
    Django框架(二十三)-- Django rest_framework-视图组件
    Django框架 --序列化组件(serializer)
    Django框架(十六)-- 中间件、CSRF跨站请求伪造
    leetcode 之 两数之和
    vue中webpack相关的配置
    vue-cli3 初始化创建项目
    vue之模块化开发
    vue基础之常用方法
    凯哥带你用python撸算法之雪花算法
    docker数据卷之持久化操作
  • 原文地址:https://www.cnblogs.com/1624413646hxy/p/10952040.html
Copyright © 2020-2023  润新知