【问题标题】:can't close socket on KeyboardInterrupt无法在 KeyboardInterrupt 上关闭套接字
【发布时间】:2016-04-24 14:18:02
【问题描述】:
from socket import socket, AF_INET, SOCK_STREAM

sock = socket(AF_INET, SOCK_STREAM)
sock.bind(("localhost", 7777))
sock.listen(1)
while True:
    try:
        connection, address = sock.accept()
        print("connected from " + address)
        received_message = sock.recv(300)
        if not received_message:
            break
        connection.sendall(b"hello")

    except KeyBoardInterrupt:
        connection.close()

所以我试图将我的头绕在套接字周围并拥有这个非常简单的脚本 但由于某种原因,我无法用 KeyboardInterrupt 杀死这个脚本

我如何用KeyboardInterrupt 杀死脚本,为什么我不能用KeyboardInterrupt 杀死它?

【问题讨论】:

    标签: python sockets


    【解决方案1】:
    1. break 退出while 循环。没有break,循环不会结束。
    2. 为安全起见,请检查是否设置了connection

    from socket import socket, AF_INET, SOCK_STREAM
    
    sock = socket(AF_INET, SOCK_STREAM)
    sock.bind(("localhost", 7777))
    sock.listen(1)
    while True:
        connection = None # <---
        try:
            connection, address = sock.accept()
            print("connected from ", address)
            received_message = connection.recv(300)
            if not received_message:
                break
            connection.sendall(b"hello")
        except KeyboardInterrupt:
            if connection:  # <---
                connection.close()
            break  # <---
    

    更新

    • 有一个错字:KeyBoardInterrupt 应该是 KeyboardInterrupt
    • sock.recv 应该是 connection.recv

    【讨论】:

    • 这很奇怪,因为。运行它仍然不会使用 CTRL-C 关闭脚本
    • 因为我在windows上使用这个是键盘中断没有被触发的原因吗?
    • @Zion,我在 Linux 上尝试过,它成功了。 asciinema.org/a/dsatnomhvpvvl3skdunbpinov
    • @Zion,Ctrl + Break 怎么样?
    • 谢谢伙计。我猜它是一个窗户的东西。我在 OS X 上试了一下,效果很好。
    【解决方案2】:

    尝试使用timeout,让程序周期性地从accept等待进程接收KeyboardInterrupt命令中“跳出”。

    这里是一个套接字服务器的例子:

    import socket
    
    host = "127.0.0.1"
    port = 23333
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    sock.bind((host,port))
    sock.listen()
    
    sock.settimeout(0.5)
    
    print("> Listening {}:{} ...".format(host,port))
    
    try:
        while True:
            try:
                conn, addr = sock.accept()
                data = conn.recv(1024)
                if not data:
                    print("x Client disconnected!")
                    break
                else:
                    print("> Message from client: {}".format(data.decode()))
                    msg = "> Message from server".format(data.decode()).encode()
                    conn.sendall(msg)
            except socket.timeout:
                print("Timeout")
            except KeyboardInterrupt:
                pass
    except KeyboardInterrupt:
        print("Server closed with KeyboardInterrupt!")
    

    【讨论】:

      【解决方案3】:

      尝试向套接字添加超时,如下所示:

      from socket import socket, AF_INET, SOCK_STREAM
      sock = socket(AF_INET, SOCK_STREAM)
      sock.bind(("localhost", 7777))
      sock.settimeout(1.0)
      sock.listen(1)
      while True:
          try:
              connection, address = sock.accept()
              print("connected from " + address)
              received_message = sock.recv(300)
              if not received_message:
                  break
              connection.sendall(b"hello")
          except IOError as msg:
              print(msg)
              continue    
          except KeyboardInterrupt:
              try:
                  if connection:
                      connection.close()
              except: pass
              break
      sock.shutdown
      sock.close()
      

      【讨论】:

      • 如果我在 Windows 上会有所不同,因为在 Mac 上执行此操作会捕获键盘中断吗?
      • 我不能告诉你,因为我在 Linux 上,试试看:)
      • 您也可以通过使用与@987654324 匹配的try 将其用于仅发送设置(即不执行任何sock.recv(),因为您将TCP 用于本质上是UDP) @ 仅在 sock.accept() 行周围,然后根据需要使用 except KeyboardInterrupt: 外部块。
      【解决方案4】:

      我在 Windows 上遇到了这个问题。以下是我处理停止进程的方法:

          try:
              while self.running:
                  try:
                      c, addr = self.socket.accept()
                      print("Connection accepted from " + repr(addr[1]))
                      # do special stuff here...
                      print("sending...")
                      continue
                  except (SystemExit, KeyboardInterrupt):
                      print("Exiting....")
                      service.stop_service()
                      break
                  except Exception as ex:
                      print("======> Fatal Error....\n" + str(ex))
                      print(traceback.format_exc())
                      self.running = False
                      service.stop_service()
                      raise
          except (SystemExit, KeyboardInterrupt):
              print("Force Exiting....")
              service.stop_service()
              raise
      
      def stop_service(self):
          """
          properly kills the process: https://stackoverflow.com/a/16736227/4225229
          """
          self.running = False
          socket.socket(socket.AF_INET,
                        socket.SOCK_STREAM).connect((self.hostname, self.port))
          self.socket.close()
      

      注意,为了触发 KeyboardInterrupt 异常,使用:

      Ctrl+Fn+PageUp(Pause/Break)

      【讨论】:

        【解决方案5】:

        对于 Windows 用户,

        以上试图捕捉键盘中断的解决方案似乎不起作用。我最终在我的套接字上设置了超时。

        类似: server_socket.settimeout(10)

        这里在 10 秒不活动后引发异常(例如 10 秒内没有收到任何东西)

        【讨论】:

          【解决方案6】:

          如果远端很少发送数据,您也应该为连接设置超时。 这种情况下连接会引发超时异常,此时KeyboardInterrupt可以被检查。

          from socket import socket, AF_INET, SOCK_STREAM
          sock = socket(AF_INET, SOCK_STREAM)
          sock.bind(("localhost", 7777))
          sock.settimeout(1.0)
          sock.listen(1)
          while True:
          try:
              connection, address = sock.accept()
              connection.settimeout(1.0)
              print("connected from " + address)
              received_message = sock.recv(300)
              if not received_message:
                  break
              connection.sendall(b"hello")
          except socket.timeout:
              continue
          except IOError as msg:
              print(msg)
              continue
          except KeyboardInterrupt:
              try:
                  if connection:
                      connection.close()
              except: pass
              break
          sock.shutdown
          sock.close()
          

          【讨论】:

            【解决方案7】:

            CTRL+C 事件可以在一个单独的进程中被捕获并发送回另一个在主进程中运行的线程以终止套接字。下面的示例,使用 Python 3.5.4 在 Windows 10 上成功测试。放置了一些 cmets 和 print 语句,以便您查看发生了什么。

            from multiprocessing import Pipe, Process
            from socket import socket, AF_INET, SOCK_STREAM
            from threading import Thread
            import time
            
            def detect_interrupt(conn):
                try:
                    print("Listening for KeyboardInterrupt...")
                    while True:
                        time.sleep(1)
                except KeyboardInterrupt:
                    print("Detected KeyboardInterrupt!")
                    print("Sending IPC...")
                    conn.send(True)
                    conn.close()
            
            def listen_for_interrupt(conn, sock):
                print("Listening for IPC...")
                conn.recv()
                print("Detected IPC!")
                print("Closing sock...")
                sock.close()
            
            if __name__ == "__main__":
            
                sock = socket(AF_INET, SOCK_STREAM)
                sock.bind(("localhost", 7777))
                sock.listen(1)
            
                # Crate a Pipe for interprocess communication
                main_conn, detect_conn = Pipe()
                # Create a thread in main process to listen on connection
                listen_for_interrupt_thread = Thread(
                    target=listen_for_interrupt, args=(main_conn, sock), daemon=True)
                listen_for_interrupt_thread.start()
                # Create a separate process to detect the KeyboardInterrupt
                detect_interrupt_process = Process(
                    target=detect_interrupt, args=(detect_conn,))
                detect_interrupt_process.start()
            
                connection = None
                try:
                    while True:
                        print("Running socket accept()")
                        connection, address = sock.accept()
                        print("connected from " + address)
                        received_message = sock.recv(300)
                        if not received_message:
                            break
                        connection.sendall(b"hello")
                except KeyboardInterrupt:
                    print("Handling KeyboardInterrupt")
                    sock.close()
                    if connection:
                        connection.close()
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-08-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-12-21
              • 1970-01-01
              相关资源
              最近更新 更多