Windows Socket I/O模型之 WSAAsyncSelect模式

# include < winsock2. h>
# include < ws2tcpip. h>

# include "public.h"
# include "resolve.h"

typedef SINGLE_LIST_HEADER BuffHeader;
typedef SINGLE_LIST BuffObj;
typedef DOUBLE_LIST_HEADER SockObjHeader;
typedef DOUBLE_LIST SockObj;

# define WM_SOCKET ( WM_USER + 10)

HWND gWorkerWindow= NULL ;
CRITICAL_SECTION gSocketCritSec; // Syncrhonize access to socket list


SockObjHeader sockobjhead;

typedef struct _SOCKET_OBJ
{
    SOCKET s; // Socket handle

    int closing; // Indicates whether the connection is closing


    SOCKADDR_STORAGE addr; // Used for client's remote address

    int addrlen; // Length of the address


    BuffHeader buff;

    DOUBLE_LIST entry;

    CRITICAL_SECTION SockCritSec;
} SOCKET_OBJ;

SOCKET_OBJ* GetSocketObj( SOCKET s) {
    SOCKET_OBJ * sockobj = NULL ;

    sockobj = ( SOCKET_OBJ* ) HeapAlloc( GetProcessHeap( ) , HEAP_ZERO_MEMORY, sizeof ( SOCKET_OBJ) ) ;
    if ( sockobj = = NULL ) {
        fprintf ( stderr , "HeapAlloc failed./n" ) ;
        ExitProcess( - 1) ;
    }

    sockobj- > s = s;
    sockobj- > addrlen = sizeof ( sockobj- > addr) ;

    InitializeCriticalSection( & sockobj- > SockCritSec) ;
    InitializeCriticalSection( & sockobj- > buff. SendRecvQueueCritSec) ;

    return sockobj;
}

SOCKET_OBJ * FindSocketObj( SOCKET s) {
    EnterCriticalSection( & gSocketCritSec) ;

    SOCKET_OBJ * obj = NULL ;
    SOCKET_OBJ * sobj = NULL ;
    SockObj * sptr = ( SockObj * ) GotoNextDoubleList( & sockobjhead, & ( sockobjhead. head) ) ;

    while ( sptr) {
        obj = ( SOCKET_OBJ * ) container_of( SOCKET_OBJ, entry, sptr) ;

        if ( obj- > s = = s) {
            sobj = obj;
            break ;
        }

        sptr = ( SockObj * ) GotoNextDoubleList( & sockobjhead, sptr) ;
    }

    LeaveCriticalSection( & gSocketCritSec) ;

    return sobj;
}

void RemoveSocketObj( SOCKET_OBJ * sock) {
    EnterCriticalSection( & gSocketCritSec) ;

    RemoveDoubleList( & sockobjhead, & sock- > entry) ;

    LeaveCriticalSection( & gSocketCritSec) ;
}

void RemoveSocketObjByHandle( SOCKET s) {
    SOCKET_OBJ * obj = NULL ;

    obj = FindSocketObj( s) ;
    if ( obj) {
        RemoveSocketObj( obj) ;
    }

    return ;
}

int ReceivePendingData( SOCKET_OBJ * sockobj) {
    BUFFER_OBJ * buffobj= NULL ;
    int rc,
                ret;

    buffobj = GetBufferObj( gBufferSize) ;

    ret = 0;

    if ( gProtocol = = IPPROTO_TCP )
    {
        rc = recv ( sockobj- > s, buffobj- > buf, buffobj- > buflen, 0) ;
    } else {
        ExitProcess( - 1) ;
    }

    if ( rc = = SOCKET_ERROR) {
        ExitProcess( - 1) ;
    } else if ( rc = = 0) {
        FreeBufferObj( buffobj) ;

        printf ( "Closing/n" ) ;
        sockobj- > closing = TRUE ;

        if ( sockobj- > buff. head = = NULL )
        {
            // If no sends are pending, close the socket for good

            closesocket( sockobj- > s) ;

            ret = - 1;
        }
        else
        {
            ret = WSAEWOULDBLOCK;
        }
    } else {
        printf ( "recv: %d./n" , rc) ;
        buffobj- > buflen = rc;
        EnqueueSingleList( & sockobj- > buff, & buffobj- > next) ;
    }

    return ret;
}

int SendPendingData( SOCKET_OBJ * sock) {
    BUFFER_OBJ * bufobj = NULL ;
    BuffObj * entry = NULL ;
    int nleft = 0,
                idx = 0,
                ret = 0,
                rc = 0;

    while ( entry = DequeueSingleList( & sock- > buff) ) {
        bufobj = ( BUFFER_OBJ * ) container_of( BUFFER_OBJ, next, entry) ;

        if ( gProtocol = = IPPROTO_TCP ) {
            nleft = bufobj- > buflen;
            idx = 0;

            while ( nleft > 0) {
                rc = send ( sock- > s, & ( bufobj- > buf[ idx] ) , nleft, 0) ;
                if ( rc = = SOCKET_ERROR) {
                    ExitProcess( - 1) ;
                } else {
                    idx + = rc;
                    nleft - = rc;
                }
            }

            FreeBufferObj( bufobj) ;
        } else {
            ExitProcess( - 1) ;
        }
    }

    if ( sock- > closing = = TRUE ) {
        closesocket( sock- > s) ;
        ret = - 1;

        printf ( "Closing Connection./n" ) ;
    }

    return ret;
}

LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    SOCKET_OBJ * sockobj= NULL ,
                * newsock= NULL ;
    int rc;

    if ( uMsg = = WM_SOCKET) {
        if ( WSAGETSELECTERROR( lParam) ) {
            fprintf ( stderr , "Socket failed./n" ) ;
            closesocket( wParam) ;
            RemoveSocketObjByHandle( wParam) ;
        } else {
            sockobj = FindSocketObj( wParam) ;
            if ( sockobj = = NULL )
                return 0;

            switch ( WSAGETSELECTEVENT( lParam) ) {
                case FD_ACCEPT:
                    newsock = ( SOCKET_OBJ* ) GetSocketObj( INVALID_SOCKET) ;

                    newsock- > s = accept ( wParam, ( SOCKADDR * ) & sockobj- > addr, & sockobj- > addrlen) ;
                    if ( newsock- > s = = INVALID_SOCKET) {
                        fprintf ( stderr , "accept failed./n" ) ;
                        ExitProcess( - 1) ;
                    }

                    EnqueueDoubleList( & sockobjhead, & newsock- > entry) ;

                    rc = WSAAsyncSelect( newsock- > s, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE) ;
                    if ( rc = = SOCKET_ERROR)
                    {
                        fprintf ( stderr , "accept WSAAsyncSelect failed: %d/n" , WSAGetLastError( ) ) ;
                        return - 1;
                    }

                    break ;
                case FD_READ:
                    rc = ReceivePendingData( sockobj) ;

                    if ( rc = = - 1) {
                        RemoveSocketObj( sockobj) ;
                        break ;
                    } else if ( rc ! = WSAEWOULDBLOCK) {
                        PostMessage( hwnd, WM_SOCKET, wParam, FD_READ) ;
                    }
                case FD_WRITE:
                    rc = SendPendingData( sockobj) ;
                    if ( rc = = - 1)
                    {
                        RemoveSocketObj( sockobj) ;
                    }
                    break ;
                case FD_CLOSE:
                    sockobj- > closing = TRUE ;

                    PostMessage( hwnd, WM_SOCKET, wParam, FD_READ) ;
                    break ;
                default :
                    printf ( "unknown message./n" ) ;
                    ExitProcess( - 1) ;
            }
        }
    }
    return DefWindowProc( hwnd, uMsg, wParam, lParam) ;
}

HWND MakeWorkerWindow( void ) {
    WNDCLASS wndclass;
    CHAR * ProviderClass = "AsyncSelect" ;
    HWND windows;

    wndclass. style = CS_HREDRAW | CS_VREDRAW;
    wndclass. lpfnWndProc = ( WNDPROC) WindowProc;
    wndclass. cbClsExtra = 0;
    wndclass. cbWndExtra = 0;
    wndclass. hInstance = NULL ;
    wndclass. hIcon = LoadIcon( NULL , IDI_APPLICATION) ;
    wndclass. hCursor = LoadCursor( NULL , IDC_ARROW) ;
    wndclass. hbrBackground = ( HBRUSH) GetStockObject( WHITE_BRUSH) ;
    wndclass. lpszMenuName = NULL ;
    wndclass. lpszClassName = ProviderClass;

    if ( RegisterClass( & wndclass) = = 0)
    {
        fprintf ( stderr , "RegisterClass() failed with error %d/n" , GetLastError( ) ) ;
        ExitProcess( - 1) ;
    }

    if ( ( windows = CreateWindow(
                    ProviderClass,
                    "" ,
                    WS_OVERLAPPEDWINDOW,
                    CW_USEDEFAULT,
                    CW_USEDEFAULT,
                    CW_USEDEFAULT,
                    CW_USEDEFAULT,
                    NULL ,
                    NULL ,
                    NULL ,
                    NULL ) ) = = NULL )
    {
        fprintf ( stderr , "CreateWindow() failed with error %d/n" , GetLastError( ) ) ;
        ExitProcess( - 1) ;
    }

    return windows;
}

int _tmain( int argc, _TCHAR* argv[ ] )
{
    WSADATA wsd;
    struct addrinfo * res= NULL ,
                    * ptr= NULL ;
    SOCKET_OBJ * sockobj= NULL ,
                    * sptr= NULL ,
                    * tmp= NULL ;
    int rc;
    MSG msg;

    InitializeCriticalSection( & gSocketCritSec) ;

    if ( WSAStartup( MAKEWORD( 2, 2) , & wsd) ! = 0) {
        fprintf ( stderr , "load winsock2 failed./n" ) ;
        return 0;
    }

    gWorkerWindow = MakeWorkerWindow( ) ;

    res = ResolveAddress( gSrvAddr, gPort, gAddressFamily, gSocketType, gProtocol) ;
    if ( res = = NULL )
    {
        fprintf ( stderr , "ResolveAddress failed to return any addresses!/n" ) ;
        return - 1;
    }

    InitializeDoubleHead( & sockobjhead) ;

    ptr = res;
    while ( ptr) {
        sockobj = GetSocketObj( INVALID_SOCKET) ;

        sockobj- > s = socket ( ptr- > ai_family, ptr- > ai_socktype, ptr- > ai_protocol) ;
        if ( sockobj- > s = = INVALID_SOCKET) {
            fprintf ( stderr , "create socket failed./n" ) ;
            ExitProcess( - 1) ;
        }

        EnqueueDoubleListHead( & sockobjhead, & sockobj- > entry) ;

        rc = bind ( sockobj- > s, ptr- > ai_addr, ptr- > ai_addrlen) ;
        if ( rc = = SOCKET_ERROR) {
            fprintf ( stderr , "bind failed./n" ) ;
            ExitProcess( - 1) ;
        }

        if ( gProtocol = = IPPROTO_TCP ) {
            rc = listen ( sockobj- > s, 200) ;
            if ( rc = = SOCKET_ERROR) {
                fprintf ( stderr , "bind failed./n" ) ;
                ExitProcess( - 1) ;
            }

            rc = WSAAsyncSelect( sockobj- > s, gWorkerWindow, WM_SOCKET, FD_ACCEPT | FD_CLOSE) ;
            if ( rc = = SOCKET_ERROR) {
                fprintf ( stderr , "WSAAsyncSelect failed./n" ) ;
                ExitProcess( - 1) ;
            }

        } else {
            ExitProcess( - 1) ;
        }

        ptr = ptr- > ai_next;
    }
    freeaddrinfo ( res) ;

    while ( rc = GetMessage( & msg, NULL , 0, 0) ) {
        if ( rc = = - 1) {
            fprintf ( stderr , "GetMessage failed./n" ) ;
            return - 1;
        }     

        TranslateMessage( & msg) ;
        DispatchMessage( & msg) ;
    }

    WSACleanup( ) ;
    DeleteCriticalSection( & gSocketCritSec) ;

    return 0;
}

 

版权声明: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。

猜你喜欢

转载自blog.csdn.net/micklongen/article/details/6171925
今日推荐