完成端口模型开发
2013-11-20 23:14
295 查看
完成端口模型实现步骤:
1.创建完成端口
2.创建服务线程,通常服务线程数量为CPU数量的2倍
3.将套接字与完成端口关联在一起
4.调用输入输出函数,发起重叠I/O操作
5.在服务线程中,在完成端口上等待重叠I/O操作结果
一、创建完成端口对象
要在应用程序中利用完成端口模型,就必须首次创建完成端口对象。CreateIoCompletionPort()函数实现此功能,函数声明如下:
如果该函数调用成功则返回完成端口的句柄,如果失败则返回NULL.
在套接字应用程序中,要创建完成端口对象,需要设置FileHandle参数为INVALID_HANDLE_VALUE,ExisstingCompletionPort参数为NULL,CompletionKey参数为0,NumberOfConcurrentThreads参数为0,代码如下:
二、创建服务线程
创建完成端口后,在创建服务线程。首先应用程序要获得计算机CPU的数量,着可以通过调用GetSystemInfo()函数来完成,然后调用CreateThread()或_beginthreadex()函数创建服务线程。通常应用程序要把完成例程作为线程参数传递给服务线程。
以下代码演示了创建服务线程的过程:
服务线程开始工作后有3种状态:
(1)等待状态。在没有I/O操作完成通知包被投递到完成端口时,服务线程在完成端口上等待。
(2)服务状态。当I/O操作完成通知到达时,服务线程按照LIFO方式被唤醒,开始为客户端提供服务,当完成服务后,服务线程回到完成端口,继续等待。
(3)阻塞状态。为客户端提供服务的线程,调用了Sleep()之类的函数,线程进入阻塞状态。
三、套接字与完成端口关联
创建完成端口后,需要将套接字与完成端口关联在一起,此功能还是通过调用CreateIoCompletionPort()函数来实现,如调用成功返回端口句柄,即ExistingCompletionPort,失败则返回NULL.
在套接字应用程序中,调用该函数即告知系统当I/O操作完成时,向完成端口发送一个I/O操作完成通知包。这些通知包按照FIFO方式,在完成端口上排队等待服务线程的读取。
以下程序中_completionKey结构体保存客户端套接字和地址,在调用CreateIoCompletionPort()函数时,将该结构体指针作为完成键传递进去。
四、发起重叠I/O操作
将套接字与完成端口关联起来后,应用程序调用下面的函数,发起发起重叠I/O操作
(1)WSASend()和WSASenTo()函数:发送数据
(2)WSARecv()和WSARecvFrom()函数:接收数据
在应用程序中,通常声明一个和I/O操作相关的结构体,以保存I/O操作的相关信息
以下代码演示了调用WSARecv()函数发起异步接收数据的过程
如果函数返回ERROR_IO_PENDING,则说明成功的发起了一个异步接收数据操作。
五、等待重叠I/O操作结果
服务线程被启动后,调用GetQueuedCompletionStatus()(该函数的用法请参看MSDN)函数等待重叠I/O操作的完成结果。当重叠I/O操作完成时,I/O操作完成通知包被发送到完成端口上,此时该函数返回。完成通知包包含的信息有传输的字节数,完成键和重叠结构。
以下代码演示了GetQueuedCompletionStatus()函数的用法:
六、取消异步操作
当关闭套接字应用程序时,如果此时系统中还有未完成的异步操作,那么应用程序可以调用CanceIo()函数取消等待执行的异步操作。该函数调用成功则返回值为TRUE,所有在曦套接字上等待的异步操作都被成功地取消。
七、投递完成通知包
当服务线程退出,应用程序可以调用PostQueuedCompletionStatus()(Posts an I/O completion packet to an I/O completion port.)函数想服务线程发送一个特殊的完成通知包。在服务线程中,接收到这通知包后,线程退出。
PostQueuedCompletionStatus()函数功能为向完成端口上发送一个I/O操作完成通知包。在服务线程中,GetQueuedCompletionStatus()函数返回调用PostQueuedCompletionStatus()函数时传递的第2个、第3个和第4个参数值。
以下代码表明利用PostQueuedCompletionStatus()函数向服务线程发送退出通知包的过程。设置PostQueuedCompletionStatus()函数的第3和第4个参数为NULL。在服务线程中,根据GetQueuedCompletionStatus()函数的第3和第4个参数值为NULL,服务线程退出。
这个笔记终于记完了。so pain
1.创建完成端口
2.创建服务线程,通常服务线程数量为CPU数量的2倍
3.将套接字与完成端口关联在一起
4.调用输入输出函数,发起重叠I/O操作
5.在服务线程中,在完成端口上等待重叠I/O操作结果
一、创建完成端口对象
要在应用程序中利用完成端口模型,就必须首次创建完成端口对象。CreateIoCompletionPort()函数实现此功能,函数声明如下:
HANDLE CreateIoCompletionPort( HANDLE FileHandle, //文件句柄 HANDLE ExistingCompletionPort, //存在的完成端口句柄 DWORD CompletionKey, //完成键,通常应用程序利用该参数保存于套接字相关信息 DWORD NumberOfConcurrentThreads //完成端口并发线程的数量。如果为0,则通知系统完成例程并发线程的数量等于CPU的数量 );
如果该函数调用成功则返回完成端口的句柄,如果失败则返回NULL.
在套接字应用程序中,要创建完成端口对象,需要设置FileHandle参数为INVALID_HANDLE_VALUE,ExisstingCompletionPort参数为NULL,CompletionKey参数为0,NumberOfConcurrentThreads参数为0,代码如下:
HANDLE hIoCompletionPort; hIoCompletionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
二、创建服务线程
创建完成端口后,在创建服务线程。首先应用程序要获得计算机CPU的数量,着可以通过调用GetSystemInfo()函数来完成,然后调用CreateThread()或_beginthreadex()函数创建服务线程。通常应用程序要把完成例程作为线程参数传递给服务线程。
以下代码演示了创建服务线程的过程:
//服务线程函数 DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID); SYSTEM_INFO SystemInfo; //计算机系统信息 GetSystemInfo(&SystemInfo); //获取计算机系统信息 for(int i=0;i<SystemInfo.dwNumberOfProcessors*2;i++) { HANDLE ThreadHandle; DWORD ThreadID; //创建服务线程 if((ThreadHandle=CreateThread(NULL,0,ServerWorkerThread,CreateIoCompletionPort,0,&ThreadID))==NULL) { printf("调用创建线程函数失败!%d\n",GetLastError()); return; } CloseHnadle(ThreadHandle); }
服务线程开始工作后有3种状态:
(1)等待状态。在没有I/O操作完成通知包被投递到完成端口时,服务线程在完成端口上等待。
(2)服务状态。当I/O操作完成通知到达时,服务线程按照LIFO方式被唤醒,开始为客户端提供服务,当完成服务后,服务线程回到完成端口,继续等待。
(3)阻塞状态。为客户端提供服务的线程,调用了Sleep()之类的函数,线程进入阻塞状态。
三、套接字与完成端口关联
创建完成端口后,需要将套接字与完成端口关联在一起,此功能还是通过调用CreateIoCompletionPort()函数来实现,如调用成功返回端口句柄,即ExistingCompletionPort,失败则返回NULL.
在套接字应用程序中,调用该函数即告知系统当I/O操作完成时,向完成端口发送一个I/O操作完成通知包。这些通知包按照FIFO方式,在完成端口上排队等待服务线程的读取。
以下程序中_completionKey结构体保存客户端套接字和地址,在调用CreateIoCompletionPort()函数时,将该结构体指针作为完成键传递进去。
//完成键 //结构体 typedef struct _completionKey { SOCKET s; SOCKADDR_IN clientAddr; //客户端 }COMPLETIONKEY,*PCOMPLETIONKEY; SOCKET sListen; //服务器监听套接字 SOCKET sAccept; //客户端套接字 HANDLE hIoCompletionPort; //完成端口 PCOMPLETIONKEY pCompleKey; //完成键 SOCKADDR_IN addr; //服务器地址 SOCKADDR_IN clientLen=sizeof(addr); sAccept=accept(sListen,(SOCKADDR*)&addr,&addr); //接受客户端连接请求 //定义完成键 pCompleKey->s=sAccept; //端套接字 pCompleKey->clientAddr=addr; //地址 //将套接字与完成端口关联起来 HANDLE h=CreateIoCompletionPort((HANDLE)sAccept,hIoCompletionPort,(DWORD)pCompleKey,0)
四、发起重叠I/O操作
将套接字与完成端口关联起来后,应用程序调用下面的函数,发起发起重叠I/O操作
(1)WSASend()和WSASenTo()函数:发送数据
(2)WSARecv()和WSARecvFrom()函数:接收数据
在应用程序中,通常声明一个和I/O操作相关的结构体,以保存I/O操作的相关信息
以下代码演示了调用WSARecv()函数发起异步接收数据的过程
如果函数返回ERROR_IO_PENDING,则说明成功的发起了一个异步接收数据操作。
#define DATA_BUFSIZE 512 SOCKET sAccept //客户端套接字 //I/O操作数据结构 typedef struct _io_operation_data { WSAOVERLAPPEO overlapped; //重叠结构 WSABUF dataBuf; CHAR buffer[DATA_BUFSIZE]; //接收数据缓存区 }IO_OPERATION_DATA,*PIO_OPERATION_DATA; //定义I/O操作数据 PIO_OPERATION_DATA pIoDate; pIoDate->dataBuf.buf=pIoDate->buffer; pIoDate->data.len=DATA_BUFSIZE; ZeroMemory(&pIoDate->overlapped,sizeof(pIoDate->overlapped)) DWORD flags=0; DWORD recvBytes; //接收数据 if(WSARecv(sAccept,&(pIoDate->dataBuf),1,&recvBytes,&flags,&(pIoDate->overlapped),NULL)==SOCKET_ERROR) { if(WSAGetLastError()!=ERROR_IO_PENDING) //成功发起异步接收数据操作 { printf("调用WSARecv()函数失败%d\n",WSAGetLastError()); return; } }
五、等待重叠I/O操作结果
服务线程被启动后,调用GetQueuedCompletionStatus()(该函数的用法请参看MSDN)函数等待重叠I/O操作的完成结果。当重叠I/O操作完成时,I/O操作完成通知包被发送到完成端口上,此时该函数返回。完成通知包包含的信息有传输的字节数,完成键和重叠结构。
以下代码演示了GetQueuedCompletionStatus()函数的用法:
#define THREAD_SLEEP_TIME //超时时间 //完成键 typedef struct_completionKey { SOCKET s; SOCKADDR_IN clientAddr; //客户端地址 }COMPLETIONKEY,*PCOMPLETIONKEY; HANDLE hIoComPort; //完成端口 DWORD dwNumberOfByte; //传输字节 PCOMPLETIONKEY pCompletionKey; //完成键 LPOVERLAPPED lpOverlapped; //重叠结构指针 BOOL bRet=GetQueuedCompletionStatus(hIoComPort,&dwNumberOfByte,(LPDWORD )pCompletionKey,&lpOverlapped,THREAD_SLEEP_TIME); if(TRUE==bRet) { //成功的I/O异步操作,处理数据 } else { int nErrCode=WSAGetLastError(); if(NULL!=lpOverlapped) { //检查错误代码 } else if(WAIT_TIMEOUT==nErrCode) { //函数调用超时 continue; } else { //检查错误代码 } }
六、取消异步操作
BOOL CancelIo { HANDLE hFile; }
当关闭套接字应用程序时,如果此时系统中还有未完成的异步操作,那么应用程序可以调用CanceIo()函数取消等待执行的异步操作。该函数调用成功则返回值为TRUE,所有在曦套接字上等待的异步操作都被成功地取消。
七、投递完成通知包
当服务线程退出,应用程序可以调用PostQueuedCompletionStatus()(Posts an I/O completion packet to an I/O completion port.)函数想服务线程发送一个特殊的完成通知包。在服务线程中,接收到这通知包后,线程退出。
PostQueuedCompletionStatus()函数功能为向完成端口上发送一个I/O操作完成通知包。在服务线程中,GetQueuedCompletionStatus()函数返回调用PostQueuedCompletionStatus()函数时传递的第2个、第3个和第4个参数值。
以下代码表明利用PostQueuedCompletionStatus()函数向服务线程发送退出通知包的过程。设置PostQueuedCompletionStatus()函数的第3和第4个参数为NULL。在服务线程中,根据GetQueuedCompletionStatus()函数的第3和第4个参数值为NULL,服务线程退出。
HANDLE hIoComPort; //完成端口 PCOMPLETIONKEY pCompletionKey; //完成键 LPOVERLAPPED pOverlapped; //重叠结构指针 DWORD dwNumberOfByte; //传输字节数 //发送服务线程退出通知包 PostQueuedCompletionStatus(hIoComPort,0,(DWORD)NULL,NULL); //等待重叠I/O操作结果 GetQueuedCompletionStatus(hIoComPort,&dwNumberOfByte,(LPDWORD)pCompletionKey,&pOverlapped,THREAD_SLEEP_TIME); //其他处理 if(NULL==pOverlapped&&NULL==pCompletionKey) { //服务线程退出 }
这个笔记终于记完了。so pain
相关文章推荐
- 完成端口模型
- 用完成端口开发大响应规模的Winsock应用程序(5/完)
- winsock 完成端口 简单服务器模型
- winsock 完成端口 服务器模型(1)
- 完成端口开发大规模响应的Winsock应用程序
- 用完成端口开发大响应规模的Winsock应用程序(转载)
- 摘译:用完成端口开发大响应规模的Winsock应用程序
- 用完成端口开发大响应规模的Winsock应用程序
- 完成端口模型简介(zz)
- winsock IO 模型之五:完成端口
- 完成端口开发之QA(常见问题,持续添加)
- iOS开发UI基础—21使用嵌套模型完成的一个简单汽车图标展示程序
- [zz]CompletionPort 完成端口 用完成端口开发大响应规模的Winsock应用程序
- 转载-用完成端口开发大响应规模的Winsock应用程序
- winsock 完成端口 服务器模型(2)
- 稳定的完成端口开发细节讨论
- 完成端口与高性能服务器程序开发
- iOS开发UI篇—使用嵌套模型完成的一个简单汽车图标展示程序
- 用完成端口开发大响应规模的Winsock应用程序
- 手把手教你完成端口之二(应用中的完成端口简单模型)