首页 文章

Windows上的python select.select()

提问于
浏览
4

我正在使用here中的代码测试UDP打孔 . 它适用于Linux,但在Windows上报告错误 . 这是发生错误的代码片段:

while True:
    rfds, _, _ = select([0, sockfd], [], [])  # sockfd is a socket
    if 0 in rfds:
        data = sys.stdin.readline()
        if not data:
            break
        sockfd.sendto(data, target)
    elif sockfd in rfds:
        data, addr = sockfd.recvfrom(1024)
        sys.stdout.write(data)

和错误消息:

Traceback (most recent call last):
  File "udp_punch_client.py", line 64, in <module>
    main()
  File "udp_punch_client.py", line 50, in main
    rfds, _, _ = select([0, sockfd], [], [])
select.error: (10038, '')

我知道这个错误与Windows上的 select 实现有一些关系,每个人都引用这个:

注意Windows上的文件对象是不可接受的,但是套接字是 . 在Windows上,底层的select()函数由WinSock库提供,不处理不是源自WinSock的文件描述符 .

所以我有两个问题:

  • [0, sockfd] 中的 0 是什么意思?这是一种经常使用的技术吗?

  • 如果 select 仅适用于Windows上的 socket ,如何使代码与Windows兼容?

谢谢 .

2 回答

  • 2

    正如答案所示,我创建另一个线程来处理输入流,它的工作原理 . 这是修改后的代码:

    sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    def send_msg(sock):
        while True:
            data = sys.stdin.readline()
            sock.sendto(data, target)
    
    def recv_msg(sock):
        while True:
            data, addr = sock.recvfrom(1024)
            sys.stdout.write(data)
    
    Thread(target=send_msg, args=(sock_send,)).start()  
    Thread(target=recv_msg, args=(sockfd,)).start()
    
  • 4

    遗憾的是, select 无法帮助您在一个线程中处理 stdin 和网络事件,因为 select 无法在Windows上使用流 . 你需要的是一种无阻塞地读取 stdin 的方法 . 你可以使用:

    • stdin 的额外线程 . 这应该工作正常,是最简单的工作方式 . 如果您需要的只是等待I / O事件,Python线程支持是完全可以的 .

    • 类似于geventgreenlet机制,它修补了线程支持和标准库的大多数I / O函数,以防止它们阻塞greenlet . 还有像 twisted 这样的库(参见注释)提供了非阻塞文件I / O.这种方式是最一致的,但它应该要求使用与您的框架匹配的样式来编写整个应用程序( twistedgevent ,差异并不重要) . 但是,我怀疑 twisted 包装器无法在Windows上从 stdin 进行异步输入(非常确定它们可以在* nix上执行此操作,因为它们可能使用相同的 select ) .

    • 其他一些技巧 . 然而,大多数可能的技巧都相当丑陋 .

相关问题