dw网站的滑屏怎么做,乐清建设路小学校园网站,网站换域名图片这么设置,山西省建设厅网站首页转载请注明来源#xff1a;http://blog.csdn.net/caoshiying?viewmodecontents 一、回顾重叠IO模型 用完成例程来实现重叠I/O比用事件通知简单得多。在这个模型中#xff0c;主线程只用不停的接受连接即可#xff1b;辅助线程判断有没有新的客户端连接被建立#xff0c;如… 转载请注明来源http://blog.csdn.net/caoshiying?viewmodecontents 一、回顾重叠IO模型 用完成例程来实现重叠I/O比用事件通知简单得多。在这个模型中主线程只用不停的接受连接即可辅助线程判断有没有新的客户端连接被建立如果有就为那个客户端套接字激活一个异步的WSARecv操作然后调用SleepEx使线程处于一种可警告的等待状态以使得I/O完成后CompletionROUTINE可以被内核调用。如果辅助线程不调用SleepEx则内核在完成一次I/O操作后无法调用完成例程因为完成例程的运行应该和当初激活WSARecv异步操作的代码在同一个线程之内。 完成例程内的实现代码比较简单它取出接收到的数据然后将数据原封不动的发送给客户端最后重新激活另一个WSARecv异步操作。注意在这里用到了“尾随数据”。我们在调用WSARecv的时候参数lpOverlapped实际上指向一个比它大得多的结构PER_IO_OPERATION_DATA这个结构除了WSAOVERLAPPED以外还被我们附加了缓冲区的结构信息另外还包括客户端套接字等重要的信息。这样在完成例程中通过参数lpOverlapped拿到的不仅仅是WSAOVERLAPPED结构还有后边尾随的包含客户端套接字和接收数据缓冲区等重要信息。这样的C语言技巧在我介绍完成端口的时候还会使用到。 二、完成端口模型 “完成端口”模型是迄今为止最为复杂的一种I/O模型。然而假若一个应用程序同时需要管理为数众多的套接字那么采用这种模型往往可以达到最佳的系统性能但不幸的是该模型只适用于Windows NT和Windows 2000操作系统。因其设计的复杂性只有在你的应用程序需要同时管理数百乃至上千个套接字的时候而且希望随着系统内安装的CPU数量的增多应用程序的性能也可以线性提升才应考虑采用“完成端口”模型。要记住的一个基本准则是假如要为Windows NT或Windows 2000开发高性能的服务器应用同时希望为大量套接字I/O请求提供服务Web服务器便是这方面的典型例子那么I/O完成端口模型便是最佳选择 完成端口模型是我最喜爱的一种模型。虽然其实现比较复杂其实我觉得它的实现比用事件通知实现的重叠I/O简单多了但其效率是惊人的。我在T公司的时候曾经帮同事写过一个邮件服务器的性能测试程序用的就是完成端口模型。结果表明完成端口模型在多连接成千上万的情况下仅仅依靠一两个辅助线程就可以达到非常高的吞吐量。 三、关键函数 1、CreateIoCompletionPort 创建一个输入/输出I / O完成端口并将其与一个指定的文件句柄关联或者创建一个尚未与文件句柄关联的I / O完成端口允许在稍后的时间关联。将已打开的文件句柄的实例与一个I / O完成端口关联允许一个进程接收包含该文件句柄的异步I / O操作完成的通知。注意这里所使用的术语文件句柄是指代表一个重叠的I / O端点的系统抽象而不仅仅是磁盘上的一个文件。任何系统对象支持重叠I / o-such网络端点TCP套接字命名管道、邮件槽可以作为文件句柄。 函数原型 HANDLE WINAPI CreateIoCompletionPort(_In_ HANDLE FileHandle,_In_opt_ HANDLE ExistingCompletionPort,_In_ ULONG_PTR CompletionKey,_In_ DWORD NumberOfConcurrentThreads
); 函数参数 FileHandle一个打开的文件句柄或者INVALID_HANDLE_VALUE。这个文件句柄必须是支持重叠IO的object。如果提供了句柄, 它必须是已经给重叠I/O模型完成端口打开的句柄。例如如果您使用CreateFile函数获取的句柄那么您在调用这个函数时必须在参数中指定FILE_FLAG_OVERLAPPED旗标。如果指定 INVALID_HANDLE_VALUE那么函数将创建一个没有关联文件句柄的IO完成端口模型此外ExistingCompletionPort参数必须设为NULLCompletionKey参数将被忽略。 ExistingCompletionPort是已经存在的完成端口。如果为NULL则为新建一个IOCP。 CompletionKey用户定义的句柄包含的I/O完成包信息。当FileHandle被设为INVALID_HANDLE_VALUE时此参数被忽略。 NumberOfConcurrentThreads操作系统可以允许同时处理I / O完成端口的I / O完成数据包的线程的最大数目。如果existingcompletionport参数不为空则忽略此参数。如果这个参数为零系统允许多个并发运行的线程因为系统中有处理器。 返回值 如果函数成功返回值是一个I / O完成端口的句柄如果ExistingCompletionPort参数为空返回值是一个新的处理。如果ExistingCompletionPort参数是一个有效的I/O完成端口句柄返回值是相同的处理。如果文件句柄参数是一个有效的处理文件处理是现在与返回的I/O完成端口。如果函数失败返回值为空。为了获得更多的错误信息调用GetLastError函数。 2、GetQueuedCompletionStatus 失望的是微软官方MSDN没有提供关于这个API的说明。以下参照一篇英文文档进行翻译。文档说这个函数试图将一个I/O完成包从指定的I/O完成端口。如果没有完成数据包队列则函数等待一个挂起的I / O操作与完成端口相关联的完成。 函数原型 BOOL WINAPI GetQueuedCompletionStatus(_In_ HANDLE CompletionPort,_Out_ LPDWORD lpNumberOfBytes,_Out_ PULONG_PTR lpCompletionKey,_Out_ LPOVERLAPPED *lpOverlapped,_In_ DWORD dwMilliseconds
); 函数参数 CompletionPort完成端口的句柄。创建一个完成端口使用CreateIoCompletionPort函数。 lpNumberOfBytes指向已完成的I / O操作期间传输的字节数的变量的指针。 lpCompletionKey指向与文件句柄关联的完成键的变量的指针该键的I / O操作已完成。一个完成的关键是每一个文件的关键是指定一个叫CreateIoCompletionPort。 lpOverlapped一个指向一个变量的指针该指针指向在已完成的I / O操作开始时指定的重叠结构的地址的变量。即使您已经通过了一个与完成端口相关联的文件句柄和一个有效的重叠结构应用程序也可以防止完成端口通知。这是通过指定的重叠结构的hevent成员有效的事件处理完成并设置其低阶位。一个有效的事件句柄其低阶位设置将保持I / O完成从被队列到完成端口。 dwMilliseconds调用方愿意等待完成数据包出现在完成端口的毫秒数。如果一个完成包没有出现在指定的时间内功能倍出返回false并设置*lpOverlapped为null。如果该参数是无限的函数将没有时间了。如果该参数为零没有I/O操作中出列函数将取消等待时间立即操作。 返回值 返回非零真如果成功或零假否则。为了获得更多的错误信息调用GetLastError。 此功能将一个线程与指定的完成端口关联。一个线程可以与至多一个完成端口相关联的。如果因为完成端口句柄与它是封闭而调用调用GetQueuedCompletionStatus突出失败函数返回false*lpOverlapped会是空的GetLastError将返回error_abandoned_wait_0。 Windows Server 2003和Windows XP关闭完成端口句柄调用优秀不会导致之前的行为。该函数将继续等待直到一项是从港口或直到发生超时删除如果指定以外的无限价值。 如果GetQueuedCompletionStatus函数调用成功它出列完成包一个成功的I/O操作完成端口和存储信息的变量所指向的下列参数lpNumberOfByteslpcompletionkey和lpOverlapped。在失败返回值是错误的这些相同的参数可以包含特定的值组合如下 如果*lpOverlapped为空功能没有出列完成包从完成端口。在这种情况下函数不存储信息在lpNumberOfBytes and lpCompletionKey所指向的参数中其值是不确定的。 如果*lpOverlapped不空和功能按一个失败的I/O操作的完成端口完成包的功能存储信息有关失败操作的变量所指向的lpcompletionkey lpOverlapped lpNumberOfBytes。为了获得更多的错误信息调用GetLastError。 3、PostQueuedCompletionStatus 将一个I / O完成数据包发送到一个I / O完成端口。I/O完成包将满足一个优秀的调用GetQueuedCompletionStatus函数。该函数返回三值传递的第二第三和第四个参数postqueuedcompletionstatus呼叫。该系统不使用或验证这些值。特别是lpOverlapped参数不需要点的重叠结构。 函数原型 BOOL WINAPI PostQueuedCompletionStatus(_In_ HANDLE CompletionPort,_In_ DWORD dwNumberOfBytesTransferred,_In_ ULONG_PTR dwCompletionKey,_In_opt_ LPOVERLAPPED lpOverlapped
); 参数 CompletionPort一个I / O完成数据包的I / O完成端口的句柄。 dwNumberOfBytesTransferred要通过lpnumberofbytestransferred参数GetQueuedCompletionStatus函数返回的值。0xFFFFFFFF表示处理所有尾随数据。只有准备关闭端口的时候才这样做。 dwCompletionKey可以通过GetQueuedCompletionStatus函数返回的值lpcompletionkey参数。 lpOverlapped要通过lpOverlapped参数GetQueuedCompletionStatus函数返回的值。 返回值 如果函数成功返回值是非零的。如果函数失败返回值为零。为了获得更多的错误信息调用GetLastError。 四、完整的示例程序 接着上面几篇Socket文章写关于公共代码与反射式客户端请参见《 Socket编程模型之简单选择模型》。下面是新建的overlapped_server工程新建了一个overlapped_server_manager类型继承自iserver_manager接口头文件完整代码如下 #pragma once#define SOCKET_MESSAGE_SIZE 1024#include WinSock2.h
#include common_callback.htypedef enum
{RECV_POSTED
}OPERATION_TYPE;typedef struct
{WSAOVERLAPPED overlap;WSABUF buffer;char message[SOCKET_MESSAGE_SIZE];DWORD received_count;DWORD flags;OPERATION_TYPE operation_type;
}PEERIO_OPERATION_DATA, *LPPEERIO_OPERATION_DATA;class completeio_server_manager:public iserver_manager
{
private:int iport;int iaddr_size;common_callback callback;BOOL brunning;SOCKET server;WSADATA wsaData;HANDLE hcomplete_port;SYSTEM_INFO system_info;LPPEERIO_OPERATION_DATA peer_data;bool bdisposed;protected:bool accept_by_crt();bool accept_by_winapi();public:void receive();void shutdown();void start_receive();void start_accept();public:completeio_server_manager();virtual ~completeio_server_manager();
}; 实现文件完整代码如下 #include completeio_server_manager.h
#include stdio.h
#include tchar.hcompleteio_server_manager::completeio_server_manager()
{iport 5150;iaddr_size sizeof(SOCKADDR_IN);brunning FALSE;GetSystemInfo(system_info);callback.set_manager(this);callback.set_receive_thread_coount(system_info.dwNumberOfProcessors);hcomplete_port CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);bdisposed false;
}completeio_server_manager::~completeio_server_manager()
{if (bdisposed)shutdown();
}bool completeio_server_manager::accept_by_crt()
{return true;
}bool completeio_server_manager::accept_by_winapi()
{SOCKADDR_IN server_addr;SOCKADDR_IN client_addr;SOCKET client;LPPEERIO_OPERATION_DATA peer_data;int iresult -1;WSAStartup(MAKEWORD(2, 2), wsaData);server socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);server_addr.sin_addr.S_un.S_addr htonl(INADDR_ANY);server_addr.sin_family AF_INET;server_addr.sin_port htons(iport);do{iresult bind(server, (struct sockaddr*)server_addr, iaddr_size);if (iresult SOCKET_ERROR){iport;server_addr.sin_port htons(iport);}} while (iresult -1);listen(server, 3);printf(基于完成端口模型的Socket服务器启动成功。监听端口是%d\n, iport);while (brunning){printf(开始监听请求。\n);client accept(server, (struct sockaddr*)client_addr, iaddr_size);if (client SOCKET_ERROR)continue;printf(新客户端连接%s:%d\n, inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));CreateIoCompletionPort((HANDLE)client, hcomplete_port, (DWORD)client, 0);peer_data (LPPEERIO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PEERIO_OPERATION_DATA));peer_data-buffer.len SOCKET_MESSAGE_SIZE;peer_data-buffer.buf peer_data-message;peer_data-operation_type RECV_POSTED;printf(开始接收客户端传送数据。\n);WSARecv(client, peer_data-buffer, 1, peer_data-received_count, peer_data-flags, peer_data-overlap, NULL);printf(收到客户端数据。\n);}return true;
}void completeio_server_manager::receive()
{DWORD dwtransfered 0;SOCKET client;LPPEERIO_OPERATION_DATA peer nullptr;while (brunning){printf(线程%d查询端口状态信息。\n,GetCurrentThreadId());GetQueuedCompletionStatus(hcomplete_port, dwtransfered, (PULONG_PTR)client, (LPOVERLAPPED*)peer, INFINITE);printf(获得端口信息。\n);if (dwtransfered 0xFFFFFFFF)return;if (peer-operation_type RECV_POSTED){if (dwtransfered 0){closesocket(client);printf(有客户端退出了。\n);HeapFree(GetProcessHeap(), 0, peer);}else{peer-message[dwtransfered] 0;send(client, peer-message, dwtransfered, 0);memset(peer, 0, sizeof(PEERIO_OPERATION_DATA));peer-buffer.len SOCKET_MESSAGE_SIZE;peer-buffer.buf peer-message;peer-operation_type RECV_POSTED;WSARecv(client, peer-buffer, 1, peer-received_count, peer-flags, peer-overlap, nullptr);}}}
}void completeio_server_manager::shutdown()
{PostQueuedCompletionStatus(hcomplete_port, 0xFFFFFFFF, 0, NULL);//端口尾随数据。brunning FALSE;callback.shutdown();//清扫CloseHandle(hcomplete_port);closesocket(server);WSACleanup();bdisposed true;
}void completeio_server_manager::start_accept()
{brunning TRUE;bdisposed false;callback.start_accept_by_winapi();
}void completeio_server_manager::start_receive()
{brunning TRUE;bdisposed false;callback.start_receive();
}int main()
{completeio_server_manager csm;csm.start_accept();csm.start_receive();printf(服务器启动成功。按任意键关闭服务器并退出程序。\n);getchar();csm.shutdown();return 0;
} 五、效果 六、心得体会 成功创建一个完成端口后便可开始将套接字句柄与对象关联到一起。但在关联套接字之前首先必须创建一个或多个“工作者线程”以便在I/O请求投递给完成端口对象后为完成端口提供服务。在这个时候大家或许会觉得奇怪到底应创建多少个线程以便为完成端口提供服务呢这实际正是完成端口模型显得颇为“复杂”的一个方面因为服务I/O请求所需的数量取决于应用程序的总体设计情况。 在此要记住的一个重点在于在我们调用CreateIoCompletionPort时指定的并发线程数量与打算创建的工作者线程数量相比它们代表的并非同一件事情。早些时候我们曾建议大家用CreateIoCompletionPort函数为每个处理器都指定一个线程处理器的数量有多少便指定多少线程以避免由于频繁的线程“场景”交换活动从而影响系统的整体性能。CreateIoCompletionPort函数的NumberOfConcurrentThreads参数明确指示系统在一个完成端口上一次只允许n个工作者线程运行。假如在完成端口上创建的工作者线程数量超出n个那么在同一时刻最多只允许n个线程运行。 但实际上在一段较短的时间内系统有可能超过这个值但很快便会把它减少至事先在CreateIoCompletionPort函数中设定的值。那么为何实际创建的工作者线程数量有时要比CreateIoCompletionPort函数设定的多一些呢这样做有必要吗如先前所述这主要取决于应用程序的总体设计情况。假定我们的某个工作者线程调用了一个函数比如Sleep或WaitForSingleObject但却进入了暂停锁定或挂起状态那么允许另一个线程代替它的位置。换言之我们希望随时都能执行尽可能多的线程当然最大的线程数量是事先在CreateIoCompletionPort调用里设定好的。 这样一来假如事先预计到自己的线程有可能暂时处于停顿状态那么最好能够创建比CreateIoCompletionPort的NumberOfConcurrentThreads参数的值多的线程以便到时候充分发挥系统的潜力。一旦在完成端口上拥有足够多的工作者线程来为I/O请求提供服务便可着手将套接字句柄同完成端口关联到一起。这要求我们在一个现有的完成端口上调用CreateIoCompletionPort函数同时为前三个参数——FileHandleExistingCompletionPort和CompletionKey——提供套接字的信息。其中 FileHandle参数指定一个要同完成端口关联在一起的套接字句柄。ExistingCompletionPort参数指定的是一个现有的完成端口。 CompletionKey完成键参数则指定要与某个特定套接字句柄关联在一起的“单句柄数据”在这个参数中应用程序可保存与一个套接字对应的任意类型的信息。之所以把它叫作“单句柄数据”是由于它只对应着与那个套接字句柄关联在一起的数据。可将其作为指向一个数据结构的指针来保存套接字句柄在那个结构中同时包含了套接字的句柄以及与那个套接字有关的其他信息。 转载于:https://my.oschina.net/zhtqs/blog/1509843