WSADATA wsaData;
if( 0 != WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) ) {
printf( \wsaData.szSystemStatus );
printf( \ LOBYTE( wsaData.wVersion), HIBYTE( wsaData.wVersion ),
LOBYTE( wsaData.wHighVersion), HIBYTE( wsaData.wHighVersion) );
return -1; } else {
printf(\ }
//////////////////////////////////////////////////////////////////////////
int nPort = 20055;
// 创建完成端口对象
// 创建工作线程处理完成端口对象的事件
HANDLE hIocp = ::CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0, 0, 0 );
::CreateThread( NULL, 0, ServerThread, (LPVOID)hIocp, 0, 0 ); // 创建监听套接字,绑定本地端口,开始监听
SOCKET sListen = ::socket( AF_INET,-SOCK_STREAM, 0 ); SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = ::htons( nPort );
addr.sin_addr.S_un.S_addr = INADDR_ANY;
::bind( sListen, (sockaddr *)&addr, sizeof( addr ) ); ::listen( sListen, 5 );
printf( \ // 循环处理到来的请求 while ( TRUE ) {
// 等待接受未决的连接请求
SOCKADDR_IN saRemote;
int nRemoteLen = sizeof( saRemote );
SOCKET sRemote = ::accept( sListen, (sockaddr *)&saRemote, &nRemoteLen );
// 接受到新连接之后,为它创建一个per_handle数据,并将他们关联到完成端口对象
PPER_HANDLE_DATA pPerHandle =
( PPER_HANDLE_DATA )::GlobalAlloc( GPTR, sizeof( PPER_HANDLE_DATA ) ); if( pPerHandle == NULL ) {
break; }
pPerHandle->s = sRemote;
memcpy( &pPerHandle->addr, &saRemote, nRemoteLen ); ::CreateIoCompletionPort( ( HANDLE)pPerHandle->s, hIocp, (DWORD)pPerHandle, 0 );
// 投递一个接受请求
PPER_IO_DATA pIoData = ( PPER_IO_DATA )::GlobalAlloc( GPTR, sizeof( PPER_IO_DATA ) );
if( pIoData == NULL ) {
break; }
pIoData->nOperationType = OP_READ; WSABUF buf;
buf.buf = pIoData->buf; buf.len = BUFFER_SIZE;
DWORD dwRecv = 0; DWORD dwFlags = 0;
::WSARecv( pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pIoData->ol, NULL ); }
////////////////////////////////////////////////////////////////////////// ERROR_PROC:
WSACleanup();
////////////////////////////////////////////////////////////////////////// return 0; }
/****************************************************************** * 函数介绍:处理完成端口对象事件的线程 * 输入参数: * 输出参数: * 返回值 :
*******************************************************************/ DWORD WINAPI ServerThread( LPVOID lpParam ) {
HANDLE hIocp = ( HANDLE )lpParam; if( hIocp == NULL ) {
return -1; }
DWORD dwTrans = 0;
PPER_HANDLE_DATA pPerHandle; PPER_IO_DATA pPerIo;
while( TRUE ) {
// 在关联到此完成端口的所有套接字上等待I/O完成
BOOL bRet = ::GetQueuedCompletionStatus( hIocp, &dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIo, WSA_INFINITE ); if( !bRet ) // 发生错误 {
::closesocket( pPerHandle->s ); ::GlobalFree( pPerHandle ); ::GlobalFree( pPerIo ); cout << \ continue; }
// 套接字被对方关闭
if( dwTrans == 0 && ( pPerIo->nOperationType == OP_READ || pPerIo->nOperationType&nb-sp;== OP_WRITE ) ) {
::closesocket( pPerHandle->s );
::GlobalFree( pPerHandle ); ::GlobalFree( pPerIo );
cout << \ continue; }
switch ( pPerIo->nOperationType ) {
case OP_READ: // 完成一个接收请求 {
pPerIo->buf[dwTrans] = '\\0'; printf( \
// 继续投递接受操作 WSABUF buf;
buf.buf = pPerIo->buf; buf.len = BUFFER_SIZE;
pPerIo->nOperationType = OP_READ;
DWORD dwRecv = 0; DWORD dwFlags = 0;
::WSARecv( pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIo->ol, NULL ); }
break;
case OP_WRITE: case OP_ACCEPT: break; } }
return 0; }
(完)
欢迎阅读此篇IOCP教程。我将先给出IOCP的定义然后给出它的实现方法,最后剖析一个Echo程序来为您拨开IOCP的谜云,除去你心中对IOCP的烦恼。OK,但我不能保证你明白IOCP的一切,但我会尽我最大的努力。以下是我会在这篇文章中提到的相关技术: ? ? ? ? ? ?
I/O端口 同步/异步 堵塞/非堵塞 服务端/客户端 多线程程序设计 Winsock API 2.0
在这之前,我曾经开发过一个项目,其中一块需要网络支持,当时还考虑到了代码的可移植性,只要使用 select,connect,accept,listen,send还有recv,再加上几个#ifdef的封装以用来处理Winsock和BSD套接字[socket]中间的不兼容性,一个网络子系统只用了几个小时很少的代码就写出来了,至今还让我很回味。那以后很长时间也就没再碰了。
前些日子,我们策划做一个网络游戏,我主动承担下网络这一块,想想这还不是小case,心里偷着乐啊。网络游戏好啊,网络游戏为成百上千的玩家提供了乐趣和令人着秘的游戏体验,他们在线上互相战斗或是加入队伍去战胜共同的敌人。我信心满满的准备开写我的网络,于是乎,发现过去的阻塞同步模式模式根本不能拿到一个巨量多玩家[MMP]的架构中去,直接被否定掉了。于是乎,就有了IOCP,如果能过很轻易而举的搞掂IOCP,也就不会有这篇教程了。下面请诸位跟随我进入正题。
什么是IOCP?
先让我们看看对IOCP的评价
I/O完成端口可能是Win32提供的最复杂的内核对象。 [Advanced Windows 3rd] Jeffrey Richter 这是[IOCP]实现高容量网络服务器的最佳方法。
[Windows Sockets2.0:Write Scalable Winsock Apps Using Completion Ports] Microsoft Corporation
完成端口模型提供了最好的伸缩性。这个模型非常适用来处理数百乃至上千个套接字。 [Windows网络编程2nd] Anthony Jones & Jim Ohlund
I/O completion ports特别显得重要,因为它们是唯一适用于高负载服务器[必须同时维护许多连接线路]的一个技术。Completion ports利用一些线程,帮助平衡由I/O请求所引起的负载。这样的架构特别适合用在SMP系统中产生的”scalable”服务器。 [Win32多线程程序设计] Jim Beveridge & Robert Wiener
看来我们完全有理由相信IOCP是大型网络架构的首选。那IOCP到底是什么呢?
微软在Winsock2中引入了IOCP这一概念 。IOCP全称I/O Completion Port,中文译为I/O完成端口。IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序。与使用select()或是其它异步方法不同的是,一个套接字[socket]与一个完成端口关联了起来,然后就可继续进行正常的Winsock操作了。然而,当一个事件发生的时候,此完成端口就将被操