关于IOCP网上到处都是资料,说的也很详细。我在这里就不再多说了,这只是本人在学习IOCP时的笔记,和配合AcceptEx写的一个极小的服务端程序。由于刚刚接触ICOP加上本人刚毕业不到一年,所以里面的理解或观点可能有误,还请大家多多批评!
VC6.0开发,旨在体现IOCP的架构,忽略细节,服务程序的功能只是接收客户连接,接着接收到客户数据,然后原封不动的返回给客户!
下面这段话,如果不感兴趣,可以跳过去,直接看代码...
先说IOCP,其实思路很清晰:
1.声明一个数据结构,用来存放客户套接字和客户信息
2.声明一个包含OVERLAPPED字段的I/O结构
3.创建完成端口
4.创建服务线程
5.接收客户端连接请求
6.关联这个套接字到完成端口中
7.服务线程中不断的等待I/O结果,在结果中提供服务和根据需要发起另一个异步操作。
按照这个思路很快的写出了一个服务程序,但是遇到了下面的问题:
1.WSAGetLastError()返回10045,找了半天才发现发起重叠操作时候,WSARecv中flag参数没有初始化,需要初始化赋值为0。
2.在GetQueuedCompletionStatus中,没有错误,但总是返回读取的字数为0。I/O重叠结构中也收不到任何字符。这时候我就在这里用了一下recv()函数,在recv中却可以收到来自客户端发送的数据。难道每次都要自己recv()?肯定不是!如果那样还用扩展的I/O结果何用。一定是哪里指定了接收的数目,而自己不小心指定为0了,所以没有接收数据。找了半天果然如此。在发起重叠操作时候,扩展的I/O中WSABUF的赋值有问题。
我的错误:wsaBuf.len = (I/O结构).len;
改为: wsaBuf.len = (I/O结构).len = DATABUF_SIZE;
修改之后终于可以接收和发送数据了。
为什么要用AcceptEx?
在学习IOCP时,看到一位大神写的文章,他用客户端开了3W个线程同时连接服务端和发送数据,我好奇就也开了3W个线程去同时连接服务端,结果很多都printf连接失败的信息!再看看大神的文章,再搜一下AcceptEx。对比accept,觉得AcceptEx确实很强大。AcceptEx和accept主要的区别就在于接收套接字:
accept函数是等待客户连接进来之后才创建套接字,虽然在我们看到的就是一个socket函数,但是在函数背后,系统应该会消耗不少资源,因为它要打通一个和外界通讯的路。如果大量套接字并发接入,难免有的套接字不能及时创建和接收。
AcceptEx则是事先创建好套接字,坐等客户端的连接就行了。
但是,AcceptEx相比accept确实复杂了很多。原来一句accept就可以解决的,现在却要为AcceptEx做很多服务,但是只要理清思路,这个做起来也是很从容的。
1.创建一个监听套接字
2.将监听套接字关联到完成端口中
3.对监听套接字调用bind()、listen()
4.通过WSAIoctl获取AcceptEx、GetAcceptExSockaddrs函数的指针
5.创建一个用于接收客户连接的套接字
6.用获取到的AcceptEx函数指针发起用于接收连接的异步操作
7.服务器接收到连接的套接字,设置一下它的属性(有人说没有必要)。用这个接收到的套接字去发起重叠的I/O操作。
8.多次重复5,6就是多次发起接收连接的异步操作的过程。
对于第4步,为什么要获取AcceptEx的指针,而不是直接就调用AcceptEx这个函数呢?网上找到的资料是这么说的:
Winsock2的其他供应商不一定会实现AcceptEx函数。同样情况也包括的其他Microsoft的特定APIs如TransmitFile,GetAcceptExSockAddrs以及其他Microsoft将在以后版本的windows里。
在运行WinNT和Win2000的系统上,这些APIs在Microsoft提供的DLL(mswsock.dll)里实现,可以通过链接mswsock.lib或者通过WSAioctl的SIO_GET_EXTENSION_FUNCTION_POINTER操作动态调用这些扩展APIs.
未获取函数指针就调用函数(如直接连接mswsock.lib并直接调用AcceptEx)的消耗是很大的,因为AcceptEx实际上是存在于Winsock2结构体系之外的。每次应用程序常试在服务提供层上(mswsock之上