《Windows网络与通信程序设计》读书笔记----IOCP与可伸缩网络程序
来源:互联网 发布:铃声放大软件 编辑:程序博客网 时间:2024/05/22 11:56
完成端口I/O模型
I/O完成端口是应用程序使用线程池处理I/O请求的一种机制。处理多个并发异步I/O请求时,使用I/O完成端口比在I/O请求时创建线程更快更有效。
CreateIoCompletionPort函数有点复杂,需要说明一下
CreateIoCompletionPort函数有两个功能。
1、创建一个完成端口对象。
2、将一个或者多个文件句柄(套接字句柄)关联到I/O完成端口对象。
其中在第二步中,为该完成端口关联套接字句柄时,需要用到前3个参数。
Filehandle 要关联的套接字句柄
ExistingCompletion 已经创建的完成端口对象句柄
CompletionKey 指定一个句柄唯一(per-handle)数据,它将与FIleHandle套接字句柄关联在一起。应用程序可以在此存储任意类型信息,通常是一个指针。
CompletionKey参数通常用来描述与套接字相关的信息,所以称它为句柄唯一(per=handle)数据。
向完成端口关联套接字句柄之后,便可以通过在套接字上投递重叠发送和接收请求处理I/O了。在这些I/O操作完成时,I/O系统会向完成端口对象发送一个完成通知封包。I/O完成端口以先进选出的方式为这些封包排队。应用程序使用GetQueuedCompletionSatus函数可以取得这些队列中的封包。这个函数应该在处理完成端口对象I/O的服务线程中调用。
GetQueuedCompletionStatus( HANDLECompletionPort, //完成端口对象句柄 LPDWORDlpNumberOfBytes //取得I/O操作期间传输的字节数 PULONG_PTRlpCompletionKey, //取得在关联套接字时指定的句柄唯一数据(指针) LPOVERLAPPED*lpOverlapped, //取得投递I/O操作时指定的OVERLAPPED 结构 DWORD dwMilliseconds //如果完成端口没有完成封包,此参数指定了等待的事件,INFINIE为无穷大)
I/O服务线程调用GetQueuedCompletionStatus函数取得有事件发生的套接字的信息,通过lpNumberOfBytes参数得到传输的字节数量,通过lpCompletionKey参数得到与套接字关联的句柄唯一(per-handle)数据,通过lpOverlapped参数得到投递I/O请求时使用的重叠对象地址,进一步得到I/O唯一(per-I/O)数据。
lpCompletionKey参数包含了我们称为per-Handle的数据,因为当套接字第一次与完成端口关联时,这个数据就关联到一个套接字句柄。这是传递给CreateIoCompletionPort函数的CompletionKey参数。
lpOverlapped参数指向一个OVERLAPPED结构,结构后面便是我们称为per-I/O的数据,这可以是工作线程处理完成封包时想要知道的任何信息。
个人感觉,这两个数据结构的设计和程序整体设计很有联系。
一个简单示例具体编程流程:
1、创建一个完成端口对象,创建一个或多个服务线程,服务线程调用GetQueuedCompletionStatus取得完成I/O通知信息。主线程接收连接,将新套接字关联到完成端口上,然后在新连接上投递Read I/O异步请求。
2、服务线程从GetQueuedCompletionStatus函数返回后,根据per-handle I/O数据中的信息,做出相应的处理,并继续投递下一个Read I/O异步请求。
#define _WIN32_WINNT 0x0400 #include<windows.h>#include<cstdio>#include"InitSocket.h"#define BUFFER_SIZE 2048CInitSock initSock ; //进入main函数前已经进行了初始化typedefstruct _PER_HANDLE_DATA//per-handle数据{SOCKET s ;//对应的套接字句柄sockaddr_in addr ;//客户方地址} PER_HANDLE_DATA ,*PPER_HANDLE_DATA ;/********************************************************///包含版本typedefstruct _PER_IO_DATA//per-I/O数据{OVERLAPPED ol ;//重叠结构,必须放作第一个结构,用C++派生的方法更好char buf[BUFFER_SIZE] ;//数据缓冲区int nOperationType ;//操作类型#defineOP_READ 1//操作类型码#defineOP_WRITE 2#defineOP_ACCEPT 3} PER_IO_DATA,*PPER_IO_DATA ;/**********************************************************//*********************************************************** * 派生版本的class _PER_IO_DATA : public OVERLAPPED{public :char buf[BUFFER_SIZE] ;int nOperationType ;#defineOP_READ 1//操作类型码#defineOP_WRITE 2#defineOP_ACCEPT 3} ;typedef _PER_IO_DATA PER_IO_DATA ;typedefPER_IO_DATA *PPER_IO_DATA;***************************************************/DWORD WINAPI ServerThread(LPVOID lpParam) ;int main(void){int nPort = 4567 ;//创建完成端口对象,创建工作线程处理完成端口对象中的事件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_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) ;CreateIoCompletionPort((HANDLE)pPerHandle->s,hCompletion,(DWORD)pPerHandle,0) ; //关联,不是创建.涉及到转型,将指针转为DWORD,与pPerHandle->s关联的唯一数据就是PerHandle//投递一个接收请求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 ;//作为引子的接收请求,引发接收操作//与该套接字相关的OVERLAPPED结构WSARecv(pPerHandle->s,&buf,1,&dwRecv,&dwFlags,&pPerIO->ol,NULL) ; }return 0 ;}//服务线程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请求完成了{case OP_READ : //完成一个接收请求{pPerIO->buf[dwTrans] = '\0' ;printf(pPerIO->buf) ;//继续投递接收I/O请求WSABUF buf ;buf.buf = pPerIO->buf ;buf.len = BUFFER_SIZE ;pPerIO->nOperationType = OP_READ ;DWORD nFlags = 0 ;WSARecv(pPerHandle->s,&buf,1,&dwTrans,&nFlags,&pPerIO->ol,NULL) ; //继续引发在该套接字上面的接收操作//投递一个发送请求I/O,我自己添加,为了测试OR_WRITE之用PPER_IO_DATA pSendPerIO = (PPER_IO_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_DATA)) ;pSendPerIO->nOperationType = OP_WRITE ;memcpy(pSendPerIO->buf,pPerIO->buf,dwTrans) ;WSABUF dbuf ;dbuf.buf = pSendPerIO->buf ;dbuf.len = dwTrans ; //如果大于客户端的接收缓冲区,则客户端需要重复多次recv才能接收完所有数据WSASend(pPerHandle->s,&dbuf,1,&dwTrans,nFlags,&pSendPerIO->ol,NULL); //引发在该套接字上面的发送操作} break ;case OP_WRITE :{printf("发送数据完成\n");}break ;case OP_ACCEPT : //这个多余的,因为主线程因为在accpet了break ;}}return 0 ;}
关于IOCP机制的的更多说明,请参考另外一篇博文:《Windows核心编程 5th》部分读书笔记----第10章 同步设备I/O与异步设备I/O
- 《Windows网络与通信程序设计》读书笔记----IOCP与可伸缩网络程序
- 《Windows网络与通信程序设计》读书笔记----可伸缩服务器系统设计实例
- 完成端口封装(修复Windows 网络与通信程序设计 可伸缩IOCP模型的bug)
- WIN网络编程-IOCP与可伸缩网络程序
- 《Windows网络与通信程序设计》读书笔记-----WSAAsyncSelect模型
- 《Windows网络与通信程序设计》读书笔记----WSAEventSelect模型
- 《Windows网络与通信程序设计》读书笔记----重叠(Overlapped)I/O模型
- 《Windows网络与通信程序设计》读书笔试----select模型
- 学习《windows网络与通信程序设计》一
- 《Windows网络与通信程序设计》——示例:地址信息
- Windows 网络与通信程序设计 王艳平 Phoenix 金羽 防火墙
- IOCP模型与网络编程
- IOCP模型与网络编程
- IOCP模型与网络编程
- IOCP模型与网络编程
- IOCP模型与网络编程
- IOCP模型与网络编程
- IOCP模型与网络编程
- 进程间通信——FIFO
- WEB项目中log4j的使用及介绍
- Linux系统脚本的三种执行方式
- Java_awt_图形用户界面GUI简单应用
- [5月3日的脚本] 如何获取某指定数据库中所有的存储过程
- 《Windows网络与通信程序设计》读书笔记----IOCP与可伸缩网络程序
- 【开源电子DIY的盛夏】Embedded Pi 五月携手Raspeberry Pi & Arduino
- 悲观所和乐观所的初步认识
- 水晶报表(使用VS2010配合水晶报表)
- 安装前准备(接上)
- 实习生电面2--亚信联创
- assets和res/raw的区别
- ROWID和命令对应
- jsoup 简介及简单使用