<<

Winsock Programmer's FAQ
Section 7: Articles

>>

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.


<< Debugging TCP WsControl() Revealed >>
Last modified on 4 July 2000 at 14:12 UTC-7 Please send corrections to tangent@cyberport.com.
< Go to the main FAQ page
<< Go to my Programming pages
<<< Go to my Home Page