IOCP

IOCP全称I/O Completion Port,中文译为I/O 完成端口IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给 应用程序。与使用select()或是其它异步方法不同的是,一个 套接字[socket]与一个 完成端口关联了起来,然后就可继续进行正常的 Winsock操作了。然而,当一个事件发生的时候,此 完成端口就将被 操作系统加入一个队列中。然后 应用程序可以对核心层进行查询以得到此 完成端口
这里我要对上面的一些概念略作补充,在解释[完成]两字之前,我想先简单的提一下同步和异步这两个概念,逻辑上来讲做完一件事后再去做另一件事就是同步,而同时一起做两件或两件以上事的话就是异步了。你也可以拿 单线程和多线程来作比喻。但是我们一定要将同步和堵塞,异步和非堵塞区分开来,所谓的堵塞 函数诸如accept(…),当调用此 函数后,此时线程将挂起,直到 操作系统来通知它,“HEY兄弟,有人连进来了”,那个挂起的线程将继续进行工作,也就符合”生产者-消费者”模型。堵塞和同步看上去有两分相似,但却是完全不同的概念。大家都知道I/O设备是个相对慢速的设备,不论打印机,调制解调器,甚至硬盘,与CPU相比都是奇慢无比的,坐下来等I/O的完成是一件不甚明智的事情,有时候数据的流动率非常惊人,把数据从你的 文件服务器中以Ethernet速度搬走,其速度可能高达每秒一百万字节,如果你尝试从文件服务器中读取100KB,在用户的眼光来看几乎是瞬间完成,但是,要知道,你的线程执行这个命令,已经浪费了10个一百万次 CPU周期。所以说,我们一般使用另一个 线程来进行I/O。重叠IO[overlapped I/O]是Win32的一项技术,你可以要求 操作系统为你传送数据,并且在传送完毕时通知你。这也就是[完成]的含义。这项技术使你的程序在I/O进行过程中仍然能够继续处理 事务。事实上, 操作系统内部正是以 线程来完成overlapped I/O。你可以获得线程所有利益,而不需要付出什么痛苦的代价。
完成端口中所谓的[端口]并不是我们在TCP/IP中所提到的端口,可以说是完全没有关系。我到现在也没想通一个I/O设备[I/O Device]和端口[IOCP中的Port]有什么关系。估计这个端口也迷惑了不少人。IOCP只不过是用来进行读写操作,和文件I/O倒是有些类似。既然是一个读写设备,我们所能要求它的只是在处理读与写上的高效。
下面是一个VC++实现IOCP服务器框架的流程:
当然,IOCP也是基于Winsock2套接字库的,所以在开始先必须初始化套接字库。
?
1
2
3
4
//初始化套接字库
  
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData );
IOCP除了需一个服务器套接字外,还需一个完成端口的句柄,该句柄直接调用API
?
1
2
3
4
5
//创建IOCP句柄
HANDLE m_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
  
//创建服务器套接字,这里要注意的是最后一个参数必须为:WSA_FLAG_OVERLAPPED 重叠模式
SOCKET m_Server = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
之后为套接字绑定一个本地端口,用来监听客户端的连接
?
1
2
3
4
5
SOCKADDR_IN addr;
addr.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" );
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
bind(m_Server,(SOCKADDR*)&addr, sizeof (SOCKADDR);
绑定端口之后,建立一个监听线程,用来监听客户端的连接,当有连接进来时,将该连接的套接字加入到IOCP对队列中,同时再创建几个工作线程,这样在该连接发生请求时,IOCP模型就会在工作线程通知,这样我们就可以在工作线程中,完成对客户端的请求做出一系列响应。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 监听线程流程
UINT WINAPI AcceptThreadProc( LPVOID lpParameter ){
sockaddr_in sockAddr = {0};
int len = sizeof (sockAddr);
while (TRUE){
SOCKET sNew = WSAAccept(m_Server,(sockaddr*)&sockAddr,&len,NULL,NULL);
// 将客户端加入到IOCP队列中
CreateIoCompletionPort(( HANDLE )sNew,m_hIocp, ULONG_PTR (sNew),0);
// 这里要注意一下,对网上一些相关的资料,一些新手在写IOCP时
// 会发现得不到用户请求及一些事件,哪是因为没有对该用户投递一个接收IO
// IOCP的工作原理就是,必须对一个连接投递一个投收IO,处理完一个,再投递一个...
OVERLAPPEDPLUS* ol; // ....对OL结构进行初始化后
int nRet = WSARecv(ol->s, &(ol->wsaBuf), 1, &(ol->dwBytes), &(ol->dwFlags), &(ol->ol), NULL);
int nError = WSAGetLastError();
// 在投递接收IO时。返回值要注意。如果为 nError == ERROR_IO_PENDING 说明已经投递成功。
}}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 工作线程流程
  
UINT WINAPI WorkerThreadProc( LPVOID lpParameter )
  
{
  
  DWORD   CompleteBytes = 0;
  
  DWORD   dwKey = 0;
  
  POVERLAPPEDPLUS ol = NULL;
  
  BOOL bRet = GetQueuedCompletionStatus(m_hIocp,&CompleteBytes,( PULONG_PTR )&dwKey,(LPOVERLAPPED*)&ol,INFINITE);
  
  if (!bRet)
  
  {
  
   // 客户端断开了连接
  
  }
  
   
  
  // 如果为接收时 CompleteBytes 为实际收到的大,发送时为实际发送的大小
  
  switch (ol->OpCode)
  
  {
  
   case IOCP_RECV:
  
    //.... 处理接收请求
  
    break ;
  
   case IOCP_SEND:
  
    //.... 处理发送请求
  
    break ;
  
  }
  
}
工作线程中循环调用了 GetQueuedCompletionStatus 这个API来取出当前活动的套接字,当套接字有数据应请求或者断开时。该函数也会立即返回。
如果要对工作线程停止,PostQueuedCompletionStatus 使用该API 向工作发送停止命令,工作线程收到此命令后,可以安全退出线程。
以上就是大致实现流程,仅限于VC++开发使用,由于其它平台语言实现起来有些困难,可以采用一些第三方做的 IOCP支持库,如网上现比较好用的有易语言和C++版的,IOCP服务器模型支持库,该第三方库写的比较好用。可以直接使用的。还有一些其它版本的。实现原理也是以上方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值