Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python select.select() on Windows

I'm testing UDP punching using code from here. It works on Linux however reports error on Windows. Here's the code snippet where the error occurs:

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)

And error msg:

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, '')

I know this error has some thing to do with the select implementation on Windows, and everyone quote this:

Note File objects on Windows are not acceptable, but sockets are. On Windows, the underlying select() function is provided by the WinSock library, and does not handle file descriptors that don’t originate from WinSock.

So I got two questions:

  1. What does 0 in [0, sockfd] mean? Is this some sort often-used technique?
  2. If select only works with socket on Windows, How to make the code Windows compatible?

Thank you.

like image 939
laike9m Avatar asked Mar 07 '14 13:03

laike9m


People also ask

What is select () in Python?

Python's select() function is a direct interface to the underlying operating system implementation. It monitors sockets, open files, and pipes (anything with a fileno() method that returns a valid file descriptor) until they become readable or writable, or a communication error occurs.

What is select module?

This select module, shown in Example 7-6, allows you to check for incoming data on one or more sockets, pipes, or other compatible stream objects.

What is the difference between select and poll in polling method?

select() only uses (at maximum) three bits of data per file descriptor, while poll() typically uses 64 bits per file descriptor. In each syscall invoke poll() thus needs to copy a lot more over to kernel space.


2 Answers

Unfortunately, select will not help you to process stdin and network events in one thread, as select can't work with streams on Windows. What you need is a way to read stdin without blocking. You may use:

  1. An extra thread for stdin. That should work fine and be the easiest way to do the job. Python threads support is quite ok if what you need is just waiting for I/O events.
  2. A greenlet-like mechanism like in gevent that patches threads support and most of I/O functions of the standard library to prevent them from blocking the greenlets. There also are libraries like twisted (see the comments) that offer non-blocking file I/O. This way is the most consistent one, but it should require to write the whole application using a style that matches your framework (twisted or gevent, the difference is not significant). However, I suspect twisted wrappers are not capable of async input from stdin on Windows (quite sure they can do that on *nix, as probably they use the same select).
  3. Some other trick. However, most of the possible tricks are rather ugly.
like image 100
Ellioh Avatar answered Sep 30 '22 20:09

Ellioh


As the answer suggests, I create another thread to handle input stream and it works. Here's the modified code:

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()
like image 39
laike9m Avatar answered Sep 30 '22 20:09

laike9m