BSD Sockets Compatibility

by Warren Young

So you say you’re a long-time Unix hacker who’s new to Windows
programming? And you’ve heard of this great API called Winsock that’s
compatibile with your beloved BSD sockets, but try as you might, you
just can’t find the readv() call? Well bunky, this is the article for
you.

Introduction

In the beginning, there was chaos in the world of Windows TCP/IP APIs. A
program written for, say, FTP Software’s TCP/IP stack wouldn’t run on
JSB’s stack.

Then, sometime in 1990, a bunch of people got together and decided to
make one nice, big, compatible API called Windows Sockets that would
allow a single program to run on any vendor’s stack. They decided to
base this API on the popular BSD sockets model of network programming,
but for various reasons, there are still many differences between
Winsock and BSD sockets. This article points out how Winsock differs
from BSD sockets, and how to translate BSD sockets programs to use
similar Winsock functionality.

The Official Word

The Winsock API documentation has a section called Porting Socket
Applications to
Winsock
that covers many of the same issues that this article does, and a few
others besides.

#include Differences

Under BSD sockets, there are quite a few different header files you need
to include, depending on what sockets calls you use. A typical BSD
sockets program has a block of #includes near the top like this:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

For Winsock, you don’t need any of these. Instead, you just need to
include winsock.h. (Or, if you need Winsock 2-specific functionality,
winsock2.h.)

errno vs. WSAGetLastError()

WSAGetLastError() is essentially the same thing as Unix’s errno global
variable. The error constants and their values are different; there’s a
table in the Winsock spec where it lists all the error constants, one
column of which shows the equivalent BSD error constant for a given
Winsock error constant. Usually the difference is just the addition of
“WSA” to the beginning of the constant name for the Winsock versions.
(E.g. WSAEINTR is the Winsock version of BSD’s EINTR error constant.)

Another thing to keep in mind is that, although the perror() call exists
in most Windows compilers’ run-time libraries, it doesn’t work for
Winsock calls. (This is a consequence of Winsock not returning its error
codes in the errno variable.) There is a function called
WSAGetLastErrorMessage() in the basic Winsock
examples area
of the FAQ that you can use to build a perror()-like function. It’s in
the ws-util.cpp module.

EAGAIN

Many Unix programs, especially those with System V roots, check for the
EAGAIN value in the global errno variable when a non-blocking call
fails. This is the same thing as BSD’s EWOULDBLOCK and Winsock’s
WSAEWOULDBLOCK errors. You’ll have to check your system’s header files,
but all Unixes I’ve checked on this matter #define EAGAIN and
EWOULDBLOCK to the same value, so you may want to get into the habit of
using EWOULDBLOCK instead of EAGAIN under Unix, to make transitions to
and from Winsock easier.

Equivalence of File and Socket Handles

Under Unix, the I/O system calls work with file descriptors and socket
descriptors equally well. It’s common in BSD sockets programs to use
read() instead of recv() to read data from a socket, for example. Under
Windows 3.1 and Windows 95 derivatives, socket descriptors are
completely distinct from file descriptors. Although the run-time library
(RTL) supplied with most Windows compilers includes POSIX emulation
functions, these are designed to work only with files, not sockets.

Windows NT derivatives are more like Unix, in that its native file I/O
functions also work with sockets: ReadFile() is roughly equivalent to
Winsock’s recv() function, for example.

The Visual C++ RTL emulates POSIX functions, except that they’re named
with a leading underscore: for example, _read() instead of read(). The
_read() function uses ReadFile() internally, so you’d think it would
work with sockets. The problem is, the first argument is an RTL-specific
handle, not an operating system file handle. If you pass a socket handle
to _read() or _write(), the RTL will realize that it isn’t an RTL
handle and the call will fail.

Fortunately, there is a bridge function in Visual C++’s RTL:
_open_osfhandle(). (If you’re not using Visual C++, you’ll have to
check its RTL source for a similar function.) I’ve not tried it, but it
appears to take an operating system file handle (including socket
handles) and return a handle you can use with the POSIX emulation
functions in the RTL. I’m told that this will work with sanely-coded
non-Microsoft Winsock stacks, but since I haven’t tried it, you should
if you want to support these alternate stacks.

Again, the _open_osfhandle() workaround only works on Windows NT
derivatives, because the file I/O functions don’t work with sockets on
the Windows 95 derivatives, so fooling the RTL into accepting one
doesn’t help you.

If these limitations are too much for your program, you may want to give
one of the Unix emulation systems a try. I’ve personally used
Cygwin for this purpose, and it works beautifully.
Others include Microsoft’s Services for
Unix and AT&T’s
UWIN, but I’ve not used
either of them.

All that aside, it’s usually much easier to rewrite your program to use
portable functions like recv() than it is to arm-twist the Windows port
to work with Unix idioms.

Winsock’s closesocket() vs. Unix’s close()

Winsock defines a different function for closing sockets because not all
versions of Windows have file descriptor and socket descriptor
equivalency like Unix. See the discussion in the previous item for more
on the file/socket handle mismatch issue.

Winsock’s ioctlsocket() vs. Unix’s ioctl()

Unix provides the ioctl() call to allow you to set and get various bits
of info on a file descriptor, which includes socket descriptors. Winsock
replicates some common Unix ioctls in the ioctlsocket() call, but much
is missing.

If you use the SIOCGIFCONF ioctl on Unix to get information about the
system’s network interfaces, Winsock 2 provides very similar
functionality with its SIO_GET_INTERFACE_LIST option for
ioctlsocket().

fcntl()

The Unix fcntl() call has no direct equivalent under Winsock. Where
necessary, similar functionality exists in Winsock’s ioctlsocket() call.
For example, the equivalent of using Unix’s fcntl() to set a socket’s
O_NONBLOCK flag is setting the FIONBIO flag with Winsock’s
ioctlsocket().

poll()

There are several wrappers for poll() using select() out there. Here’s
one.
It doesn’t attempt to implement any of the special poll() features found
in a true System V system, such as STREAMS support. Also, the code is
rather old, written in a K&R C style that some newer compilers might
reject. Finally, since it is built directly on top of select(), it has
the same
limitations.

Another option is to dig the implementation of poll() out of Jarle
Aasa’s Win32 port of the adns library. This
implementation has three limitations: 1) It’s
GPL’d, which means you can’t use
the code in your program unless your program is also licensed under the
GPL; 2) it’s built on the Win32 event object mechanism, which has a
hard 64-object
limitation; and
3) it is reportedly not written in a way that is easy for third-party
programmers to extract and use.

readv() and writev()

Winsock 2’s overlapped I/O mechanism includes scatter/gather
functionality similar to that provided by readv() and writev().

dup()

The Unix dup() function duplicates a file handle, and of course also
works for sockets. Under Winsock 2, you can do the same thing with
WSADuplicateSocket(). It’s a bit more involved, but the
WSADuplicateSocket() documentation in MSDN
has a good step-by-step example showing how to use this mechansim.

dup2()

There is partial support for this feature under Winsock, though the
mechanism is dissimilar to the dup2() feature. Under Unix, dup2() takes
a handle and duplicates it like dup() does, but with a twist: it assigns
the new filehandle a value that you specify. This is usually used to map
a socket to the C language’s stdin or stdout file descriptors so that
you can use standard I/O functions like printf() and fgets() with the
socket.

Item KB190351 in the Microsoft
Knowledge Base documents a method by which you can redirect a child
process’s standard descriptors to a socket. The limitations are that you
cannot do this to your own process’s descriptors, you cannot redirect
arbitrary descriptors to a socket (i.e. you can only do it with stdin,
stdout and stderr), and not all processes are fully compatible with this
API feature. Still, it at least makes an inetd-like program possible
under Win32.

Detecting a Dropped Connection

Under BSD Unixes, if the remote peer closes its connection and your
program is blocking on recv(), you will get a 0 back from recv().
Winsock behaves the same way, except that it can also return -1, with
WSAGetLastError() returning WSAECONNRESET, WSAECONNABORTED or
WSAESHUTDOWN, to signal the detectable flavors of abnormal
disconnections.

Under Unix, if you’re blocking on send() and your program is ignoring
the SIGPIPE signal, it will return with a -1 when the remote peer
disconnects, and errno will be EPIPE. Otherwise, your program will be
sent the SIGPIPE signal, which will terminate your program if you don’t
handle it. Under Winsock, the SIGPIPE/EPIPE functionality does not exist
at all: send() will either return 0 for a normal disconnect or -1 for an
abnormal disconnect, with WSAGetLastError() returning the same errors as
in the recv() discussion above.

UDP Behavior

According to Ilpo Ruotsalainen, “…most BSD socket implementations do
not pass delayed UDP errors (ICMP port unreachable at least, maybe
others too) to recvfrom() while Winsock 2 [under Windows 2000 but not
Windows 98] does. Linux [behaves like Windows 2000] too, but provives
SO_BSDCOMPAT setsockopt() for being compatible with the BSD style.”

In other words, a portable program has to be prepared for the
possibility of error codes for non-immediate problems from recvfrom(),
but it can’t depend on receiving them.