Click here to Skip to main content
12,248,339 members (49,603 online)
Click here to Skip to main content
Add your own
alternative version

Stats

26K views
3K downloads
41 bookmarked
Posted

Asynchronous (non-blocking) client socket wrapper class without MFC

, 4 Dec 2010 GPL3
Rate this:
Please Sign up or sign in to vote.
A Win32 API non-blocking sockets implementation, client side.

AsyncSocketWin32.png

Introduction

I created this class while trying to avoid having recv hang/block indefinitely in a simple client application I was working on, without using MFC. recv can hang for a variety of reasons; the most common is untimely calling of recv, or calling it when the remote server didn't send data it was supposed to send, e.g.: because of a malformed request.

Using the Code

#include "XSocket.h"

....

CXSocket mySock;

if (!mySock.Init()) //initalize winsocks
    return false;
    
//////////////////////////////////////////////////////////////////////////

if (!mySock.Connect(pHostName, nPort))
{
    int nError = mySock.GetLastError();
    return false;
}

/////////////////////////////////////////////////////////////////////////

// Send a buffer, 5 seconds timeout
// further error checking omitted for previty

int nLen = 0;
if (mySock.Send(szBuff, strlen(szBuff), nLen, 5000) != E_XSOCKET_SUCCESS)
    return false;

/////////////////////////////////////////////////////////////////////////

// Receive server's response, 5 seconds time out
// last argument is optional, if not used Rec will return immediately

do
{
    if (mySock.Recv(szBuff, sizeof(szBuff) - 1, nLen, 5000) 
        != E_XSOCKET_SUCCESS)
    {
    break;
    }
}
while (nLen == sizeof(szBuff));    

//////////////////////////////////////////////////////////////////////////
// Optional: explicitly close the socket, if not called socket will be closed
// auto on destruction

mySock.Close();

Canceling a request, regardless of timeout:

mySock.Abort();

Checking if there is data available for reading; this can be called at any time after a successful Connect:

long lLen = mySocket.GetLenDataAvail();

Points of Interest

We turn on non-blocking mode for the socket, and at the same time attach an event object to it by calling:

WSAEventSelect(hSocket, hEvent, FD_READ | FD_WRITE | FD_CLOSE)

This will attach hEvent to hSocket so that when there is a new read, write, and close event, hEvent will be signaled. Also, WSAEventSelect automatically sets hSocket to non-blocking mode.

Sending Data

The data sending code looks like this:

int CXSocket::Send(const char* pBuff, int nLen, int& nLenSent, DWORD dwTimeOut)
{    

_BEGIN:

    int nRet = 0;
    m_nLastError = 0;

    if ((nRet = send(m_hSocket, pBuff, nLen, 0)) > 0 || 
               (m_nLastError = WSAGetLastError()) != WSAEWOULDBLOCK)
        return E_XSOCKET_SUCCESS;

    /////////////////////////////////////////////////////////////

    HANDLE arrHandles[2];

    arrHandles[0] = m_eventNet.GetEvent();
    arrHandles[1] = m_eventStop.GetEvent();

    DWORD dwWaitRes = 
       WaitForMultipleObjects(2, arrHandles, FALSE, dwTimeOut); 
    
    if (dwWaitRes == WAIT_OBJECT_0 + 1)
        return E_XSOCKET_ABORTED;
    else if (dwWaitRes != WAIT_OBJECT_0)
        return E_XSOCKET_TIMEDOUT;


    /////////////////////////////////////////////////////////////

    WSANETWORKEVENTS myNetEvents;

    if (WSAEnumNetworkEvents(m_hSocket, m_eventNet.GetEvent(), 
                                    &myNetEvents) != 0)
    {
        m_nLastError = WSAGetLastError();
        return E_XSOCKET_SOCKERR;
    }

    if ((myNetEvents.lNetworkEvents & FD_WRITE) != FD_WRITE)
    {
        goto _BEGIN;
    }

    if (myNetEvents.iErrorCode[FD_WRITE_BIT] != 0)
        return E_XSOCKET_SOCKERR;


    ///////////////////////////////////////////////////////////

    nLenSent = send(m_hSocket, pBuff, nLen, 0);


    return E_XSOCKET_SUCCESS;
}

What we do here is first issue a Send request; if our request can be fulfilled, data will be sent immediately and the return value of Send will be the length of the data actually sent. Otherwise, our request will be queued and Send will return WSAEWOULDBLOCK. The Winsock subsystem will then notify us when it is possible to send our data through signaling the event we attached to the socket earlier. At that point, we call WSAEnumNetworkEvents to make sure the event is an FD_WRITE; if it is not, we continue waiting for FD_WRITE (if dwTimeOut is greater than zero).

Receiving Data

The data receiving code looks like this:

int CXSocket::Recv(char* pBuff, int nLen, int& nLenReceived, DWORD dwTimeOut)
{

_BEGIN:

    /////////////////////////////////////////////////////////////////
    HANDLE arrHandles[2];

    arrHandles[0] = m_eventNet.GetEvent();
    arrHandles[1] = m_eventStop.GetEvent();

    DWORD dwWaitRes = 
      WaitForMultipleObjects(2, arrHandles, FALSE, dwTimeOut); 
    
    if (dwWaitRes == WAIT_OBJECT_0 + 1)
        return E_XSOCKET_ABORTED;
    else if (dwWaitRes != WAIT_OBJECT_0)
        return E_XSOCKET_TIMEDOUT;


    //////////////////////////////////////////////////////////////////
    WSANETWORKEVENTS myNetEvents;

    if (WSAEnumNetworkEvents(m_hSocket, 
         m_eventNet.GetEvent(), &myNetEvents) != 0)
    {
        m_nLastError = WSAGetLastError();
        return E_XSOCKET_SOCKERR;
    }

    if ((myNetEvents.lNetworkEvents & FD_READ) != FD_READ)
        goto _BEGIN;

    if (myNetEvents.iErrorCode[FD_READ_BIT] != 0)
        return E_XSOCKET_SOCKERR;


    ///////////////////////////////////////////////////////////////////
    if ((nLenReceived = 
               recv(m_hSocket, pBuff, nLen, 0)) == WSAEWOULDBLOCK)
    {
        nLenReceived = 0;
        return E_XSOCKET_NOMOREDATA;
    }


    return E_XSOCKET_SUCCESS;
}

What we do is wait for an FD_READ event. If we get it, we call recv to read the data available.

History

  • 03 - Dec - 10: First release.

The latest version is available here.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

h_seldon
United States United States
Latest Project: Website Filter

You may also be interested in...

Comments and Discussions

 
QuestionThere is a memory leak! Pin
Yu_Matrix11-May-14 22:43
memberYu_Matrix11-May-14 22:43 
GeneralMy vote of 5 Pin
David xiao8-Apr-11 21:31
memberDavid xiao8-Apr-11 21:31 
GeneralEvent clost error Pin
mikemao2-Jan-11 20:40
membermikemao2-Jan-11 20:40 
GeneralRe: Event clost error Pin
mikemao2-Jan-11 20:43
membermikemao2-Jan-11 20:43 
GeneralRe: Event clost error Pin
mikemao2-Jan-11 20:44
membermikemao2-Jan-11 20:44 
GeneralRe: Event clost error Pin
h_seldon2-Jan-11 21:47
memberh_seldon2-Jan-11 21:47 
GeneralWaiting for two events Pin
Alex Fr4-Dec-10 7:21
memberAlex Fr4-Dec-10 7:21 
GeneralRe: Waiting for two events [modified] Pin
h_seldon4-Dec-10 12:31
memberh_seldon4-Dec-10 12:31 
GeneralLooks interesting Pin
HamidYaseen4-Dec-10 5:13
memberHamidYaseen4-Dec-10 5:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160426.1 | Last Updated 4 Dec 2010
Article Copyright 2010 by h_seldon
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid