Winsock提供了另一个有用的异步I/O模型。和WSAAsyncSelect模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知。对于表1总结的、由WSAAsyncSelect模型采用的网络事件来说,它们均可原封不动地移植到新模型。在用新模型开发的应用程序中,也能接收和处理所有那些事件。该模型最主要的差别在于网络事件会投递至一个事件对象句柄,而非投递至一个窗口例程。
跟WSAAsyncSelect类似,但是不是通过消息实现,而是通过事件对象。因为是基于select实现,一个线程也只能管理64个socket。事件选择模型是基于消息的。它允许程序通过Socket,接收以事件为基础的网络事件通知。事件选择模型相关函数主要有4个,第一个是:
WSAEVENT WSACreatEvent(void);
WSANOTINITIALISED //在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN //网络子系统失效。
WSA_NOT_ENOUGH_MEMORY //无足够内存创建事件对象。
int WSAEventSelect(SOCKET s, WSAEVENT hEventObject,long lNetworkEvents);
第3个是:
DWORD WSAAPI WSAWaitForMultipleEvents( DWORD cEvents,const WSAEVENT FAR * lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable );
WSANOTINITIALISED //在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN //网络子系统失效。
WSA_NOT_ENOUGH_MEMORY //无足够内存完成该操作。
WSA_INVALID_HANDLE //lphEvents数组中的一个或多个值不是合法的事件对象句柄。
WSA_INVALID_PARAMETER //cEvents参数未包含合法的句柄数目。
int WSAAPI WSAEnumNetworkEvents ( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents, LPINT lpiCount);
WSANOTINITIALISED //在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN //网络子系统失效。
WSAEINVAL //参数中有非法值。
WSAEINPROGRESS //一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数WSAENOBUFS 所提供的缓冲区太小。
示例仍然使用反射式服务端与反射式客户端。关于反射式客户端的代码请参见:http://blog.csdn.net/caoshiying/article/details/52550761第三章。关于本示例程序使用的公共组件请参见http://blog.csdn.net/caoshiying/article/details/52550761第四章。可以用类向导创建一个event_server_manager类型,头文件如下:
#pragma once
#include
#include
#include
class event_server_manager:
protected iserver_manager
{
private:
WSADATA wsa;
int iclient_count;
int iaddr_size;
int iport;
SOCKET server;
SOCKET clients[FD_SETSIZE];
WSAEVENT mevents[FD_SETSIZE];
common_callback callback;
BOOL brunning;
private:
void cleanup(int index);
protected:
bool accept_by_crt();
bool accept_by_winapi();
void receive();
public:
void shutdown();
void start_accept_by_crt();
void start_accept_by_winapi();
void start_receive();
public:
event_server_manager();
virtual ~event_server_manager();
};
实现文件如下:
#include "event_server_manager.h"
#include
#include
#include
#include
event_server_manager::event_server_manager()
{
WSAStartup(MAKEWORD(2, 2), &wsa);
iclient_count = 0;
iport = 5150;
iaddr_size = sizeof(SOCKADDR_IN);
brunning = FALSE;
callback.set_manager(this);
}
event_server_manager::~event_server_manager()
{
}
bool event_server_manager::accept_by_crt()
{
SOCKET conn_socket;
SOCKADDR_IN local;
SOCKADDR_IN conn_addr;
int iret = 0;
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(iport);
do
{
iret = bind(server, (struct sockaddr*)&local, iaddr_size);
if (iret == 0)
break;
iport++;
local.sin_port = htons(iport);
} while (iret == SOCKET_ERROR);
listen(server, 3);
printf_s("服务已经启动,监听端口是:%d\n", iport);
while (brunning)
{
conn_socket = accept(server, (struct sockaddr*)&conn_addr, &iaddr_size);
if (conn_socket == INVALID_SOCKET)
{
printf_s("拒绝一个连接。\n");
continue;
}
printf_s("新连接%s:%d。\n", inet_ntoa(conn_addr.sin_addr), ntohs(conn_addr.sin_port));
clients[iclient_count] = conn_socket;
mevents[iclient_count] = WSACreateEvent();
WSAEventSelect(conn_socket, mevents[iclient_count], FD_ALL_EVENTS);
iclient_count++;
}
return true;
}
bool event_server_manager::accept_by_winapi()
{
return true;
}
void event_server_manager::receive()
{
DWORD iret = 0;
int index = 0;
WSANETWORKEVENTS nevent;
char message[1024] = { 0 };
while (brunning)
{
iret = WSAWaitForMultipleEvents(iclient_count, mevents, FALSE, 1000, FALSE);
if (iret == WSA_WAIT_FAILED || iret == WSA_WAIT_TIMEOUT)
continue;
index = iret - WSA_WAIT_EVENT_0;
WSAEnumNetworkEvents(clients[index], mevents[index], &nevent);
if (nevent.lNetworkEvents&FD_READ)
{
iret = recv(clients[index], message, 1024, 0);
if (iret == 0 || (iret == SOCKET_ERROR&&WSAGetLastError() == WSAECONNRESET))
cleanup(index);
else
{
message[iret] = 0;
send(clients[index], message, iret, 0);
}
}
if (nevent.lNetworkEvents&FD_CLOSE)
{
cleanup(index);
}
}
}
void event_server_manager::cleanup(int index)
{
SOCKADDR_IN client;
assert(index < iclient_count);
getpeername(clients[index], (struct sockaddr*)&client, &iaddr_size);
printf_s("客户端%s:%d断开连接。\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
closesocket(clients[index]);
WSACloseEvent(mevents[index]);
if (index < iclient_count - 1)
{
clients[index] = clients[iclient_count - 1];
mevents[index] = mevents[iclient_count - 1];
}
clients[iclient_count - 1] = 0;
mevents[iclient_count - 1] = INVALID_HANDLE_VALUE;
iclient_count--;
}
void event_server_manager::shutdown()
{
brunning = FALSE;
callback.shutdown();
for (int i = 0; i < iclient_count; i++)
{
closesocket(clients[i]);
WSACloseEvent(mevents[i]);
}
closesocket(server);
WSACleanup();
}
void event_server_manager::start_accept_by_crt()
{
brunning = TRUE;
callback.start_accept_by_crt();
}
void event_server_manager::start_accept_by_winapi()
{
brunning = TRUE;
callback.start_accept_by_winapi();
}
void event_server_manager::start_receive()
{
brunning = TRUE;
callback.start_receive();
}
int main(int argc, char** argv)
{
event_server_manager esm;
esm.start_accept_by_crt();
esm.start_receive();
printf_s("按任意键关闭服务端并退出程序。\n");
system("pause");
esm.shutdown();
return 0;
}