网络编程基础粘包现象

粘包

  • tcp是流式传输,字节流,数据与数据之间是没有边界的
    • 流式传输优点:
      • 不限定长度
      • 可靠传输
    • 缺点:
      • 和一个人的通信连接conn会一直占用我们的通信资源
  • udp协议,面向数据包的传输
    • 数据包优点
      • 由于不需要建立连接,所以谁发的消息我都能接受到
    • 缺点
      • 不能传输过长的数据
      • 不可靠

粘包现象

  • 由于流式传输的特点,产生了数据连续发送的粘包现象。
    • 在一个conn建立起来的连接上传输的多条数据是没有边界的
  • 数据的发送和接收实际上不是在执行send/recv的时候就立刻被发送和接收,而是需要经过操作系统内核
  • Nagle 算法,能够将发送间隔实际很近的短数据合成一个包发送到接收端
  • 拆包机制: 当要发送的数据超过了网络上能够传输的最大长度,就会被tcp协议强制拆包

解决粘包问题

  • struct模块

    • park("i",len(msg)) 把数字转成4字节

    • unpack("i",bytes)[0] 把四字节转换成长度

    • server端
      import struct
      import socket
      
      sk = socket.socket()
      sk.bind(('127.0.0.1',9090))
      sk.listen()
      
      conn,addr = sk.accept()
      while True:
          s = input('>>>').encode('utf-8')
          pack_num = struct.pack('i',len(s))
          conn.send(pack_num)
          conn.send(s)
      conn.close()
      sk.close()
      
    • client端
      import socket
      import struct
      sk = socket.socket()
      sk.connect(('127.0.0.1',9090))
      
      while True:
          pack_num = sk.recv(4)
          num = struct.unpack('i',pack_num)[0]
          ret = sk.recv(num)
          print(ret.decode('utf-8'))
      sk.close()
      

并发的 socketserver

  • 实现同一时刻server端可以和多个client端建立连接

验证客户端链接的合法性

  • server端

  • import os
    import hmac
    import socket
    def auth(conn):
        msg = os.urandom(32)  # # 生成一个随机的字符串
        conn.send(msg)  # # 发送到client端
        result = hmac.new(secret_key, msg)  # 处理这个随机字符串,得到一个结果
        client_digest = conn.recv(1024)  # 接收client端处理的结果
        if result.hexdigest() == client_digest.decode('utf-8'):
            print('是合法的连接')  # 对比成功可以继续通信
            return True
        else:
            print('不合法的连接')  # 不成功 close
            return False
    secret_key = b'alex_sb'
    sk = socket.socket()
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    conn,addr = sk.accept()
    if auth(conn):
        print(conn.recv(1024))
        # 正常的和client端进行沟通了
        conn.close()
    else:
        conn.close()
    sk.close()
    
  • client端

  • import hmac
    import socket
    def auth(sk):
        msg = sk.recv(32)
        result = hmac.new(key, msg)
        res = result.hexdigest()
        sk.send(res.encode('utf-8'))
    
    key = b'alex_s'
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    auth(sk)
    sk.send(b'upload')
    # 进行其他正常的和server端的沟通
    sk.close()
    
posted @ 2018-09-17 22:49  云丛  阅读(124)  评论(0编辑  收藏  举报