darwin之socket消息获取与处理
select_watchevent(select_modwatch):socket注册窗口消息(类型req->er_data)
select_waitevent:当窗口消息发生后,获取窗口消息。如果还想继续获取该socket其他消息,则需要重新调用select_watchevent注册。
在windows下,EventThread线程创建一个窗口,所有的socket事件都以windows消息的形式发送到这个窗口上。
EventThread内部有一个OSRefTable fRefTable,用来管理多个socket。key是: 消息id, value是:EventContext。
每一个实例化的socket注册窗口消息的id都不一样,每次收到socket注册都回累加。
void EventContext::RequestEvent(int theMask):调用select_watchevent注册socket消息,如果是第一次注册,则关联到EventThread::fRefTable表中。
virtual void EventContext::ProcessEvent(int /*eventBits*/) :处理消息,在EventThread线程内部,不断地调用select_waitevent函数,获取所有注册到fRefTable上发送的窗口消息,一旦获取到,则调用ProcessEvent处理。
注意ProcessEvent是一个虚函数,也就意味着派生类处理消息的方式可能会改变。在EventContext中,只是将其对应的task signal到taskthread中。
WSAAsyncSelect参考:
int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
该函数会自动将套接字设置为非阻塞模式,并且把发生在该套接字上且是你所感兴趣的事件,以Windows消息的形式发送到指定的窗口,
你需要做的就是在传统的消息处理函数中处理这些事件。参数hWnd表示指定接受消息的窗口句柄;参数wMsg表示消息码值(这意味着你
需要自定义一个Windows消息码);参数IEvent表示你希望接受的网络事件的集合,它可以是如下值的任意组合:FD_READ, FD_WRITE,
FD_OOB, FD_ACCEPT, FD_CONNECT, FD_CLOSE 之后,就可以在我们熟知的Windows消息处理函数中处理这些事件。如果在某一套接字s上
发生了一个已命名的网络事件,应用程序窗口hWnd会接收到消息wMsg。参数wParam即为该事件相关的套接字s;参数lParam的低字段指
明了发生的网络事件,lParam的高字段则含有一个错误码,事件和错误码可以通过下面的宏从lParam中取出:
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
下面继续使用伪代码来帮助阐述如何将上一节的阻塞模式WinSock应用升级到非阻塞模式。
首先自定义一个Windows消息码,用于标识我们的网络消息。
#define WM_CUSTOM_NETWORK_MSG (WM_USER + 100)
//服务器端,在监听之前,将监听套接字置为非阻塞模式,并且标明其感兴趣的事件为FD_ACCEPT。
WSAAsyncSelect(server, wnd, WM_CUSTOM_NETWORK_MSG, FD_ACCEPT);
listen(server);
//客户端,在连接之前,将套接字置为非阻塞模式,并标明其感兴趣的事件为FD_CONNECT。
WSAAsyncSelect(client, wnd, WM_CUSTOM_NETWORK_MSG, FD_CONNECT);
ServerAddress?server;
connect(client,?server);
//接着,在Windows消息处理函数中,我们将处理监听事件、连接事件、及读写事件,方便起见,这里将服务器和客户端的处理代码放在
了一起。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CUSTOM_NETWORK_MSG: // 自定义的网络消息码
{
SOCKET socket = (SOCKET)wParam; // 发生网络事件的套接字
long event = WSAGETSELECTEVENT(lParam); // 事件
int error = WSAGETSELECTERROR(lParam); // 错误码
switch (event)
{
case FD_ACCEPT: // 服务器收到新客户端的连接请求
{
// 接收到客户端连接,分配一个客户端套接字
SOCKET client = accept(socket);
// 将新分配的客户端套接字置为非阻塞模式,并标明其感兴趣的事件为读、写及关闭
WSAAsyncSelect(client, hWnd, message, FD_READ | FD_WRITE | FD_CLOSE);
}
break;
case FD_CONNECT: // 客户端连接到服务器的操作返回结果
{
// 成功连接到服务器,将客户端套接字置为非阻塞模式,并标明其感兴趣的事件为读、写及关闭
WSAAsyncSelect(socket, hWnd, message, FD_READ | FD_WRITE | FD_CLOSE);
}
break;
case FD_READ: // 收到网络包,需要读取
{
// 使用套接字读取网络包
recv(socket);
}
break;
case FD_WRITE:
{
// FD_WRITE的处理后面会具体讨论
}
break;
case FD_CLOSE: // 套接字的连接方(而非本地socket)关闭消息
{
}
break;
default:
break;
}
}
break;
…
}
…
}
以上就是非阻塞模式WinSock的应用框架,WSAAsyncSelect模型将套接字和Windows消息机制很好地粘合在一起,为用户异步SOCKET应用提供
了一种较优雅的解决方案。