Microsoft KB Archive/331756

= PRB: The Ioctlsocket function cannot detect inline out-of-band data =

Article ID: 331756

Article Last Modified on 7/8/2005

-

APPLIES TO


 * Microsoft Windows NT 4.0
 * Microsoft Windows 2000 Professional Edition
 * Microsoft Windows 2000 Server
 * Microsoft Windows 2000 Advanced Server
 * Microsoft Windows XP Home Edition
 * Microsoft Windows XP Professional
 * Microsoft Windows Server 2003, Web Edition
 * Microsoft Windows Server 2003, Standard Edition (32-bit x86)
 * Microsoft Windows Server 2003, Enterprise Edition (32-bit x86)
 * Microsoft Windows Server 2003, Datacenter Edition (32-bit x86)
 * Microsoft Windows NT 4.0 Driver Development Kit
 * Microsoft Win32 Device Driver Kit for Windows 2000
 * Microsoft Windows XP Driver Development Kit
 * Microsoft Windows Server 2003 Driver Development Kit

-



This article was previously published under Q331756





SYMPTOMS
When a socket is set for the SO_OOBINLINE flag, the out-of-band (OOB) data that is sent on the socket is returned as ordinary data. You may notice that the OOB data is mixed with other ordinary data in a call to the recv function. Because of this behavior, you cannot retrieve the OOB data by using the recv function when the SO_OOBINLINE flag is set to 0.



CAUSE
The IoctlSocket function with the SIOCATMARK command is used to determine whether all OOB data has been read. However, on sockets that have SO_OOBINLINE turned on, the SIOCATMARK input/output control command (IOCTL) always returns TRUE, and the OOB data is returned to the user as ordinary data.



WORKAROUND
To work around this behavior, turn off the SO_OOBINLINE setting, and use the select function to verify that OOB data is present. (The select function verifies that OOB data is present when the SO_OOBINLINE flag is turned off.)

When SO_OOBINLINE is turned off, the select function returns with the appropriate exceptfds socket set if OOB data is available in the buffer. However, for OOB data in TCP, the OOB data block is always one byte. This particular behavior is by design.

The application can call the recv function that has the MSG_OOB flag to read the OOB data (one byte), or the application can call the recv function that does not have the MSG_OOB flag to read the ordinary data stream.

To implement this workaround, follow these steps, and then test the code (as described later).

Note: The following code explanation is based on the sample code in the &quot;More Information&quot; section.   Call the setsockopt function to turn off SO_OOBINLINE: BOOL optval = FALSE; setsockopt(mySocket, SOL_SOCKET, SO_OOBINLINE, (char *)&optval, sizeof(optval)); Note This step is optional because, by default, SO_OOBINLINE is FALSE.   Use the following code to declare two fd_set structures. The select function uses these structures. fd_set fdread,fdOOB;   Use the following code to initialize the sets and to add to the sets the sockets that are to be checked: FD_ZERO(&fdread); FD_ZERO(&fdOOB);

// 'remoteSocket' is the socket that you check in this sample. FD_SET(remoteSocket, &fdread); FD_SET(remoteSocket, &fdOOB);   Call the select function to check the status of the sockets in the sets. The following code is an example of the socket status in the sets that are returned: if((err=select(0,&fdread,0,&fdOOB,0))==SOCKET_ERROR) {   printf(&quot;select failed: %d\n&quot;,WSAGetLastError); return; }   Use the following logic to check the resulting sets and to determine whether any OOB data is available for reading.  If OOB data is available, call the recv function with the MSG_OOB flag to read the OOB data.</li> If OOB data is not available, call the revc function without the MSG_OOB flag to read the ordinary data.</li></ul>

if(FD_ISSET(remoteSocket,&fdOOB)) {   recv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,MSG_OOB); //Other code here, to do something when the data is OOB. } else if(FD_ISSET(remoteSocket,&fdread)) {   recv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0); //Other code here, to do something when it is ordinary data. } </li></ol>

Note: This method works only if the OOB data that is to be sent is one byte. If you send multiple-byte OOB data at the same time, only the last byte of the OOB data is retrieved. The data that is not retrieved is considered ordinary data.

Test the Code
<ol> Run MyServer  to start the server.

Use MyClient   to connect the client to the server.</li> The server waits 10 seconds for the user to provide input; you must press a key or keys on the client computer. When MyServer indicates the status of Waiting, press the a key (lowercase), press the A key (uppercase), and then press the b key (lowercase).

This causes the client to send 10 bytes a, to send 1 byte A (OOB data), and then to send 10 bytes b to the server.

On the server, you will see the following:

<pre class="fixed_text">[OOB]: A

[Normal]: aaaaaaaaabbbbbbbbb

The data A is read as urgent (OOB) data.</li></ol>

<div class="moreinformation_section">

Complete Code List
Use the following code to create a server console application and a client console application, respectively:

Server Code (MyServer.cpp)

 * 1) include <windows.h>
 * 2) include <stdio.h>
 * 3) include <stdlib.h>
 * 4) include <winsock.h>
 * 5) include <assert.h>

// Usage: myserver.

void ChannelHandler(SOCKET remoteSocket, struct sockaddr_in *pRemoteIp) {   int err; BYTE buffer[40 + 1]; int i;   fd_set fdread,fdOOB; BOOL isOOB;

printf(&quot;Connected from %d.%d.%d.%d:%d\n&quot;,        pRemoteIp->sin_addr.S_un.S_un_b.s_b1,        pRemoteIp->sin_addr.S_un.S_un_b.s_b2,        pRemoteIp->sin_addr.S_un.S_un_b.s_b3,        pRemoteIp->sin_addr.S_un.S_un_b.s_b4,        ntohs(pRemoteIp->sin_port));

printf(&quot;Waiting&quot;); for (i=0; i<10; i++) { printf(&quot;.&quot;); Sleep(1000); }   printf(&quot;ok\n&quot;);

for { int recv_len; isOOB=FALSE; u_long value = 0; //Always clear the set before calling select FD_ZERO(&fdread); FD_ZERO(&fdOOB);

//Add socket s to sets. FD_SET(remoteSocket, &fdread); FD_SET(remoteSocket, &fdOOB);

if((err=select(0,&fdread,0,&fdOOB,0))==SOCKET_ERROR) {           printf(&quot;select failed: %d\n&quot;,WSAGetLastError); return; }

//select should return the number of sockets that are in the fdread set. if (err >0) {           if(FD_ISSET(remoteSocket,&fdOOB)){ recv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,MSG_OOB); printf(&quot;\n I receive the OOB data, NUM IS %d\n&quot;,recv_len); isOOB=TRUE; } else if(FD_ISSET(remoteSocket,&fdread)){ recv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0); }

if (recv_len==SOCKET_ERROR) { printf(&quot;recv failed, win32 error is 0x%lx\n&quot;, WSAGetLastError); return ; }           if (recv_len == 0) { printf(&quot;\nConnected from %d.%d.%d.%d:%d\n&quot;,                    pRemoteIp->sin_addr.S_un.S_un_b.s_b1,                    pRemoteIp->sin_addr.S_un.S_un_b.s_b2,                    pRemoteIp->sin_addr.S_un.S_un_b.s_b3,                    pRemoteIp->sin_addr.S_un.S_un_b.s_b4,                    ntohs(pRemoteIp->sin_port)); return ; }           buffer[recv_len] = '\0'; if (!isOOB) { // This is not OOB data printf(&quot;[Normal]: %s\n&quot;, buffer); } else { //We have got OOB data printf(&quot;[OOB]: %s\n&quot;, buffer); }                  }    }    return ; }

int main(int argc, char *argv[]) {   WSADATA wsaData; int err; SOCKET mySocket = INVALID_SOCKET; struct sockaddr_in localIp; unsigned short nPort; // Listen port number

if (argc!=2) {       printf(&quot;MyServer <Port>\n&quot;); return 0; }   nPort = (unsigned short)atol(argv[1]);

err = WSAStartup(MAKEWORD(2,0),&wsaData); assert(err==0); assert(wsaData.wVersion == MAKEWORD(2,0));

mySocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); assert(mySocket!=INVALID_SOCKET);

// Bind the listen socket to any local IP address. localIp.sin_family = AF_INET; localIp.sin_port = htons(nPort);; localIp.sin_addr.S_un.S_addr = INADDR_ANY;

err = bind(mySocket,(SOCKADDR *)&localIp,sizeof(localIp)); if (err==SOCKET_ERROR) { printf(&quot;bind failed, win32 error is 0x%lx\n&quot;, WSAGetLastError); goto Cleanup; }

BOOL optval = FALSE; err = setsockopt(mySocket, SOL_SOCKET, SO_OOBINLINE, (char *)&optval, sizeof(optval)); if (err == SOCKET_ERROR) { printf(&quot;setsockopt failed, win32 error is 0x%lx\n&quot;, WSAGetLastError); return 0; }

err = listen(mySocket, 5); assert(err==0);

for { struct sockaddr_in remoteIp; SOCKET remoteSocket = INVALID_SOCKET; int nAddrLen = sizeof(SOCKADDR);

remoteSocket = accept(mySocket, (SOCKADDR *)(&remoteIp), &nAddrLen); if (remoteSocket == INVALID_SOCKET) { int error = WSAGetLastError; printf(&quot;accept failed, win32 error is 0x%lx\n&quot;, GetLastError); goto Cleanup; } else { ChannelHandler(remoteSocket, &remoteIp); closesocket(remoteSocket); }   }

Cleanup: WSACleanup; return 0; }

Client Code (MyClient.cpp):

 * 1) include <windows.h>
 * 2) include <process.h>
 * 3) include <string.h>
 * 4) include <stdio.h>
 * 5) include <stdlib.h>
 * 6) include <conio.h>
 * 7) include <assert.h>
 * 8) include <winsock.h>
 * 9) include <memory.h>

void ChannelHandler(void *pData) {   SOCKET mySocket = (SOCKET)pData; BYTE buffer[1024]; int nSize;

for { nSize = recv(mySocket, (char*)buffer, sizeof(buffer), 0); if (nSize == 0) { closesocket(mySocket); break; }       if (nSize==SOCKET_ERROR) { int error = WSAGetLastError; printf(&quot;recv failed due to %lX\n&quot;, error); closesocket(mySocket); break; }       printf(&quot;%s&quot;, buffer); }   return ; }

main(int argc, char *argv[]) {   int err; WSADATA wsaData; SOCKET mySocket = INVALID_SOCKET; unsigned short nPort; // Listen port number struct sockaddr_in remoteIp; if (argc!=3) {       printf(&quot;TcpDemoC <RemoteIp> <Port>\n&quot;); return 0; }

// Init Socket API. err = WSAStartup(MAKEWORD(1,1),&wsaData); assert(err==0); nPort = (unsigned short)atol(argv[2]); // Create an TCP socket to listen on. mySocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); assert(mySocket!=INVALID_SOCKET); // Connect to the remote address. remoteIp.sin_family = AF_INET; remoteIp.sin_port = htons(nPort);; remoteIp.sin_addr.S_un.S_addr = inet_addr(argv[1]); err = connect(mySocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp)); if (err == SOCKET_ERROR) { int error = WSAGetLastError; printf(&quot;connect failed due to %lX\n&quot;, error); goto Cleanup; }   _beginthread(ChannelHandler, 0, (void *)mySocket); for { int nSize; int ch = _getch; if (ch=='.') { shutdown(mySocket, 2); break; }       if (isupper(ch)) { nSize = send(mySocket, ((char *)&ch),1, MSG_OOB); } else { char buf[10]; memset(buf, ch, sizeof(buf)); nSize = send(mySocket, buf, sizeof(buf)-1, 0); }       if (nSize == SOCKET_ERROR) { int error = WSAGetLastError; printf(&quot;send failed due to %lX\n&quot;, error); break; }   }     Sleep(1000); Cleanup: if (mySocket!=INVALID_SOCKET) { closesocket(mySocket); }   WSACleanup; return 0; }

<div class="references_section">