Winsock Programmer's FAQ: Intermediate Winsock Issues
<<

Winsock Programmer's FAQ
Section 3: Intermediate Winsock Issues

>>
3.1 - How do I speak { HTTP, POP3, SMTP, FTP, Telnet, NNTP, etc. } with Winsock?

Winsock proper does not provide a way for you to speak these protocols, because it only deals with the layers underneath these application-level protocols. However, there are many ways for you to get your program to speak these protocols.

Perhaps the easiest method is to use a third-party library. The Resources section lists several of these.

Another common method is to use the WinInet library exposed by Microsoft's Internet Explorer. This offers easy access to the HTTP, FTP and Gopher protocols. Newer versions of Microsoft's development tools include classes and controls to access this mechanism.

Finally, you can always roll your own. You should start by reading the specifications for the protocol you want to implement. Most of the Internet's protocols are documented in RFCs. The Important RFCs page links to the most commonly referenced application-level RFCs. As far as standards documents go, RFCs are generally clearly-written. Many of the standard protocols are intrinsically simple: they don't require much work to implement. Others may require more work to implement than you may be prepared for.

3.2 - How do I get my IP address from within a Winsock program?

There are three methods, which each have advantages and disadvantages:

  1. To get the local IP address if you already have a bound or connected socket, just call getsockname() on the socket.
  2. To get your address without opening a socket first, do a gethostbyname() on the value gethostname() returns. This will return a list of all the host's interfaces, as shown in this example. (See the example page for problems with the method.)
  3. The third method only works on Winsock 2. The new WSAIoctl() API supports the SIO_GET_INTERFACE_LIST option, and one of the bits of information returned is the addresses of each of the network interfaces in the system. [C++ Example] (Again, see the example page for caveats.)
3.3 - What's the proper way to impose a packet scheme on a stream protocol like TCP?

The two most common methods are delimiters and length-prefixing.

An example of delimiters is separating packets with, say, a caret (^). Naturally your delimiter must never occur in regular data, or you must have some way of "escaping" delimiter characters.

An example of length-prefixing is prepending a two-byte integer containing the packet length on every packet. See the FAQ article How to Use TCP Effectively for the proper way to send integers over the network.

There are hybrid methods, too. The HTTP protocol, for example, separates header lines with CRLF pairs (a kind of delimiting), but when an HTTP reply contains a block of binary data, the sever also sends the Content-length header before it sends the data, which is a kind of length-prefixing.

I favor simple length-prefixing, because as soon as you read the length prefix, you know how many more bytes to expect. By contrast, delimiters require that you blindly read until you find the end of the packet.

3.4 - How do I write my program to work through a firewall?

Most firewalls support the SOCKS protocol for this purpose, either version 4 or version 5. SOCKS allows a client machine on a protected internal network to ask the firewall to act as a relay between it and an Internet host. SOCKS version 5 adds UDP support, as well as some advanced features like end-to-end encryption and secure logins. It's overkill for most applications, but some servers require that you use a SOCKS5-compatible login to get through.

You can find out more about SOCKS at the NEC SOCKS site and at Stardust's SOCKS Summit 98 site. You may also want to read the SOCKS5 RFC. Note that a clarified version of this RFC is due out soon; I'll update this Question as soon as I find out about it.

The NEC site has a good and free "SOCKSifier" called SOCKSCap that turns almost any Winsock program into a SOCKS-compliant program, so you may not need to modify your app. (There are other vendors that offer SOCKSifiers, and a few non-Microsoft Winsock stacks are SOCKSified. Check your stack's documentation to see if it supports SOCKS natively.) The SOCKSifier method's downside is that your users must set up SOCKSCap and that they must run your program through the SOCKSifier. Thus, it's more user-friendly to implement the SOCKS protocol directly.

There is some BSD sockets client-side SOCKS code at the NEC site that you can easily modify to work with Winsock. This is included as part of the Unix SOCKS server package as a library that the included utility programs (rtelnet, rping, etc.) all link to.

Note that SOCKS inherently has trouble with some protocols because of the way they operate. Examples of this are RealAudio and almost any "multiuser" protocol, such as multiplayer online games and conferencing programs. These programs require some kind of intelligent proxy that understands the protocol. Unfortunately, these types of proxies are only available for well-established protocols like RealAudio.

3.5 - I'm writing a server. What's a good network port to use?

If you're writing a server for an existing, popular Internet protocol, it's already got a port number assigned to it. You can find the most popular of these numbers at the website for the Internet Assigned Numbers Authority (IANA).

If you're writing a server for a new protocol, there are a few rules and suggestions you should obey when choosing your server's port:

  1. Ports 1-1023 are off-limits to people inventing new protocols. They are reserved by the IANA for new "standard" protocols. Important protocols like POP3 and HTTP have low numbers (110 and 80, respectively), but your new K-RAD game server shouldn't. Note that id Software is going to Hell for using port 666 with their DOOM network server. They cleaned up their act with Quake, though.
  2. Ports 1024 through 49151 are Registered Ports, which are a good range to choose your ports from. Just beware that the entire world is choosing from ports in this range, so it may make sense for you to register your port, or at least check the current list of assigned ports. Just be aware that no one is obligated to check that list before they make up their app's port number.
  3. Ports 49152 through 65535 are Dynamic Ports, meaning that operating systems use ports in this range when choosing random ports. (The FTP protocol, for example, uses random ports in the data transfer phase.) This is a poor range to choose ports from, because there's a fairly decent chance that your program and the OS will fight over a given port eventually.
  4. Even within the "safe" 1024-49151 range, there are many numbers not listed as already taken in the IANA's assigned numbers list. Of these, you should avoid port numbers with patterns to them, or a widely-recognized meaning. People tend to pick these since they're easy to remember, but this increases the chances of a collision. Ports 1234, 5150 and 22222 are bad choices, for example.

You should also give some thought to making your program's port configurable, in case your program is run on a machine where another server is already using that port. One way to do this is through Winsock's getservbyname() function: if that function returns a port number, use that, otherwise use the default port number. Then users can change your program's port by editing the SERVICES file, located in %WINSYSDIR%\DRIVERS\ETC on Windows NT/2000 systems and c:\Windows\ on Win9x machines.

3.6 - I'm having trouble porting a BSD sockets program to Winsock. Help!

There's an article in the FAQ that covers this issue in some detail.

3.7 - How do I send a broadcast packet?

With the UDP protocol (but not with TCP) you can send a packet so that all workstations on the network will see it. This can be useful at times, but keep in mind that this creates a load on all the machines on the network, even on machines that aren't listening for the packet. This is because the packet has to go down through several layers of the network stack before it is rejected. As a result, most routers are configured to drop broadcast packets. The practical upshot of that is that broadcasts are usually only effective for programs that only have to work on a single LAN. To get around this problem, you may want to consider multicasting instead.

If you still want to use broadcasting, you must first use the setsockopt() function to enable the SO_BROADCAST flag. Next, you have to decide what kind of broadcast you want to use. The simplest type is a packet sent to IP address 255.255.255.255.

Simple broadcasts have a number of problems, however, so to make sure your broadcasts work properly, you should do a "directed broadcast" instead. A directed broadcast is targeted at a single LAN; some routers will allow you to send a directed broadcast from one LAN to another because they usually cause less disruption than simple broadcasts. Also, some network stacks only respond to certain packets if they are directed broadcasts. For example, a broadcast "ping" packet is usually ignored unless it is directed at one of the LANs that the receiving host is on.

A directed broadcast uses a different kind of address. To construct this address, you need to know the LAN's network address and the netmask for that LAN. (You can construct the network address from the address of one of the hosts on the LAN and the netmask.) So, for network 172.16.0.0 with netmask 255.255.0.0, the directed broadcast address is 172.16.255.255. Mathematically, the directed broadcast address is the network address bitwise OR'd with the one's complement of the netmask:

                u_long host_addr = 0xAC104D58;                  // 172.16.77.88
                u_long net_mask = 0xFFFFE000;                   // 255.255.224.0
                u_long net_addr = host_addr & net_mask;         // 172.16.64.0
                u_long dir_bcast_addr = net_addr | (~net_mask); // 172.16.95.255
3.8 - When should I turn off the Nagle algorithm?

Generally, almost never.

Inexperienced Winsockers usually try disabling the Nagle algorithm when they are trying to impose some kind of packet scheme on a TCP data stream. That is, they want to be able to send, say, two packets, one 40 bytes and the other 60, and have the receiver get a 40-byte packet followed by a separate 60-byte packet. (With the Nagle algorithm enabled, TCP will often coalesce these two packets into a single 100 byte packet.) Unfortunately, this is futile, for the following reasons:

  1. Even if the sender manages to send its packets individually, the receiving TCP/IP stack may still coalesce the received packets into a single packet. This can happen any time the sender can send data faster than the receiver can deal with it.
  2. Turning off the Nagle algorithm in a client program will not affect the way that the server sends packets, and vice versa.
  3. Routers and other intermediaries on the network can fragment packets, and there is no guarantee of "proper" reassembly with stream protocols.
  4. If the network stack runs out of buffers, it may fragment a packet, queuing up as many bytes as it has buffer space for and discarding the rest.
  5. Winsock is not required to give you all the data it has queued on a socket even if your recv() call gave Winsock enough buffer space. It may require several calls to get all the data queued on a socket.

Aside from these problems, disabling the Nagle algorithm almost always causes a program's throughput to degrade. The only time you should disable the algorithm is when some other consideration, such as packet timing, is more important than throughput.

Often, programs that deal with real-time user input will disable the Nagle algorithm to achieve the snappiest possible response, at the expense of network bandwidth. Three examples are Telnet clients, X Windows servers, and multiplayer network games. In all three cases, it is more important that there be as little delay between packets as possible than it is to conserve network bandwidth.

Note that this is a changable issue: back when network bandwidth was truly scarce, Telnet clients usually enabled the Nagle algorithm to save on bandwidth. A contributing factor was that user interfaces in those days tended to be line oriented, whereas modern ones are real-time, with each keystroke potentially changing the entire display. The moral is, consider your application and resources carefully before deciding to disable the Nagle algorithm.

For more on this topic, see the Lame List and the FAQ article How to Use TCP Effectively.

3.9 - Is Winsock thread-safe?

The Winsock specification does not mandate that a Winsock implementation be thread-safe, but it does allow an implementor to create a thread-safe version of Winsock.

Bob Quinn says, on this subject:

  • "WinSock, any implementation, is thread safe if the WinSock implementation developer makes it so (it doesn't just happen)."
  • "I don't know of any implementations from Microsoft (or any other vendors) that are not thread safe."
  • "If a WinSock application developer creates a multi-threaded application that shares sockets among the threads, it is that developer's responsibility to synchronize activities between the threads."

By "synchronize activities", I believe Bob means that it may cause problems if, for example, two threads repeatedly call send() on the same socket. There is no guarantee in the Winsock specification about how the data will be interleaved in this situation. Similarly, if one thread calls closesocket() on a socket, it must somehow signal other threads using that socket that the socket is now invalid.

I believe one thread calling send() and another thread calling recv() on a single socket is safe, but I have not tested this. Hard information, demonstration code and/or anecdotal evidence either way would be appreciated.

Instead of multiple threads accessing a single socket, you may want to consider setting up a pair of network I/O queues. Then, give one thread sole ownership of the socket: this thread sends data from one I/O queue and enqueues received data on the other. Then other threads can access the queues (with suitable synchronization).

Applications that use some kind of non-synchronous socket typically have some I/O queue already. Of particular interest in this case is overlapped I/O or I/O completion ports, because these I/O strategies are also thread-friendly. You can tell Winsock about several OVERLAPPED blocks, and Winsock will finish sending one before it moves on to the next. This means you can keep a chain of these OVERLAPPED blocks, each perhaps added to the chain by a different thread. Each thread can also call WSASend() on the block they added, making your main loop simpler.

3.10 - If two threads in an application call recv() on a socket, will they each get the same data?

No. Winsock does not duplicate data among threads.

Note that if you do call recv() at the same time on a single socket from two different threads, havoc may result. See the previous question for more info.

3.11 - Is there any way for two threads to be notified when something happens on a socket?

No. If two threads call WSAAsyncSelect() on a single socket, only the thread that made the last call to WSAAsyncSelect() will receive further notification messages. Similarly, if two threads call WSAEventSelect() on a socket, only the event object used in the last call will be signaled when an event occurs on that socket. You also can't call WSAAsyncSelect() on a socket in one thread and WSAEventSelect() on that same socket in another thread, because the calls are mutually exclusive for any single socket. Finally, you cannot reliably call select() on a single socket from two threads and get the same notifications in each, because one thread could clear or cause an event, which would change the events that the other thread sees.

3.12 - How do I detect if the modem is connected?

Although this is not a Winsock question per se, it is sometimes useful for a Winsock program to only do its thing if the computer is already connected to the Internet. Before implementing this, however, keep in mind that many computers are not connected to the Internet through a modem. Often, they are connected to a LAN, and that LAN is somehow gatewayed to the Internet. For these situations, the following call will fool your program into believing that the user either has no Internet connection, or that it is never up.

Having said that, the Remote Access Service (RAS) API gives an application access to the dial-up networking subsystem. In particular, the RasEnumConnections() call lets you easily get a list of the connected modems.

3.13 - How can I get the local user name?

There are a few ways. The easiest is to use the Win32 call GetUserName(). [C++ Example].

The other way is shown in the Microsoft Knowledge Base article Q155698. It is much more complex, and it shows two completely different methods, one for Windows 9x/Windows 3.1 and one for Windows NT. Unless you need Windows 3.1 support or the LAN Manager domain name (as opposed to the DNS domain name), I suggest you give this article a miss.

3.14 - Windows 9x's Dial Up Networking keeps popping up an automatic dial window, even when it isn't necessary. Can I make it stop?

On some Windows 9x systems with more than one network interface, Dial Up Networking sometimes pops up an automatic-dial window even when it is obviously not required. An example of such a setup is a machine on a LAN that also has a modem for connecting to the Internet.

The most common trigger for the DUN dial window is a Winsock program calling the gethostbyname() function, which initiates a DNS lookup. Even if the name is that of a LAN machine and there's a DNS server on the LAN, DUN will still try to bring up the Internet link to try that first.

If you try messing with the DNS configuration of a multihomed Win9x box, it's clear that the network subsystem just isn't designed to support a local DNS server in addition to a remote one. The best solution, then, is to just use straight IP addresses, and write your programs to recognize an IP address, so they don't have to call gethostbyname().

I've heard that DUN 1.3 and/or the Winsock 2 updates fix this problem, but other reports say it doesn't help.


<< Information for New Winsockers Advanced Winsock Issues >>
Last modified on 29 April 2000 at 15:52 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