【问题标题】:python select.select() on WindowsWindows 上的 python select.select()
【发布时间】:2014-04-10 16:50:34
【问题描述】:

我正在使用来自here 的代码测试 UDP 冲孔。它适用于 Linux,但在 Windows 上报告错误。这是发生错误的代码sn-p:

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。

所以我有两个问题:

  1. [0, sockfd] 中的0 是什么意思?这是某种常用的技术吗?
  2. 如果select在Windows上只能和socket一起工作,如何使代码与Windows兼容?

谢谢。

【问题讨论】:

  • 0fdstdinFile Descriptors
  • @tmr232 知道如何修改代码吗?
  • @J.F.Sebastian 好的

标签: python windows sockets select


【解决方案1】:

不幸的是,select 无法帮助您在一个线程中处理stdin 和网络事件,因为select 无法在 Windows 上处理流。您需要的是一种在不阻塞的情况下阅读stdin 的方法。您可以使用:

  1. stdin 的额外线程。这应该可以正常工作,并且是完成这项工作的最简单方法。如果您只需要等待 I/O 事件,那么 Python 线程支持是相当不错的。
  2. 一种类似于greenlet 的机制,类似于gevent,它修补线程支持和标准库的大部分I/O 函数,以防止它们阻塞greenlets。还有像twisted(见cmets)这样的库提供非阻塞文件I/O。这种方式是最一致的一种,但它应该要求使用与您的框架匹配的样式来编写整个应用程序(twistedgevent,差异不大)。但是,我怀疑 twisted 包装器无法在 Windows 上从 stdin 异步输入(很确定他们可以在 *nix 上做到这一点,因为他们可能使用相同的 select)。
  3. 其他一些技巧。但是,大多数可能的技巧都相当丑陋。

【讨论】:

  • 我会尽快接受你的回答。
  • +1 线程可能是 Windows 上最简单的路线。还有twistedasyncio(郁金香)
  • 应该看asyncio,谢谢。至于twisted,它是一个优秀的库(我一般使用twistedgevent,两者都喜欢)。是的,它具有类文件对象的包装器。我真的认为使用这种框架是最好的解决方案。然而,这不仅仅是对现有代码的小修复。
  • 糟糕。 Twisted wrapper 似乎适用于 POSIX 文件描述符。可能它可能无法在 Windows 上等待标准输入。
  • 对应的扭虫是7岁,这里是the patch。这是asyncio and stdio recipe
【解决方案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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-12
    • 2016-03-10
    • 2012-07-20
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多