PRB: The Ioctlsocket function cannot detect inline out-of-band data (331756)
The information in this article applies to:
- Microsoft Windows NT 4.0
- Microsoft Windows 2000 Professional
- 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
- Microsoft Windows Server 2003, Enterprise Edition
- Microsoft Windows Server 2003, Datacenter Edition
- Microsoft Windows NT 4.0 Driver Development Kit (DDK)
- Microsoft Windows 2000 Driver Development Kit (DDK)
- Microsoft Windows XP Driver Development Kit (DDK)
- Microsoft Windows Server 2003 Driver Development Kit (DDK)
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.CAUSEThe 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
"More Information" 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("select() failed: %d\n",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.
- If OOB data is not available, call the revc function without the MSG_OOB flag to read the ordinary data.
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.
} 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- Run MyServer <Port> to start the server.
Use MyClient <ServerIP><ServerPort> to connect the client to the server. - 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:[OOB]: A
[Normal]: aaaaaaaaabbbbbbbbb The data A is read as urgent (OOB) data.
MORE INFORMATIONComplete Code ListUse the following code to create a server console application and
a client console application, respectively: Server Code (MyServer.cpp)#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <assert.h>
// Usage: myserver <port>.
void ChannelHandler(SOCKET remoteSocket, struct sockaddr_in *pRemoteIp)
{
int err;
BYTE buffer[40 + 1];
int i;
fd_set fdread,fdOOB;
BOOL isOOB;
printf("Connected from %d.%d.%d.%d:%d\n",
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("Waiting");
for (i=0; i<10; i++) {
printf(".");
Sleep(1000);
}
printf("ok\n");
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("select() failed: %d\n",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("\n I receive the OOB data, NUM IS %d\n",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("recv() failed, win32 error is 0x%lx\n", WSAGetLastError());
return ;
}
if (recv_len == 0) {
printf("\nConnected from %d.%d.%d.%d:%d\n",
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("[Normal]: %s\n", buffer);
} else { //We have got OOB data
printf("[OOB]: %s\n", 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("MyServer <Port>\n");
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("bind() failed, win32 error is 0x%lx\n", WSAGetLastError());
goto Cleanup;
}
BOOL optval = FALSE;
err = setsockopt(mySocket, SOL_SOCKET, SO_OOBINLINE, (char *)&optval, sizeof(optval));
if (err == SOCKET_ERROR) {
printf("setsockopt() failed, win32 error is 0x%lx\n", 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("accept() failed, win32 error is 0x%lx\n", GetLastError());
goto Cleanup;
} else {
ChannelHandler(remoteSocket, &remoteIp);
closesocket(remoteSocket);
}
}
Cleanup:
WSACleanup();
return 0;
}
Client Code (MyClient.cpp):#include <windows.h>
#include <process.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <assert.h>
#include <winsock.h>
#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("recv failed due to %lX\n", error);
closesocket(mySocket);
break;
}
printf("%s", 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("TcpDemoC <RemoteIp> <Port>\n");
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("connect failed due to %lX\n", 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("send failed due to %lX\n", error);
break;
}
}
Sleep(1000);
Cleanup:
if (mySocket!=INVALID_SOCKET) {
closesocket(mySocket);
}
WSACleanup();
return 0;
} REFERENCESProtocol-Independent Out-of-Band Data Windows Sockets Functions
For more information, click the following article number to view the article in the Microsoft Knowledge Base:
830597
How to send multiple-byte out-of-band data by using Winsock
Modification Type: | Major | Last Reviewed: | 7/8/2005 |
---|
Keywords: | kbDSWNET2003Swept kbnetwork kbWinsock kbDDK kbSDK kbprb KB331756 kbAudDeveloper |
---|
|