socket模型使归纳(EventSelect—>HPR_WaitForMultipleObjects—>EnumNetworkEvents示例)

共有五种类型的套接字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,网络,callback,winapi)