网络编程2 socket套接字

168 阅读3分钟

socket套接字

  • 套接字家族
    文件类型的: AF_UNIX
    网络类型的: AF_INET
    
    • 图示: image.png
    • 服务端启动之后,客户端才能够运行起来
              使用套接字完成客户端与服务端的交互
        服务端:
      # 1.创建一个socket对象
      server = socket.socket()  # 括号里什么都写默认是基于网络的TCP套接字
      # 2.绑定一个固定的地址(ip\port)
      server.bind(('127.0.0.1',8080))       # 地址用本机的
      # 3.半连接池    # 主要是为了做缓冲 避免太多无效等待
      server.listen(5)
      # 4.连接客户端
      sock, address = server.accept()   # accept有两个形参返回值,用两个接收
      print(sock,address)
      # 5.数据交互
      sock.send(b'hello big')     # 往客户端发送数据
      data = sock.recv(1024)      # 接收客户端发送的数据   1024bytes
      print(data)
      # 6.断开连接
      sock.close()     # 断掉与客户端的连接
      server.close()   # 关机
      
        客户端:
      import socket
      # 1.产生一个socket对象
      client = socket.socket()
      # 2.连接服务端(ip,port)
      client.connect(('127.0.0.1',8080))
      # 3.数据交互
      data = client.recv(1024) #接收服务端发送的数据
      print(data)
      client.send(b'fack you')
      # 关闭
      client.close()
             
             现在我们只完成了客户端接收到了服务端发送的信息,并不能进行互动
      针对以上完成优化
      
      1.send与recv
          客户端与服务端不能同时收或发
      2.针对自定义的消息
          使用input即可
      3.循环通信(畅聊下去) 
          在用户交互环节添加循环
      4.避免客户端断开连接,让服务可以持续运行
          使用异常捕获,一旦客户端断开连接 服务端结束通信循环 调到连接处等待
      5.避免输入空格(什么都不输)后程序没有响应
          判断输入的东西是否为空,如果是就重新输入(主要是客户端)
      6.避免程序频繁重启后端口被占用报错(mac电脑)
          from socket import SOL_SOCKET,SO_REUSEADDR
      server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)       # 在bind前面添加
      7.客户端异常退出会发送空消息(针对mac linux)
      针对接收的消息加判断处理即可 
      
  • 黏包问题
    服务端代码
        sock.recv(1024)
        sock.recv(1024)
        sock.recv(1024)
    
    客户端代码
        client.send(b'jason')
        client.send(b'kevin')
        client.send(b'tony')
    
    • TCP特性
      • 流式协议:所有的数据类似于水流一样,连接到一起
      数据量很小,并且时间间隔很多,就会自动组织到一起
      
      • recv
        需要接收的数据量是未知的,所以才会有黏包的现象,知道的话就不会产生黏包了
        
      • 黏包实例
        struct模块无论数据长度是多少,都可以帮你打包成固定长度 
        
        image.png
      基于打包后的固定长度,可以反向解析出其真实长度
          struct模块针对数据量特别大的数字没有办法打包 
          
         
          服务端:
            # 1.先构造数据文件的字典
            # 2.将字典打包成固定长度的数据
            # 3.发送固定长度的字典报头
            # 4.发送真实字典数据
            # 5.发送真实数据
          
          客户端
            # 1.先接收长度为4的报头数据
            # 2.根据报头解包出字典的长度
            # 3.直接接收字典数据
            # 4.解码并反序列化出字典
            # 5.从数据字典中获取真实数据的各项信息
      
          ```
      
    • UDP协议
      • 用户数据报协议
      简单的面向无连接的,不可靠的数据报的 传输层协议
      
      • 黏包问题
      UDP不存在黏包问题,所以UDP多用于短消息的交互(例如我们使用的qq)
      
      • UDP实例操作
      服务端不需要考虑客户端是否异常退出
          服务端
            import socket
            server = socket.socket(type=socket.SOCK_DGRAM)
            server.bind(('127.0.0.1', 8080))
            msg, address = server.recvfrom(1024)
            print('msg>>>:%s' % msg.decode('utf8'))
            print('address>>>:',address)
            server.sendto('我是服务端,和我一起成为魔法少女吧'.encode('utf8'), address)
      
          客户端
            import socket
            client = socket.socket(type=socket.SOCK_DGRAM)
            server_address = ('127.0.0.1', 8080)
            client.sendto('我是客户端,魔法少女可以拯救世界吗'.encode('utf8'), server_address)
            msg, address = client.recvfrom(1024)
            print('msg>>>:%s' % msg.decode('utf8'))
            print('address>>>:',address)