TCP的recv问题

 TCP编程时,recv()函数默认阻塞等待,返回socket携带的信息,要根据业务场景设计recv的等待和结束机制:

recv阻塞,以接收空字符串结束

  • 如下TcpServer.py,为通常的recv机制:
    • 因为客户端单次send数据长度可能超过设置的1024,所以把recv放到循环体内,以保证接收数据流的完整性。
    • 但是不可能让recv一直阻塞等待这里,通常的做法就是判断数据长度为空则退出循环
    • 在正常传输数据时,recv接收的不可能为空,也就无法触发break退出循环recv
    • 需要额外发送一次,携带空字符串的请求,用来触发break关键字
      import socket
      
      address = ("0.0.0.0", 12346)
      
      
      def tcp_server():
          server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          server_socket.bind(address)
          server_socket.listen(5)
          print("TcpServer开始监听:{}".format(address))
          while True:
              client_socket, client_address = server_socket.accept()
              data = str()
              while True:
                  msg = client_socket.recv(1024)
                  data += msg
                  if not len(msg):
                      break
              print(client_address, ":", data.decode())
              client_socket.close()
      
      
      if __name__ == '__main__':
          tcp_server()
      
      

       

recv阻塞,以判断结束符结束

  • 下面python程序tcp_link函数中,创建结束符terminator,send时携带字符串上加上termimator,recv时判断数据最后7个字符串为terminator,则认为传输结束,退出循环
  • 优势:较上一种,同样保证完整接收到数据,不需要再次发送请求节省了系统性能开销,主动标识结束增加了代码可控性,退出更优雅
    import socket
    import threading
    import subprocess
    
    
    class TcpServer:
        def __init__(self):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind(("0.0.0.0", 60000))
            s.listen(5)
            print("Waiting for connection...")
            while True:
                # 接收一个新连接
                sock, address = s.accept()
                # 创建新的线程来处理tcp连接
                t = threading.Thread(target=self.tcp_link, args=(sock, address), daemon=True)
                t.start()
    
        def tcp_link(self, sock, address):
            print("Accept new connection from %s:%s..." % address, "="*30)
            terminator = "abcdefg"
            buffer = str()
            while True:
                data = sock.recv(1024).decode()
                buffer += data
                if buffer[-7:] == terminator:
                    break
            buffer = buffer[:-7]
            print("accept data:{}".format(buffer))
            if buffer[-4:] == ".bat":
                # 后缀为.bat,说明要运行批处理文件
                print(".bat file running")
                self.run_bat(buffer)
            else:
                # 其他的判定为运行adb命令
                print("adb command running")
                command_reply = self.run_command(buffer) + terminator
                reply = str(command_reply).encode()
                sock.send(reply)
            sock.close()
            print("Connection from %s:%s closed!" % address, "="*30)
    
        @staticmethod
        def run_bat(bat_file_path):
            subprocess.getoutput(str(bat_file_path))
            try:
                subprocess.check_output(str(bat_file_path),
                                        shell=True,
                                        universal_newlines=True,
                                        stderr=subprocess.STDOUT,
                                        timeout=3)
            except subprocess.CalledProcessError as ex:
                print('run .bat file error, message: {}'.format(ex.output))
            except subprocess.TimeoutExpired:
                print('run .bat file error, because of timeout')
    
        @staticmethod
        def run_command(command):
            try:
                # 超时为3秒
                s = subprocess.check_output(command,
                                            shell=True,
                                            universal_newlines=True,
                                            stderr=subprocess.STDOUT,
                                            timeout=3)
            except subprocess.CalledProcessError as ex:
                s = ex.output
            except subprocess.TimeoutExpired:
                s = "TimeOut"
            return s
    
    
    if __name__ == '__main__':
        print("TCP server is start !")
        TcpServer()
    
    

 

recv不阻塞,以接收或超时结束 

  • recv默认是阻塞的,但是可以设置为不阻塞socket.setblocking(0),可以recv前设置一个sleep时间模拟等待,但是sleep后再recv如果还是接收不到值,会抛出BlockingIOError,在有些特殊的业务场景可能会适用
    import socket
    import time
    
    address = ("0.0.0.0", 12345)
    
    
    def tcp_server():
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind(address)
        server_socket.listen(5)
        print("TcpServer开始监听:{}".format(address))
        while True:
            client_socket, client_address = server_socket.accept()
            client_socket.setblocking(0)
            time.sleep(2)
            try:
                msg = client_socket.recv(1024).decode()
            except BlockingIOError as e:
                msg = e
            print(client_address, ":", msg)
            client_socket.close()
    
    
    if __name__ == '__main__':
        tcp_server()
    

     

你可能感兴趣的:(python)