zoukankan      html  css  js  c++  java
  • socket

    Day30 socket

    1. socket套接字

      ​ socket处于应用层与传输层之间,提供了一些简单的接口. 避免与操作系统之间对接,省去了相当繁琐复杂的操作.

      ​ socket在python中属于一个模块.为我们提供了这些简单的功能.1565871775183

    2. 单个客户端与服务端通信(low版)

      server

      1565873426711

      import socket
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.bind(('127.0.0.1',8088))
      phone.listen(3)
      
      conn,addr = phone.accept()
      
      from_client_data = conn.recv(1024)
      print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
      
      to_client_data = input('>>>')
      conn.send(to_client_data.encode('utf-8'))
      conn.close()
      phone.close()
      

      client

      1565873452645

      import socket
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.connect(('127.0.0.1',8088))
      
      to_server_data = input('>>>')
      phone.send(to_server_data.encode('utf-8'))
      
      from_server_data = phone.recv(1024)
      print(f'收到来自服务端的信息:{from_server_data.decode("utf-8")}')
      phone.close()
      
    3. 基于TCP协议的socket单个循环通信

      server:

      import socket
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.bind(('127.0.0.1',8088))
      phone.listen(3)
      
      conn,addr = phone.accept()
      while 1:
          try:
              from_client_data = conn.recv(1024)
              print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
      
              to_client_data = input('>>>')
              conn.send(to_client_data.encode('utf-8'))
          except ConnectionResetError:
              print('客户端异常断开链接')
              break
      conn.close()
      phone.close()
      

      client:

      import socket
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.connect(('127.0.0.1',8088))
      
      while 1:
              to_server_data = input('>>>')
              phone.send(to_server_data.encode('utf-8'))
      
              from_server_data = phone.recv(1024)
              print(f'收到来自服务端的信息:{from_server_data.decode("utf-8")}')
      phone.close()
      
    4. 基于TCP协议的socket 链接+循环 通信

      server

      import socket
      
      phone = socket.socket()
      
      phone.bind(('127.0.0.1',8848))
      
      phone.listen(2)
      # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
      
      while 1:
          conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
          print(f'链接来了: {conn,addr}')
      
          while 1:
              try:
                  from_client_data = conn.recv(1024)  # 最多接受1024字节
      
                  if from_client_data.upper() == b'Q':
                      print('客户端正常退出聊天了')
                      break
      
                  print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
                  to_client_data = input('>>>').strip().encode('utf-8')
                  conn.send(to_client_data)
              except ConnectionResetError:
                  print('客户端链接中断了')
                  break
          conn.close()
      phone.close()
      
      
      
      

      client

      import socket
      
      phone = socket.socket()
      
      phone.connect(('127.0.0.1',8848))
      while 1:
          to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
          if not to_server_data:
              # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
              print('发送内容不能为空')
              continue
          phone.send(to_server_data)
          if to_server_data.upper() == b'Q':
              break
          from_server_data = phone.recv(1024)  # 最多接受1024字节
          print(f'来自服务端消息:{from_server_data.decode("utf-8")}')
      
      phone.close()
      
      
      
    5. 基于TCP协议的socket通信: 实例: 远程执行命令

      server

      import socket
      import subprocess
      phone = socket.socket()
      
      phone.bind(('127.0.0.1',8848))
      
      phone.listen(2)
      # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
      
      while 1:
          conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
          print(f'链接来了: {conn,addr}')
      
          while 1:
              try:
      
                  from_client_data = conn.recv(1024)  # 最多接受1024字节
      
      
                  if from_client_data.upper() == b'Q':
                      print('客户端正常退出聊天了')
                      break
      
                  obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                         shell=True,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE,
      
                                         )
                  result = obj.stdout.read() + obj.stderr.read()
      
                  conn.send(result)
              except ConnectionResetError:
                  print('客户端链接中断了')
                  break
          conn.close()
      phone.close()
      
      
      
      
      # shell: 命令解释器,相当于调用cmd 执行指定的命令。
      # stdout:正确结果丢到管道中。
      # stderr:错了丢到另一个管道中。
      # windows操作系统的默认编码是gbk编码。
      
      
      

      client

      import socket
      
      phone = socket.socket()
      
      phone.connect(('127.0.0.1',8848))
      while 1:
          to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
          if not to_server_data:
              # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
              print('发送内容不能为空')
              continue
          phone.send(to_server_data)
          if to_server_data.upper() == b'Q':
              break
          from_server_data = phone.recv(1024)  # 最多接受1024字节
          print(f'{from_server_data.decode("gbk")}')
      
      phone.close()
      
      
      
    6. 粘包现象

      展示收发的问题

      server

      # 发多次收一次
      # import socket
      #
      # phone = socket.socket()
      #
      # phone.bind(('127.0.0.1',8848))
      #
      # phone.listen(5)
      #
      #
      # conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
      #
      # from_client_data = conn.recv(1024)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      #
      # from_client_data = conn.recv(1024)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      # conn.close()
      # phone.close()
      
      
      
      # 发一次收多次
      
      # import socket
      #
      # phone = socket.socket()
      #
      # phone.bind(('127.0.0.1',8848))
      #
      # phone.listen(5)
      #
      #
      # conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
      #
      # from_client_data = conn.recv(3)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      #
      # from_client_data = conn.recv(3)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      #
      # from_client_data = conn.recv(3)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      #
      # from_client_data = conn.recv(3)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      #
      # conn.close()
      # phone.close()
      
      

      client

      # 发多次收一次
      # import socket
      #
      # phone = socket.socket()
      #
      # phone.connect(('127.0.0.1',8848))
      #
      #
      # phone.send(b'he')
      # phone.send(b'llo')
      #
      #
      # phone.close()
      # Nigle算法
      
      
      # 发一次收多次
      # import socket
      #
      # phone = socket.socket()
      #
      # phone.connect(('127.0.0.1',8848))
      #
      #
      # phone.send(b'hello world')
      #
      #
      # phone.close()
      
      
      

      现象

      server

      # 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
      # import socket
      # import subprocess
      # phone = socket.socket()
      #
      # phone.bind(('127.0.0.1',8848))
      #
      # phone.listen(2)
      # # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
      #
      # while 1:
      #     conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
      #     # print(f'链接来了: {conn,addr}')
      #
      #     while 1:
      #         try:
      #
      #             from_client_data = conn.recv(1024)  # 最多接受1024字节
      #
      #
      #             if from_client_data.upper() == b'Q':
      #                 print('客户端正常退出聊天了')
      #                 break
      #
      #             obj = subprocess.Popen(from_client_data.decode('utf-8'),
      #                                    shell=True,
      #                                    stdout=subprocess.PIPE,
      #                                    stderr=subprocess.PIPE,
      #
      #                                    )
      #             result = obj.stdout.read() + obj.stderr.read()
      #             print(f'总字节数:{len(result)}')
      #             conn.send(result)
      #         except ConnectionResetError:
      #             print('客户端链接中断了')
      #             break
      #     conn.close()
      # phone.close()
      
      
      
      # s1 = '太白jx'
      # # print(len(s1))
      # b1 = s1.encode('utf-8')
      # # print(b1)
      # print(len(b1))
      
      
      '''
               客户端                          服务端
      
      第一次:  ipconfig                       317字节
               300个字节                       17个字节
               
      
               客户端                          服务端
      
      第二次:   dir                           376字节
                17字节                        376字节
               
      
      '''
      
      
      # 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
      
      # import socket
      #
      # phone = socket.socket()
      #
      # phone.bind(('127.0.0.1',8848))
      #
      # phone.listen(5)
      #
      #
      # conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
      #
      # from_client_data = conn.recv(1024)  # 最多接受1024字节
      # print(f'来自客户端{addr}消息:{from_client_data.decode("utf-8")}')
      # conn.close()
      # phone.close()
      
      
      # 展示一些收发的问题。
      
      

      client

      # import socket
      #
      # phone = socket.socket()
      #
      # phone.connect(('127.0.0.1',8848))
      # while 1:
      #     to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
      #     if not to_server_data:
      #         # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
      #         print('发送内容不能为空')
      #         continue
      #     phone.send(to_server_data)
      #     if to_server_data.upper() == b'Q':
      #         break
      #     from_server_data = phone.recv(300)  # 最多接受1024字节
      #     # print(f'{from_server_data.decode("gbk")}')
      #     print(len(from_server_data))
      #
      # phone.close()
      
      
      
      # 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
      
      # import socket
      #
      # phone = socket.socket()
      #
      # phone.connect(('127.0.0.1',8848))
      #
      #
      # phone.send(b'he')
      # phone.send(b'll')
      # phone.send(b'o')
      #
      #
      # phone.close()
      # Nigle算法
      
      
    7. 操作系统的缓存区

      1. 为什么存在缓冲区??

        1. 暂时存储一些数据.
        2. 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速.

        缺点: 造成了粘包现象之一.

    8. 什么情况下出现粘包

      1. 出现粘包的情况

        连续短暂的send多次(数据量很小),你的数据会统一发送出去.

      2. 深入研究收发

    9. 如何解决粘包现象

      解决粘包现象的思路:

      服务端发一次数据 10000字节,

      客户端接收数据时,循环接收,每次(至多)接收1024个字节,直至将所有的字节全部接收完毕.将接收的数据拼接在一起,最后解码.

      1. 遇到的问题: recv的次数无法确定.

        你发送总具体数据之前,先给我发一个总数据的长度:5000个字节。然后在发送总数据。

        客户端: 先接收一个长度。 5000个字节。

        然后我再循环recv 控制循环的条件就是只要你接受的数据< 5000 一直接收。

      2. 遇到的问题: 总数据的长度转化成的字节数不固定

        服务端:
        conn.send(total_size) 
        
        conn.send(result)
        total_size int类型
        
        
        客户端:
        total_size_bytes = phone.recv(4)
        total_size
        data = b''
        while len(data) < total_size:
        	data = data + phone.recv(1024)
        
        
        

        你要将total_size int类型转化成bytes类型才可以发送

        387 ---- > str(387) '387' ---->bytes b'387' 长度 3bytes

        4185 ----> str(4185) '4185' ---->bytes b'4185' 长度 4bytes

        18000------------------------------------------------------> 长度 5bytes

        我们要解决:

        将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来。

        struct模块

        1565877838745

    10. low版解决粘包现象

      server

      # 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
      import socket
      import subprocess
      import struct
      phone = socket.socket()
      
      phone.bind(('127.0.0.1',8848))
      
      phone.listen(2)
      # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
      
      while 1:
          conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
          # print(f'链接来了: {conn,addr}')
      
          while 1:
              try:
      
                  from_client_data = conn.recv(1024)  # 接收命令
      
      
                  if from_client_data.upper() == b'Q':
                      print('客户端正常退出聊天了')
                      break
      
                  obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                         shell=True,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE,
      
                                         )
                  result = obj.stdout.read() + obj.stderr.read()
                  total_size = len(result)
                  print(f'总字节数:{total_size}')
      
                  # 1. 制作固定长度的报头
                  head_bytes = struct.pack('i',total_size)
      
                  # 2. 发送固定长度的报头
                  conn.send(head_bytes)
      
                  # 3. 发送总数据
                  conn.send(result)
              except ConnectionResetError:
                  print('客户端链接中断了')
                  break
          conn.close()
      phone.close()
      
      
      
      
      # import struct
      
      
      

      client

      import socket
      import struct
      phone = socket.socket()
      
      phone.connect(('127.0.0.1',8848))
      while 1:
          to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
          if not to_server_data:
              # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
              print('发送内容不能为空')
              continue
          phone.send(to_server_data)
          if to_server_data.upper() == b'Q':
              break
      
          # 1. 接收报头
          head_bytes = phone.recv(4) 
          # 2. 反解报头
          total_size = struct.unpack('i',head_bytes)[0]
      
          total_data = b''
      
          while len(total_data) < total_size:
              total_data += phone.recv(1024)
      
          print(len(total_data))
          print(total_data.decode('gbk'))
      
      phone.close()
      
      
  • 相关阅读:
    gradle项目与maven项目互转
    GET和POST两种基本请求方法的区别
    gradle项目打war和jar包
    maven项目打war和jar
    winsw打包jar
    前端
    CentOS
    Vue
    Spring
    Vue
  • 原文地址:https://www.cnblogs.com/-777/p/11361614.html
Copyright © 2011-2022 走看看