KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q147714: Windows Sockets 2 Service Provider Interface Limitations

Article: Q147714
Product(s): Microsoft Windows NT
Version(s): WinNT:3.5,3.51,4.0
Operating System(s): 
Keyword(s): kbnetwork
Last Modified: 14-FEB-2002

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Windows NT Workstation versions 3.5, 3.51, 4.0 
- Microsoft Windows NT Server versions 3.5, 3.51, 4.0 
-------------------------------------------------------------------------------

SUMMARY
=======

The Windows Sockets 2 Service Provider Interface (Revision 2.1.0) provides
specifications for transport network programming under Windows. This interface
is the most comprehensive open transport API designed to date. However, few
limitations exist. The purpose of this article is to list and describe the
limitation items of the specifications and provide alternative solutions to work
around the limitation items. The information below is contributed by the Windows
Sockets Vendor Community.

MORE INFORMATION
================

1. Calling connect() on a non-blocking socket, getting WSAEWOULDBLOCK, then
  immediately calling recv() and expecting WSAEWOULDBLOCK before the connection
  has been established.

  Reason: This assumes that the connection will never be established by the time
  the application calls recv().

  Workaround: An application using a non-blocking socket must handle the
  WSAEWOULDBLOCK error value, but must not depend on occurrence of the error.

2. Calling select() with three empty FD_SETs and a valid TIMEOUT structure as a
  delay function.

  Reason: The select() function is intended as a network function, not a general
  purpose timer.

  Workaround: Use a legitimate system timer service.

3. Polling with connect() on a non-blocking socket to determine when the
  connection has been established.

  Reason: The WinSock 1.1 specification does not define an error for connect()
  when a non-blocking connection is pending. Therefore, the error value
  returned varies.

  Workaround: Use asynchronous notification of connection completion An
  application that prefers synchronous operation mode can use the select()
  function (please see item 21 below).


4. Assuming socket handles are always less than 16.

  Reason: The only invalid socket handle value is defined by the WinSock.H file
  as INVALID_SOCKET. Any other value the SOCKET type can handle is fair game,
  and an application must handle it. Socket handles are not transparent.
  Therefore, applications should not depend on specific values.

  Workaround: Expect a socket handle of any value, including 0. Do not expect
  socket handle values to change with each successive call to socket() or
  WSASocket(). Socket handles may be re-used by the WinSock implementation.

5. Polling with select() and a zero timeout in Win16's non-preemptive
  environment.

  Reason: With any non-zero timeout, select() calls the current blocking hook
  function, so that an application anticipating an event yields to other
  processes executing in a 16-bit Windows environment. However, with a zero
  time-out, an application does not yield to other processes, and may not allow
  network operations to occur (it will loop continuously).

  Workaround: Use a small non-zero timeout, or use asynchronous notification
  instead of using select().

6. Calling WSAAsyncSelect() with a zero Event mask in order to make the socket
  non-blocking.

  Reason: WSAAsyncSelect() is designed to allow an application to register for
  asynchronous notification of network events. The WinSock version 1.1
  specification does not specify an error for a zero event mask, but may
  interpret it as an invalid input argument (so it may fail with WSAEINVAL) or
  ignore the request.

  Workaround: To make a socket non-blocking without registering for asynchronous
  notification, use ioctlsocket() FIONBIO.

7. Telnet applications that neither enable OOBINLINE, nor read OOB data.

  Reason and Workaround not available.

8. Assuming zero (0) is an invalid socket handle value.

  Reason and Workaround: Please see item 4 above.

9. Applications that do not properly shut-down when the user closes the main
  window while a blocking API is running.

  Reason: WinSock applications that do not close sockets and call WSACleanup()
  does not allow a WinSock implementation to re-claim resources used by the
  application. Resource leakage can eventually result in resource shortage for
  all other WinSock applications (for example: network system failure).

  Workaround: While a blocking API is running, do the following to abort:

  a. Call WSACancelBlockingCall().

  b. Wait until the pending function returns. If it is cancelled before the
     operation completes, the pending function fails with the WSAEINTR error.
     However, applications must also be prepared for success due to the race
     condition involved with cancellation.

  c. Close this socket and all other sockets.

     NOTE: The proper closure of a connected stream socket involves the
     following:

      - Call shutdown() how=1

      - Loop on recv() until it returns 0 or fails with any error

      - Call closesocket().

  d. Call WSACleanup().

10. Out of band data.

  Reason: TCP does not do Out of Band (OOB) data reliably. In addition, there
  are incompatible differences in the implementation at the protocol level (in
  the urgent pointer offset). Berkeley Software Distribution's (BSD)
  implementation performs RFC 793. Many others implement the corrected RFC
  1122 version. One version cannot point to the OOB data of the other
  version.

  Workaround: Use a separate socket for urgent data. Some protocols require it
  (please see item 7 above).

11. Calling strlen() on a hostent structure's IP address, then truncating it to
  four bytes, thereby overwriting part of the malloc() heap header.

  Reason and Workaround not available.

12. Polling with recv(MSG_PEEK) to determine when a complete message has
  arrived.

  Reason and Workaround not available.

13. Bounding every set of operations with calls to WSAStartup() and
  WSACleanup().

  Reason: This is possible as long as each WSAStartup() has a matching call to
  WSACleanup(), but requires more work.

  Workaround: In a DLL, custom control or class library, it is possible to
  register the calling client based on a unique task handle or process ID.
  This allows automatic registration without duplication. Automatic
  de-registration can occur when a process closes its last socket. You may
  also use the process notification mechanisms available in the 32- bit
  environment.

14. Ignoring API errors.

  Reason: When a function fails, the error value returned by WSAGetLastError()
  (or included in an asynchronous message) informs you why it has failed.
  Based on the function that fails and the socket state, you can determine why
  the function fails and what you can do.

  Workaround: Check for error values, and write your applications to anticipate
  them. When a fatal error occurs, display an error message that shows the
  following:

   - The function that has failed

   - The WinSock error number and macro

   - A short description of the error definition

   - Workarounds

15. Installing an empty blocking hook that returns FALSE.

  Reason: One of the primary purposes of the blocking hook function is to
  provide a mechanism for an application with a pending blocking operation to
  yield. By returning FALSE from the blocking hook function, you defeat this
  purpose and your application prevents multitasking in the non-preemptive
  16-bit Windows environment. This also prevents some WinSock implementations
  from completing the pending network operation.

  Workaround: Sub-class the active window. This prevents re-entrant messages.

16. Client applications that bind to a specific port.

  Reason: Client applications actively initiate a network communication. Server
  applications passively wait for communication. A server must bind() to a
  specific port which is known to clients that need to use the service.
  However, a client does not need to bind() its socket to a specific port to
  communicate with a server.


  Workaround: Let the WinSock implementation assign the local port number
  implicitly when you call connect() (on stream or datagram sockets) or
  sendto() (on datagram sockets).

17. Nagle challenged applications.

  Reason: The Nagle algorithm reduces trivial network traffic. The algorithm
  recommends that you do not send a TCP segment until:

  - All outstanding TCP segments have been acknowledged

  -or-

  - There is a full TCP segment ready to send.

  A "Nagle challenged application" is one that cannot wait until either of
  these conditions occurs. However, it has time-critical data that it must
  send continuously. This results in unwanted network traffic.

  Workaround: Do not write applications that depend on the immediate data echo
  from the remote TCP host.

18. Assuming stream sockets maintain message frame boundaries.

  Reason: Stream sockets (TCP) provide data streams. The largest message length
  an application can depend on is one-byte in length. This means that with any
  call to send() or recv(), the WinSock implementation may transfer any number
  of bytes less than the buffer length specified.

  Workaround: Whether you use a blocking or non-blocking socket, compare the
  return from send() or recv() with the expected value. If the value is less
  than the expected value, adjust the buffer length and pointer for another
  function call (which may occur asynchronously, if you use asynchronous
  operation mode).

19. 16-bit DLL's that call WSACleanup() from their WEP.

  Reason: 16-bit Windows does not guarantee that WEP() is called and the
  Windows subsystem has been in frequent dis-array.

  Workaround: Do not use WEP().

20. Single-byte send() and recv().

  Reason: Couple one-byte sends with Nagle disabled.


  Workaround: Send modest amounts and receive as much as possible.


21. Select().

  Reason: Consider the steps involved in using select(). You must use the
  macros to clear the three fdsets, then set the appropriate fdsets for each
  socket, then set the timer, and then call select().


  Workaround: Use asynchronous operation mode.

22. Applications that call gethostbyname() before calling inet_addr().

  Reason: Some users prefer to use network addresses rather than host names.
  The WinSock version 1.1 specification does not specify what gethostbyname()
  should do with an IP address in standard ASCII dotted IP notation. As a
  result, the outcome is unpredictable. It may succeed and do a
  reverse-lookup, or it may fail.

  Workaround: With any destination input by a user (which may be a host name or
  dotted IP address), call inet_addr() first to check for an IP address, and
  if that fails, call gethostbyname() to try to resolve it.

  Furthermore, in some applications, you may want to explicitly check the input
  string for the broadcast address "255.255.255.255," because the return value
  from inet_addr() for this address is the same as SOCKET_ERROR.

23. Win32 applications that install blocking hooks.

  Reason: Aside from yielding to other applications (please see item 15 above),
  blocking hook functions are designed to allow concurrent processing within a
  task, while there is a blocking operation pending. In Win32, there are
  threads.

  Workaround: Use threads.

24. Polling with ioctlsocket(FIONREAD) on a stream socket until a complete
  "message" appears.

  Reason: A stream socket (TCP) does not preserve message boundaries (please
  see item 18 above). An application that uses ioctlsocket() FIONREAD or
  recv(MSG_PEEK) to wait for a complete "message" to arrive may not succeed.
  One reason is due to the internal WinSock system buffering. If the bytes in
  a "message" straddle a system buffer boundary, the WinSock does not report
  bytes that exist in other buffers.

  Workaround: Do not use peek reads. Read data into your application buffers
  and examine it there.

25. Assuming that a UDP datagram of any length may be sent.

  Reason: Various networks all have their limitations on maximum transmission
  unit (MTU).

  Workaround: Check for the maximum datagram size with the SO_MAX_MSGSIZE
  socket option.

26. Assuming the UDP transmissions (especially multicast transmissions) are
  reliable.

  Reason: UDP has no reliability mechanisms.

  Workaround: Use TCP and keep track of your own message boundaries.

27. Applications that require vendor-specific extensions, and cannot run (or
  load) without them.

  Workaround: Have a fall-back position that uses only base capabilities for
  when the extension functions are not present.

Additional query words: prodnt rfc

======================================================================
Keywords          : kbnetwork 
Technology        : kbWinNTsearch kbWinNTWsearch kbWinNTW400 kbWinNTW400search kbWinNT351search kbWinNT350search kbWinNT400search kbWinNTW350 kbWinNTW350search kbWinNTW351search kbWinNTW351 kbWinNTSsearch kbWinNTS400search kbWinNTS400 kbWinNTS351 kbWinNTS350 kbWinNTS351search kbWinNTS350search
Version           : WinNT:3.5,3.51,4.0

=============================================================================

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.