hit counter for tumblr
Real Time Web Analytics

purefractalsolutions.com

sockets as a cats

Updated: 2011-10-07 15:17:44
If you found mistake, simply select it
and press Control+Enter

This code snippet demonstrates how to create socket which is compatible with ReadFile/WriteFile/WaitFor Win32 functions.

Problem

There is design inconvenience or defect in Win32 in comparison with Linux and other Posix APIs - normal Win32 socket handle is not a regular system handle and standard system IO (and other) functions can't properly work with it.

Solution

Probably, you will be surprised - this is not a design defect. Do you like cats? No. Oops, you simply aren't able to prepare them. Exact the same and with sockets. Idea is to create socket in special way. In that way we create socket as Installable File System handle.

Creating socket

Simply is the use WSASocket function instead of socket. Next problem is how to get properly filled WSAPROTOCOL_INFO stuct to call WSASocket. The WSAEnumProtocols function helps us to find apropriate filled WSAPROTOCOL_INFO stuct. We need to enumerate all available protocols and find one with XP1_IFS_HANDLES flag is on. After socket creation we can use common ReaFile, Writefile, WaitFor* WIN API functions.
[-]
#ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
#endif

#ifndef STRICT
    #define STRICT
#endif

#include <windows.h>
#include <winsock2.h>
#include <string>
#include <vector>
#include <iostream>

#ifdef WIN32
    #include <malloc.h>
#else /* __GNUC__ */
     #include <alloca.h>
     #ifndef _alloca
         #define _alloca  alloca
     #endif
#endif

#ifndef WINSOCK_VERSION_REQUIRED_HIGH
    #define WINSOCK_VERSION_REQUIRED_HIGH  2
#endif

#ifndef WINSOCK_VERSION_REQUIRED_LOW
    #define WINSOCK_VERSION_REQUIRED_LOW   0
#endif

inline
void initSocketsAPI()
   {
    WORD versionRequested = MAKEWORD( WINSOCK_VERSION_REQUIRED_HIGH, WINSOCK_VERSION_REQUIRED_LOW );
    WSADATA   wsaData;
    int wsaRes = WSAStartup( versionRequested , &wsaData );
    if (wsaRes)
       throw ::std::runtime_error( (::std::string("Failed to init windows sockets, error code: ")  /* + int2str(wsaRes)).c_str() */  ));

    if ( LOBYTE( wsaData.wVersion ) != WINSOCK_VERSION_REQUIRED_HIGH
       ||HIBYTE( wsaData.wVersion ) != WINSOCK_VERSION_REQUIRED_LOW
       )
       {
        WSACleanup( );
        throw ::std::runtime_error( (::std::string("Failed to init windows sockets, version taken: ")
                            /* + int2str(LOBYTE( wsaData.wVersion )) + ::std::string(1,'.') + int2str(HIBYTE( wsaData.wVersion )) ).c_str() */  ));
       }
   }

bool tryWsaEnumProtocols( int protocol
                        , ::std::vector<WSAPROTOCOL_INFO> &protocolsList // result set
                        , SIZE_T &structCount // in/out
                        )
   {
    DWORD byteCount = structCount*sizeof(WSAPROTOCOL_INFO);
    DWORD byteCountRequired = byteCount;
    LPWSAPROTOCOL_INFO pInfo = (LPWSAPROTOCOL_INFO)_alloca(byteCount);
    int nProtocols[2] = { protocol, 0 };
    int res = ::WSAEnumProtocols( nProtocols, pInfo, &byteCountRequired);
    if (res==SOCKET_ERROR)
       {
        if (::WSAGetLastError()==WSAENOBUFS)
           {
            structCount = byteCountRequired / sizeof(WSAPROTOCOL_INFO);
            return false; // not enough buffer
           }
        return true; // other errors, stop enumerating
       }

    if (byteCountRequired>byteCount)
       {
        structCount = byteCountRequired / sizeof(WSAPROTOCOL_INFO);
        return false;
       }

    structCount = (SIZE_T)res;
    for(SIZE_T i = 0; i!=structCount; ++i)
       {
        protocolsList.push_back( *pInfo++ );
       }
    return true;
   }

SOCKET createSocketHandle(int af, int type, int protocol)
   {
    SIZE_T numStructs = 16; // 4 is good value for most systems
    ::std::vector<WSAPROTOCOL_INFO> protocolsList;

    bool stopTryEnumProtocols = tryWsaEnumProtocols(protocol, protocolsList, numStructs);
    while(!stopTryEnumProtocols)
        {
         numStructs *=2;
         stopTryEnumProtocols = tryWsaEnumProtocols(protocol, protocolsList, numStructs);
        }

    //std::cout<<"Struct count: "<<numStructs<<"\n"; // prints 4

    ::std::vector<WSAPROTOCOL_INFO>::iterator it = protocolsList.begin();
    for(; it != protocolsList.end(); ++it)
       {
        if ( (af==AF_UNSPEC || af == it->iAddressFamily) 
           &&(type == it->iSocketType) 
           &&(it->dwServiceFlags1 & XP1_IFS_HANDLES)
           )
           {
            return ::WSASocket(af, type, protocol, &(*it), 0, 0);
           }
       }
    return SOCKET_ERROR;
   }


int main(int argc, char* argv[])
   {
    //char *hostAddr = "192.168.0.1";
    // google IP addrs
    char *hostAddr = "74.125.232.50";
    //char *hostAddr = "74.125.232.52";
    //char *hostAddr = "74.125.232.49";
    //char *hostAddr = "74.125.232.51";
    //char *hostAddr = "74.125.232.48";

    if (argc>1)
       hostAddr = argv[1];

    initSocketsAPI();
    SOCKET sock = createSocketHandle(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    sockaddr_in peer_addr;
    peer_addr.sin_family = AF_INET;
    peer_addr.sin_port   = htons(80);    
    peer_addr.sin_addr.s_addr   = inet_addr(hostAddr);

    if (connect(sock, (sockaddr*)&peer_addr, sizeof(peer_addr))==SOCKET_ERROR)
       {
        ::std::cout<<"connect error: "<<WSAGetLastError()<<"\n";
       }
    else
       {
        ::std::cout<<"connect OK\n";
       }

    char dataToSend[] = "GET /\r\n\r\n";
    DWORD written;
    if (!WriteFile( (HANDLE)sock, (LPCVOID)&dataToSend[0], 9, &written, 0 ))
       {
        ::std::cout<<"WriteFile error: "<<GetLastError()<<"\n";
        return 0;
       }

    DWORD waitRes = ::WaitForSingleObject( (HANDLE)sock, 10000 );


    std::cout<<"WaitForSingleObject: ";
    switch(waitRes)
       {
        case WAIT_ABANDONED:  
                std::cout<<"WAIT_ABANDONED\n";
                break;
        case WAIT_OBJECT_0:  
                std::cout<<"WAIT_OBJECT_0\n";
                break;
        case WAIT_TIMEOUT:  
                std::cout<<"WAIT_TIMEOUT\n";
                break;
        case WAIT_FAILED:  
                std::cout<<"WAIT_FAILED, err: "<< ::GetLastError() <<"\n";
                break;
        default:            
                std::cout<<"Unknown result\n";
       }

    DWORD readed;
    char buf[16384];
    if (!ReadFile( (HANDLE)sock, (LPVOID)&buf[0], sizeof(buf), &readed, 0 ))
       {
        ::std::cout<<"ReadFile error: "<<GetLastError()<<"\n";
        return 0;
       }
    std::cout<< std::string(buf, readed)<<"\n";
    return 0;
   }