socket模型使归纳

    共有五种类型的套接字I/O模型,可让Winsock应用程序对I/O进行管理,它们包括:select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、overlapped(重叠)以及completion port(完成端口)

1.select
  最初设计该模型,主要是面向某些使用Unix操作系统的计算机.使用大概原理设置一个集合,通过一个宏的定义来查询集合类的套接字可否使用。做个端口扫描用这个还是比较开心的。

2.WSAAsyncSelect
  模仿WINDOWS消息机制来实现,使用起来很方便。个人比较喜欢。MFC中的CSOCKET也采用了这个模型。
  相关函数:
int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
lEvent表示一个掩码组合,比如FD_CONNECT|FD_READ

LREWULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
wParam: 发生了一个网络事件的套接字
lParam: (低位)指定了已经发生的网络事件WSAGETSELECTEVENT  (高位)包含了可能出现的任何错误代码 用WSAGETSELECTERROR获取

大概使用框架如下:
创建套接字,使用WSAAsyncSelect进行套接字和事件绑定,在回调函数中写清消息响应

#define WM_SOCKET WM_USER+1
int WINAPI WinMain(HINSTANCE hINstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    SOCKET Listen;
    HWND Window;
    //Create a window and assign the winproc
    //Start Winsock and create a socket
    WSAStartup(...);
    Listen = Socket();
    bind(...);
    WSAAsyscSelect(Listen, Window, WM_SOCKET, FD_ACCEPT | FD_CLOSE);
    listen(Listen, 5);
}

BOOL CALLBACK WinProc(HWND hDlg, WORD wMsg, WORD wParam, DWORD lParam)
{
    SOCKET Accept;
    switch(wMsg)
    {
case WM_SOCKET:
        //whether an error occurred on the socket by using the WSAGETSELECTERROR() macro
        if(WSAGETSELECTERROR(lParam))
        {
    closesocket...
}
        //what event occurred on the socket
        switch( WSAGETSELECTEVENT(lParam) )
        {
    case FD_ACCEPT:
            case FD_READ:
            case FD_WRITE:
}
    }
}

FD_WRITE说明:三种条件下才会发出
使用connect WSAConnect,一个套接字首次建立连接
使用accept WSAAccept,套接字被接受以后
若send WSASend sendto WSASendTo操作失败,返回WSAEWOULDBLOCK错误,而且缓冲区空间变得可用
也就是说,收到首条FD_WRITE消息,便应认为自己必然能在一个套接字上发数据.

3 WSAEventSelect
你可以想象着,一排的空套接字等着对方的连接...
如上章所述,async投递一个窗口例程,而Event投递一个事件对象句柄.
或者更清楚解释为,套接字和事件对象对应着,当一个套接字有事件发生,事件对象返回相应的值,通过这个值来索引这个套接字。
要注意该模式里两种工作状态和模式,signaled/nonsignaled    manual reset/auto reset
一开始默认为未传信(nonsignaled)和人工重设(manual reset)状态,随着网络事件的触发,工作状态从未船信到已传信.在完成一个I/O请求处理后,要负责将工作状态改变

首先创建一个事件对象  WSAEVENT WSACreateEvent(void);
其次将事件对象和套接字联系在一起 int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
等待网络事件触发事件对象句柄状态 DWORD WSAWaitForMultipleEvents(....DWORD dwTimeout, BOOL fAlertable);
  dwTimeout参数规定等待一个网络事件发生有多长时间,毫秒为单位.超过立即返回,即使由fWaitAll参数规定条件未满足也如此.如果设为0,函数会检测指定的事件对象状态,然后返回.这样造成一个"轮询",尽量避免设为0.若设为WSA_INFINITE,那么只有在一个网络事件传回一个事件对象后,才会返回.
  fAlertable:忽略,设为FALSE,主要用于重叠式I/O模型中

将已传信状态更改为未传信 BOOL WSAResetEvent(WSAEVENT hEvent);
释放事件句柄使用的系统资源  BOOL WSACloseEvent(WSAEVENT hEvent);

现在还需解决的一问题是,怎么获得发生事件的套接字,async直接用一个socket变量接收,而在event里,是通过事先的一个数组索引获得.这个方法名为WSAEnumNetworkEvents();

代码框架如下:
//先创建事件数组和套接字数组
SOCKET Socket[常量];
WSAEVENT Event[];
DWORD EvenTotal = 0;
//这里进行套接字准备到bind那步,创建一个事件,并且联系
NewEvent = WSACreateEvent();
WSAEventSelect(Listen, NewEvent, FD_ACCEPT|FD_CLOSE);

//最重要一步,加入到数组
Socket[EventTotal] = Listen;
Event[EventTotal] = NewEvent;
EventTotal++;

while(TRUE)
{
    //等待一个套接字连接
    Index = WSAWaitForMultipleEvents(..);
    //在EnumNetworkEvents中进行查询,判断在哪个套接字上,发生什么网络事件类型.
    WSAEnumNetworkEvents(
         SocketArray[Index - WSA_WAIT_EVENT_0],
         EventArray[Index - WSA_WAIT_EVENT_0],
         NetworkEvents);
   
}



对一个发生事件套接字操作
WSAWaitForMultipleEvents返回值:减去预定值WSA_WAIT_EVENT_0
Iindex = WSAWaitForMultipleEvents(..);
MyEvent = EventArray[Index - WSA_WAIT_EVENT_0];
接下来可调用WSAEnumNetworkEvents函数,调查发生什么类型网络事件


4IO重叠
对一个套接字多次利用,利用CRITICAL_SECTION来进行互斥

5。。。略

你可能感兴趣的:(设计模式,框架,socket,网络应用,mfc)