/
//IOCPDemo.cpp文件 调试通过
//I/O完成端口最初的设计是应用程序发出一些异步I/O请求,当这些请求完
//成时,设备驱动把这些工作排序到完成端口,在完成端口等待的线程池
//可以处理这些完成I/O。
//首先调用CreateCompletionPort创建一个完成端口对象。函数的两个功能如下:
//1、创建一个完成端口对象。
//2、将一个或者多个文件句柄(此处是套节字)关联到I/O完成端口对象。
//最初创建时,需要设置的参数是NUMBEROFCONCURRENTTHREADS,它定义了允
//许在完成端口上同时执行的线程的数量。为0表示允许线程数量与处理器数
//量一样多。
//CreateIoCompletionPort函数的NumberOfConcurrentThreads参数个线程允
//许运行,即使创建了比允许线程更多的工作线程,仍只有允许线程数进
//入线程池。但是有时候,确实需要创建更多的线程,因为某个
//线程调用了一个函数,进行了暂停状态,体眠后暂时不能为线程池所有。所以
//必须有新的工作线程来补充。
//有了足够的线程来处理完成端口上的I/O请求之后,就该为完成端口关联套
//节字句柄,向完成端口关联套节字句柄之后,便可以通过在套节字上投递重叠
//发送和接收请求处理I/O,在这些I/O操作完成时,I/O系统会向完成端口对
//象发送一个完成通知封包。应用程序使用GetQueuedCompletionStatus函数
//可以取得这些队列中的封包,返回后说明发生了如下事件之一:
//1、GetQueuedCompletionStatus调用失败,说明在此套节字上有错误发生。
//2、BytesTransferred为0说明套节字被对方关闭。
//3、I/O请求成功完成。通过per-I/O数据中的OperationType域查看哪个I/O
//请求完成了。
//在每个套节字句柄关闭,通过PostQueuedCompletionStatus向工作线程发
//送特定的完成封包终止完成端口上处理的I/O线程,
#include "../common/initsock.h"
#include <stdio.h>
#include <windows.h>
// 初始化Winsock库
CInitSock theSock;
#define BUFFER_SIZE 1024
typedef struct_PER_HANDLE_DATA //per-handle数据
{
SOCKETs; //对应的套节字句柄
sockaddr_in addr; // 客户方地址
} PER_HANDLE_DATA, *PPER_HANDLE_DATA;
typedef struct_PER_IO_DATA //per-I/O数据
{
OVERLAPPEDol; //重叠结构
char buf[BUFFER_SIZE]; //数据缓冲区
intnOperationType; // 操作类型
#define OP_READ 1
#define OP_WRITE 2
#define OP_ACCEPT 3
} PER_IO_DATA, *PPER_IO_DATA;
DWORD WINAPI ServerThread(LPVOID lpParam)
{
// 得到完成端口对象句柄
HANDLE hCompletion = (HANDLE)lpParam;
DWORD dwTrans;
PPER_HANDLE_DATA pPerHandle;
PPER_IO_DATA pPerIO;
while(TRUE)
{
//在关联到此完成端口的所有套节字上等待I/O完成
BOOL bOK =::GetQueuedCompletionStatus(hCompletion,
&dwTrans,(LPDWORD)&pPerHandle,(LPOVERLAPPED*)&pPerIO, WSA_INFINITE);
if(!bOK) //在此套节字上有错误发生
{
::closesocket(pPerHandle->s);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
continue;
}
if(dwTrans == 0&& //套节字被对方关闭
(pPerIO->nOperationType== OP_READ || pPerIO->nOperationType ==OP_WRITE))
{
::closesocket(pPerHandle->s);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
continue;
}
switch(pPerIO->nOperationType) //通过per-I/O数据中的nOperationType域查看什么I/O请求完成了
{
caseOP_READ: // 完成一个接收请求
{
pPerIO->buf[dwTrans]= '/0';
printf(pPerIO-> buf);
//继续投递接收I/O请求
WSABUFbuf;
buf.buf= pPerIO->buf ;
buf.len= BUFFER_SIZE;
pPerIO->nOperationType= OP_READ;
DWORDnFlags = 0;
::WSARecv(pPerHandle->s,&buf, 1, &dwTrans,&nFlags,&pPerIO->ol, NULL);
}
break;
case OP_WRITE: //本例中没有投递这些类型的I/O请求
case OP_ACCEPT:
break;
}
}
return 0;
}
void main()
{
int nPort = 4567;
//创建完成端口对象,创建工作线程处理完成端口对象中事件
//参数说明:
//
//HANDLE WINAPI CreateIoCompletionPort(// __in HANDLE FileHandle,要关联的套节字句柄// __in_opt HANDLE ExistingCompletionPort,
//上面创建的完成端口对象句柄// __in ULONG_PTR CompletionKey,
//一个句柄惟一数据,它将与FILEHANDLE套节字句柄关联在一起。
//可在此存储任意类型的信息。// __in DWORD NumberOfConcurrentThreads//);
HANDLE hCompletion =::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
//在此只创建了一个完成端口对象
::CreateThread(NULL, 0, ServerThread,(LPVOID)hCompletion, 0, 0);
// 创建监听套节字,绑定到本地地址,开始监听
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM,0);
SOCKADDR_IN si;
si.sin_family = AF_INET;
si.sin_port = ::ntohs(nPort);
si.sin_addr.S_un.S_addr = INADDR_ANY;
::bind(sListen, (sockaddr*)&si,sizeof(si));
::listen(sListen, 5);
// 循环处理到来的连接
while(TRUE)
{
// 等待接受未决的连接请求
SOCKADDR_IN saRemote;
int nRemoteLen =sizeof(saRemote);
//创建新的接收套节字
SOCKET sNew = ::accept(sListen,(sockaddr*)&saRemote,&nRemoteLen);
//接受到新连接之后,为它创建一个per-handle数据,并将它们关联到完成端口对象。
PPER_HANDLE_DATA pPerHandle=
(PPER_HANDLE_DATA)::GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));
pPerHandle->s =sNew;
memcpy(&pPerHandle->addr,&saRemote, nRemoteLen);
//多个套节字对应一个I/O完成对象
::CreateIoCompletionPort((HANDLE)pPerHandle->s,hCompletion, (DWORD)pPerHandle, 0);
// 投递一个接收请求
PPER_IO_DATA pPerIO =(PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
pPerIO->nOperationType= OP_READ;
WSABUF buf;
buf.buf =pPerIO->buf;
buf.len =BUFFER_SIZE;
DWORD dwRecv;
DWORD dwFlags = 0;
::WSARecv(pPerHandle->s,&buf, 1, &dwRecv,&dwFlags,&pPerIO->ol, NULL);
}
}