Differences Between Windows and Unix Non-Blocking Sockets
While working on Twisted's networking, I discovered a series of differences between socket APIs on Windows and Unix/Linux. This page is intended to document them for the benefit of those who are developing their own networking code. Some caveats:
- This document only discusses non-blocking sockets.
- I am not an expert on Unix, Windows or networking. On the other hand Twisted has an extensive set of tests and runs numerous real programs on both platforms.
- These results were based on Python's behavior, as is my terminology. Since Python wraps the C APIs in a very lightweight way, and I haven't seen any differences between its behavior and MSDN and Unix documentation, this information is almost certainly valid for C networking code as well.
My main sources of information were the relevant MSDN pages, Volume 1 of Unix Network Programming (2nd Edition), and experimentation and results from running systems. I recommend all three for verifying this information.
For more differences see Warren Young's BSD Sockets Compatibility page.
select(2)limited to 64 FDs on Windows
- In order to fix this, you can do
#define FD_SETSIZE 512(or some larger number if you wish). Python does this for you already.
- WSA errnos
- Socket APIs will return WSA errnos, rather than the standard ones (e.g. WSAEWOULDBLOCK, although Windows also has EWOULDBLOCK). Except when they don't. Refer to MSDN.
select(2)doesn't block on empty lists of fds
- If you don't pass any socket fds to
select(2)on Unix, it will block for the specified timeout. On Windows, it will return immediately with an error. This may be a Python oddity.
- Peculiar errnos from
- I've seen errnos 0 and 2 returned on Win32 for no reason I understood, apparently when passing empty read or write list.
- Detecting failed connects is done using exception list of
- On Unix, a failed connect will return an error upon reading or writing from the socket, and select() will mark the socket as notified. The exception list in
select(2)is used for detecting OOB data. On Windows, not the read or write lists but rather the exception list is used for detecting failed connects.
- On Windows failed connects, reads and writes still return WSAEWOULDBLOCK
- Reads or writes to the socket will return a WSAEWOULDBLOCK even if connection failed. The errno is available via getsockopt with SO_ERROR.
- WSAEINVAL is equivalent to WSAEWOULDBLOCK for
- You need to treat both as equivalent on Windows. (This makes me suspicious after rereading issue before previous, might be untrue and I screwed up somewhere - verify this sometime.)
- SO_REUSEADDR is weird
- On Windows it lets you bind to the same TCP port multiple times without errors. Verified experimentally by failing test, I never bothered to check out MSDN docs for this one, I just don't use it on Windows.
- UDP blocking on recvfrom returns WSAECONNRESET for connection refused
- On Unix you get ECONNREFUSED.
- Differences in partial writes to TCP sockets (contributed by James Knight).
- In Unix, socket.send(buf) will buffer as much of buf as it has space
for, and then return how much it accepted. This could be 0 or up to
something around 128K. If you send some data and then some more, it
will append to the previous buffer.
In Windows, socket.send(buf) will either accept the entire buffer or raise ENOBUFS. Testing indicates that it will internally buffer any amount up to 50MB (this seems to be the total for either the process or the OS, I'm not sure). However, it will not incrementally accept more data to append to a socket's buffer until the big buffer has been completely emptied (seemingly down to the SO_SNDBUF length, which is 8192), but rather raises WSAEWOULDBLOCK instead.