IOCP完成端口详解1

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

制作:王志海
if (ret == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { // Error } } // Put structure in look aside list for later use FreeOverlappedPlus(OverlapPlus); // Signal accept thread to issue another AcceptEx SetEvent(hAcceptThread); break; case OP_READ: // Process the data read // ••• // Repost the read if necessary, reusing the same // receive buffer as before memset(&OverlapPlus-> ol, 0, sizeof(OVERLAPPED)); ret = WSARecv( OverlapPlus-> s, &OverlapPlus-> wbuf, 1, &OverlapPlus-> dwBytes, &OverlapPlus-> dwFlags, &OverlapPlus-> ol, NULL); if (ret == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { // Error } } break; case OP_WRITE: // Process the data sent, etc.
制作:王志海
无论何时调用重叠操作函数时,总是会通过其 lpOverlapped 参数传递一个 OVERLAPPEDPLUS 结构(例如 WSASend、WSARecv 等函数)。这就允许你为每一个重 叠调用操作设置某些操作状态信息,当操作结束后,你可以通过 GetQueuedCompletionStatus()函数获得你自定义结构的指针。注意 OVERLAPPED 字 段不要求一定是这个扩展后的结构的第一个字段。当得到了指向 OVERLAPPED 结构的指 针以后,可以用 CONTAINING_RECORD 宏取出其中指向扩展结构的指针(译者注:以上 两小段一会是 OVERLAPPEDPLUS 结构,一会是 OVERLAPPED 结构,本人也看不太懂, 请高手赐教)。 OVERLAPPED 结构的定义如下: typedef struct _OVERLAPPEDPLUS { OVERLAPPED SOCKET int WSABUF DWORD } OVERLAPPEDPLUS; #define OP_READ #define OP_WRITE #define OP_ACCEPT 1 2 0 ol; s, sclient; OpCode; wbuf; dwBytes, dwFlags;
OVERLAPPEDPLUS *OverlapPlus,
制作:王志海
INFINITE); if (ret == 0) { // Operation failed continue; } OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol); switch (OverlapPlus-> OpCode) { case OP_ACCEPT: // Client socket is contained in OverlapPlus.sclient // Add client to completion port CreateIoCompletionPort( (HANDLE)OverlapPlus-> sclient, hIocp, (ULONG_PTR)0, 0); // Need a new OVERLAPPEDPLUS structure // for the newly accepted socket. Perhaps // keep a look aside list of free structures. newolp = AllocateOverlappedPlus(); if (!newolp) { // Error } newolp-> s = OverlapPlus-> sclient; newolp-> OpCode = OP_READ; // This function prepares the data to be sent PrepareSendBuffer(&newolp-> wbuf); ret = WSASend( newolp-> s, &newolp-> wbuf, 1, &newolp-> dwBytes, 0, &newolp.ol, NULL);
// other useful information
下面让我们来看看 Figure2 里工作者线程的情况。 Figure 2 Worker Thread DWORD WINAPI WorkerThread(LPVOID lpParam) { ULONG_PTR OVERLAPPED *PerHandleKey; *Overlap; *newolp; DWORD while (1) { ret = GetQueuedCompletionStatus( hIocp, &dwBytesXfered, (PULONG_PTR)&PerHandleKey, &Overlap, dwBytesXfered;
hIocp = Creatபைடு நூலகம்IoCompletionPort( INVALID_HANDLE_VALUE, NULL,
制作:王志海
(ULONG_PTR)0, 0); if (hIocp == NULL) { // Error }
完成端口创建后,要把将使用该完成端口的套接字与之关联起来。方法是再次调用 CreateIoCompletionPort ()函数,第一个参数 FileHandle 设为套接字的句柄,第二个参 数 ExistingCompletionPort 设为刚刚创建的那个完成端口的句柄。 以下代码创建了一个套接字,并把它和前面创建的完成端口关联起来: SOCKET s;
s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { // Error if (CreateIoCompletionPort((HANDLE)s, hIocp, (ULONG_PTR)0, 0) == NULL) { // Error } ??? }
这时就完成了套接字与完成端口的关联操作。在这个套接字上进行的任何重叠操作都 将通过完成端口发出完成通知。 注意, CreateIoCompletionPort()函数中的第三个参数用 来设置一个与该套接字相关的“完成键(completion key)”(译者注: 完成键可以是任何数据 类型)。每当完成通知到来时,应用程序可以读取相应的完成键,因此,完成键可用来给套 接字传递一些背景信息。 在创建了完成端口、将一个或多个套接字与之相关联之后,我们就要创建若干个 线程来处理完成通知。这些线程不断循环调用 GetQueuedCompletionStatus()函数并返 回完成通知。 下面,我们先来看看应用程序如何跟踪这些重叠操作。当应用程序调用一个重叠 操作函数时,要把指向一个 overlapped 结构的指针包括在其参数中。当操作完成后,我 们可以通过 GetQueuedCompletionStatus()函数中拿回这个指针。不过,单是根据这个 指针所指向的 overlapped 结构,应用程序并不能分辨究竟完成的是哪个操作。要实现对 操作的跟踪,你可以自己定义一个 OVERLAPPED 结构,在其中加入所需的跟踪信息。
制作:王志海
IOPC
完成端口详解(1) 通常要开发网络应用程序并不是一件轻松的事情,不过,实际上只要掌握几个关 键的原则也就可以了——创建和连接一个套接字,尝试进行连接,然后收发数据。真正难的 是要写出一个可以接纳少则一个,多则数千个连接的网络应用程序。本文将讨论如何通过 Winsock2 在 Windows NT 和 Windows 2000 上开发高扩展能力的 Winsock 应用程序。 文章主要的焦点在客户机/服务器模型的服务器这一方,当然,其中的许多要点对模型的双 方都适用。 API 与响应规模 通过 Win32 的重叠 I/O 机制,应用程序可以提请一项 I/O 操作,重叠的操作请 求在后台完成, 而同一时间提请操作的线程去做其他的事情。 等重叠操作完成后线程收到有 关的通知。这种机制对那些耗时的操作而言特别有用。不过,像 Windows 3.1 上的 WSAAsyncSelect()及 Unix 下的 select()那样的函数虽然易于使用,但是它们不能满足 响应规模的需要。而完成端口机制是针对操作系统内部进行了优化,在 Windows NT 和 Windows 2000 上,使用了完成端口的重叠 I/O 机制才能够真正扩大系统的响应规模。 完成端口 一个完成端口其实就是一个通知队列,由操作系统把已经完成的重叠 I/O 请求的 通知放入其中。当某项 I/O 操作一旦完成,某个可以对该操作结果进行处理的工作者线程 就会收到一则通知。而套接字在被创建后,可以在任何时候与某个完成端口进行关联。 通常情况下,我们会在应用程序中创建一定数量的工作者线程来处理这些通知。 线程数量取决于应用程序的特定需要。理想的情况是,线程数量等于处理器的数量,不过这 也要求任何线程都不应该执行诸如同步读写、 等待事件通知等阻塞型的操作, 以免线程阻塞。 每个线程都将分到一定的 CPU 时间,在此期间该线程可以运行,然后另一个线程将分到一 个时间片并开始执行。 如果某个线程执行了阻塞型的操作, 操作系统将剥夺其未使用的剩余 时间片并让其它线程开始执行。也就是说,前一个线程没有充分使用其时间片,当发生这样 的情况时,应用程序应该准备其它线程来充分利用这些时间片。 完成端口的使用分为两步。首先创建完成端口,如以下代码所示: HANDLE hIocp;
制作:王志海
break; } // switch } // while } // WorkerThread
其中每句柄键(PerHandleKey)变量的内容,是在把完成端口与套接字进行关联时所 设置的完成键参数;Overlap 参数返回的是一个指向发出重叠操作时所使用的那个 OVERLAPPEDPLUS 结构的指针。 要记住,如果重叠操作调用失败时(也就是说,返回值是 SOCKET_ERROR,并 且错误原因不是 WSA_IO_PENDING),那么完成端口将不会收到任何完成通知。如果重 叠操作调用成功, 或者发生原因是 WSA_IO_PENDING 的错误时, 完成端口将总是能够收 到完成通知。 Windows NT 和 Windows 2000 的套接字架构对于开发大响应规模的 Winsock 应用程序而言,对 Windows NT 和 Windows 2000 的套接字架构有基本的了解是很有帮 助的。 与其它类型操作系统不同,Windows NT 和 Windows 2000 的传输协议没有一 种风格像套接字那样的、 可以和应用程序直接交谈的界面, 而是采用了一种更为底层的 API, 叫做传输驱动程序界面(Transport Driver Interface,TDI)。Winsock 的核心模式驱动程 序负责连接和缓冲区管理,以便向应用程序提供套接字仿真(在 AFD.SYS 文件中实现),同 时负责与底层传输驱动程序对话。 完成端口详解(2) [转] 谁来负责管理缓冲区? 正如上面所说的,应用程序通过 Winsock 来和传输协议驱动程序交谈,而 AFD.SYS 负责为应用程序进行缓冲区管理。也就是说,当应用程序调用 send()或 WSASend()函数来发送数据时,AFD.SYS 将把数据拷贝进它自己的内部缓冲区(取决于 SO_SNDBUF 设定值),然后 send()或 WSASend()函数立即返回。也可以这么说, AFD.SYS 在后台负责把数据发送出去。不过,如果应用程序要求发出的数据超过了 SO_SNDBUF 设定的缓冲区大小, 那么 WSASend()函数会阻塞, 直至所有数据发送完毕。 从远程客户端接收数据的情况也类似。 只要不用从应用程序那里接收大量的数据, 而且没有超出 SO_RCVBUF 设定的值,AFD.SYS 将把数据先拷贝到其内部缓冲区中。当 应用程序调用 recv()或 WSARecv()函数时,数据将从内部缓冲拷贝到应用程序提供的缓 冲区。 多数情况下,这样的架构运行良好,特别在是应用程序采用传统的套接字下非重 叠的 send()和 receive()模式编写的时候。不过程序员要小心的是,尽管可以通过 setsockopt()这个 API 来把 SO_SNDBUF 和 SO_RCVBUF 选项值设成 0(关闭内部缓冲
相关文档
最新文档