Winsock's Compatibility With BSD Sockets
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 pardner, this is the article
for you.
Introduction
In the beginning, there was chaos. This chaos was in the form of
incompatible 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 the
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.
This article is still evolving. Each entry below has an "added" date
and an optional "modified" date. So, you can periodically come back to
this document to see if any new BSD-like behaviors have been uncovered
in the Winsock API. Changes to this document will be announced on the
main Winsock Programmer's FAQ page, in the What's New section.
The Official Word (Added
2000.06.12)
The Winsock spec has a section called Deviation
from Berkeley Sockets that covers many of the same issues that this
article does, and a few others besides.
#include Differences (Added
2000.06.12)
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() (Added 1998.12.24,
modified 2000.06.12)
WSAGetLastError() is essentially the same thing as Unix's
errno global variable. The error constants and their
values are different, of course; 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 using 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 (Added
1998.12.24)
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
(Added 1998.08.01, modified 2000.06.12)
Under Unix, the I/O system calls work with file handles and socket
handles 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 9x, socket handles are completely
distinct from file handles. 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/2000 is 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/2000, because the Win9x file I/O functions don't work with sockets,
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 UWin, Interix, MainWin and
Wind/U, but I've not used any of them.
All that aside, it's usually much easier to rewrite your programs to
use portable functions like recv() rather than Unixy ones like
read() than it is to arm-twist the Windows port to work with
Unix idioms.
poll() (Added
2000.07.04)
There is an implementation of poll() for Winsock in
Jarle Aasa's Win32 port of
the adns library. This implementation has two 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; and 2) it's built on
the Win32 event object mechanism, which has the surprising limitation of only being able to
handle 64 sockets at one time. See the FAQ item for details.
Winsock's closesocket() vs. BSD's
close() (Added 2000.06.12)
closesocket() under Winsock closes a socket descriptor.
BSD's close() system call closes a handle of some sort, which
may be a socket descriptor. This difference in meaning required
a different function name. See the discussion in the previous item for
more on the file/socket handle mismatch issue.
Winsock's ioctlsocket() vs. BSD's
ioctl() (Added 1998.08.01)
BSD Unix (and other flavors of Unix) provides the ioctl()
call to allow you to set and get various bits of info on a socket or
file handle. Winsock replicates much of this in its ioctlsocket()
call, but not everything is present. Here's a list of equivalencies
(more are welcome!):
SIOCGIFCONF This ioctl()
option allows you to get information about the network interfaces
available. Winsock 2 provides very similar functionality with its
SIO_GET_INTERFACE_LIST option for ioctlsocket() .
fcntl() (Added
2000.06.12)
The Unix fcntl() call has no direct equivalent under
Winsock. Where necessary, similar functionality exists in Winsock's
ioctlsocket() call.
Detecting a Dropped Connection (Added 1999.09.25, modified 2000.06.12)
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.
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.
readv() and writev() (Added 1998.08.01)
BSD sockets' scatter/gather mechanism is very similar to parts of
Winsock 2's overlapped I/O mechanism. Overlapped I/O allows you to pass
several buffers to WSASend() for "gathering" before sending on the
network, and allow incoming data to be "scattered" into several input
buffers by WSARecv() .
dup() (Added
1998.12.24)
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 Winsock
spec gives you a canned function call sequence you can use.
dup2() (Added
1998.12.24, modified 1999.06.27)
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
handles so that you can use standard I/O functions like printf()
and fgets() with the socket.
Item Q190351 in the Microsoft Knowledge Base documents
a method by which you can redirect a child process's standard handles
to a socket. The limitations are that you cannot do this to your own
process's handles, you cannot redirect arbitrary handles 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.
A Cry For Updates
This article can always use more work. As it is, it gives a few good
bits of info about BSD-like behavior under Winsock, but there are still
many, many holes left to fill. If you can help me out, please drop me
a line! In particular, I'll bet that there are several other BSD-only
ioctl() options that we can duplicate in other ways with Win32
facilities.
Copyright © 1998-2000 by Warren Young. All rights
reserved.
|