贝贝花花包包店,精品555皮具,钱夹,皮夹

字体: | 推荐给好友 上一篇 | 下一篇

使用完成端口高性能,可扩展性Winsock服务程序

发布: 2008-5-01 09:15 | 作者: 网络转载 | 来源: 网络转载 | 查看: 183次

正文:
开发网络程序从来都不是一件容易的事情,尽管只需要遵守很少的一些规则;创建socket,发起连接,接受连接,发送和接受数据。真正的困难在于:让你的程序可以适应从单单一个连接到几千个连接。本文主要关注C/S结构的服务器端程序,因为一般来说,开发一个大容量,具可扩展性winsock程序一般就是指服务程序。我们将讨论基于WindowsNT4.0Windows 2000的代码,而不包括Windows3.x(什么时候的东西了),因为Winsock2的这一属性只在Windows NT4和最新版本上有效。
开发网络程序从来都不是一件容易的事情,尽管只需要遵守很少的一些规则�创建socket,发起连接,接受连接,发送和接受数据。真正的困难在于:让你的程序可以适应从单单一个连接到几千个连接。本文主要关注C/S结构的服务器端程序,因为一般来说,开发一个大容量,具可扩展性的winsock程序一般就是指服务程序。我们将讨论基于WindowsNT4.0和Windows 2000的代码,而不包括Windows3.x(什么时候的东西了),因为Winsock2的这一属性只在Windows NT4和最新版本上有效。
APIs和扩展性
和扩展性
win32重叠I/O(Overlapped I/O)机制允许发起一个操作,然后在操作完成之后接受到信息。对于那种需要很长时间才能完成的操作来说,重叠IO机制尤其有用,因为发起重叠操作的线程在重叠请求发出后就可以自由的做别的事情了。
在WinNT和Win2000上,提供的真正的可扩展的I/O模型就是使用完成端口(Completion Port)的重叠I/O.
重叠I/O(Overlapped I/O)机制允许发起一个操作,然后在操作完成之后接受到信息。对于那种需要很长时间才能完成的操作来说,重叠IO机制尤其有用,因为发起重叠操作的线程在重叠请求发出后就可以自由的做别的事情了。
在WinNT和Win2000上,提供的真正的可扩展的I/O模型就是使用完成端口(Completion Port)的重叠I/O.
WinNT和Win2000上,提供的真正的可扩展的I/O模型就是使用完成端口(Completion Port)的重叠I/O.
其实类似于WSAAsyncSelect和select函数的机制更容易兼容Unix,但是难以实现我们想要的“扩展性”。而且windows的完成端口机制在操作系统内部已经作了优化,提供了更高的效率。所以,我们选择完成端口开始我们的服务器程序的开发。
WSAAsyncSelect和select函数的机制更容易兼容Unix,但是难以实现我们想要的“扩展性”。而且windows的完成端口机制在操作系统内部已经作了优化,提供了更高的效率。所以,我们选择完成端口开始我们的服务器程序的开发。
完成端口(Completion Ports
Completion Ports
其实可以把完成端口看成系统维护的一个队列,操作系统把重叠IO操作完成的事件通知放到该队列里,由于是暴露 “操作完成”的事件通知,所以命名为“完成端口”(COmpletion Ports)。一个socket被创建后,可以在任何时刻和一个完成端口联系起来。
一般来说,一个应用程序可以创建多个工作线程来处理完成端口上的通知事件。工作线程的数量依赖于程序的具体需要。但是在理想的情况下,应该对应一个CPU创建一个线程。因为在完成端口理想模型中,每个线程都可以从系统获得一个“原子”性的时间片,轮番运行并检查完成端口,线程的切换是额外的开销。在实际开发的时候,还要考虑这些线程是否牵涉到其他堵塞操作的情况。如果某线程进行堵塞操作,系统则将其挂起,让别的线程获得运行时间。因此,如果有这样的情况,可以多创建几个线程来尽量利用时间。
应用完成端口分两步走:
IO操作完成的事件通知放到该队列里,由于是暴露 “操作完成”的事件通知,所以命名为“完成端口”(COmpletion Ports)。一个socket被创建后,可以在任何时刻和一个完成端口联系起来。
一般来说,一个应用程序可以创建多个工作线程来处理完成端口上的通知事件。工作线程的数量依赖于程序的具体需要。但是在理想的情况下,应该对应一个CPU创建一个线程。因为在完成端口理想模型中,每个线程都可以从系统获得一个“原子”性的时间片,轮番运行并检查完成端口,线程的切换是额外的开销。在实际开发的时候,还要考虑这些线程是否牵涉到其他堵塞操作的情况。如果某线程进行堵塞操作,系统则将其挂起,让别的线程获得运行时间。因此,如果有这样的情况,可以多创建几个线程来尽量利用时间。
应用完成端口分两步走:
CPU创建一个线程。因为在完成端口理想模型中,每个线程都可以从系统获得一个“原子”性的时间片,轮番运行并检查完成端口,线程的切换是额外的开销。在实际开发的时候,还要考虑这些线程是否牵涉到其他堵塞操作的情况。如果某线程进行堵塞操作,系统则将其挂起,让别的线程获得运行时间。因此,如果有这样的情况,可以多创建几个线程来尽量利用时间。
应用完成端口分两步走:
1创建完成端口句柄:
创建完成端口句柄:
HANDLE hIocp;
hIocp = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
(ULONG_PTR)0,
0);
if (hIocp == NULL) {
// Error
}
注意在第一个参数(FileHandle)传入INVALID_FILE_HANDLE,第二个参数(ExistingCompletionPort)传入NULL,系统将创建一个新的完成端口句柄,没有任何IO句柄与其关联。
FileHandle)传入INVALID_FILE_HANDLE,第二个参数(ExistingCompletionPort)传入NULL,系统将创建一个新的完成端口句柄,没有任何IO句柄与其关联。
2.完成端口创建成功后,在socket和完成端口之间建立关联。再次调用socket和完成端口之间建立关联。再次调用CreateIoCmpletionPort函数,这一次在第一个参数FileHandle传入创建的socket句柄,参数ExistingCompletionPort为已经创建的完成端口句柄。
以下代码创建了一个socket并把它和完成端口联系起来。
.完成端口创建成功后,socket和完成端口之间建立关联。再次调用socket和完成端口之间建立关联。再次调用CreateIoCmpletionPort函数,这一次在第一个参数FileHandle传入创建的socket句柄,参数ExistingCompletionPort为已经创建的完成端口句柄。
以下代码创建了一个socket并把它和完成端口联系起来。
socket并把它和完成端口联系起来。
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
}
???
}
到此为止socket已经成功和完成端口相关联。在此socket上进行的重叠IO操作结果均使用完成端口发出通知。注意:CreateIoCompletionPort函数的第三个参数允许开发人员传入一个类型为ULONG_PTR的数据成员,我们把它称为完成键(Completion key),此数据成员可以设计为指向包含socket信息的一个结构体的一个指针,用来把相关的环境信息和socket联系起来,每次完成通知来到的同时,该环境信息也随着通知一起返回给开发人员。
完成端口创建以及与socket关联之后,就要创建一个或多个工作线程来处理完成通知,每个线程都可以循环的调用GetQueuedCompletionStatus函数,检查完成端口上的通知事件。
在举例说明一个典型的工作线程的之前,我们先讨论一下重叠IO的过程。当一个重叠IO被发起,一个Overlapped结构体的指针就要作为参数传递给系统。当操作完成,GetQueueCompletionStatus可以返回指向同一个Overlapp结构的指针。为了辨认和定位这个已完成的操作,开发人员最好定义自己的OVERLAPPED结构,以包含一些自己定义的关于操作本身的额外信息。比如:
socket已经成功和完成端口相关联。在此socket上进行的重叠IO操作结果均使用完成端口发出通知。注意:CreateIoCompletionPort函数的第三个参数允许开发人员传入一个类型为ULONG_PTR的数据成员,我们把它称为完成键(Completion key),此数据成员可以设计为指向包含socket信息的一个结构体的一个指针,用来把相关的环境信息和socket联系起来,每次完成通知来到的同时,该环境信息也随着通知一起返回给开发人员。
完成端口创建以及与socket关联之后,就要创建一个或多个工作线程来处理完成通知,每个线程都可以循环的调用GetQueuedCompletionStatus函数,检查完成端口上的通知事件。
在举例说明一个典型的工作线程的之前,我们先讨论一下重叠IO的过程。当一个重叠IO被发起,一个Overlapped结构体的指针就要作为参数传递给系统。当操作完成,GetQueueCompletionStatus可以返回指向同一个Overlapp结构的指针。为了辨认和定位这个已完成的操作,开发人员最好定义自己的OVERLAPPED结构,以包含一些自己定义的关于操作本身的额外信息。比如:
socket关联之后,就要创建一个或多个工作线程来处理完成通知,每个线程都可以循环的调用GetQueuedCompletionStatus函数,检查完成端口上的通知事件。
在举例说明一个典型的工作线程的之前,我们先讨论一下重叠IO的过程。当一个重叠IO被发起,一个Overlapped结构体的指针就要作为参数传递给系统。当操作完成,GetQueueCompletionStatus可以返回指向同一个Overlapp结构的指针。为了辨认和定位这个已完成的操作,开发人员最好定义自己的OVERLAPPED结构,以包含一些自己定义的关于操作本身的额外信息。比如:
IO的过程。当一个重叠IO被发起,一个Overlapped结构体的指针就要作为参数传递给系统。当操作完成,GetQueueCompletionStatus可以返回指向同一个Overlapp结构的指针。为了辨认和定位这个已完成的操作,开发人员最好定义自己的OVERLAPPED结构,以包含一些自己定义的关于操作本身的额外信息。比如:
typedef struct _OVERLAPPEDPLUS {
OVERLAPPED ol;
SOCKET s, sclient;
int OpCode;
WSABUF wbuf;
DWORD dwBytes, dwFlags;
// other useful information
} OVERLAPPEDPLUS;
此结构的第一个成员为默认的OVERLAPPED结构,第二,三个为本地服务socket和与该操作相关的客户socekt,第4个成员为操作类型,对于socket,现在定义的有
OVERLAPPED结构,第二,三个为本地服务socket和与该操作相关的客户socekt,第4个成员为操作类型,对于socket,现在定义的有
#define OP_READ 0
#define OP_WRITE 1
#define OP_ACCEPT 2
3种。然后还有应用程序的socket缓冲区,操作数据量,标志位以及其他开发人员认为有用的信息。
当进行重叠IO操作,把OVERLAPPEDPLUS结构作为重叠IO的参数lpOverlapp传递(如WSASend,WASRecv,等函数,有一个lpOverlapped参数,要求传入一个OVERLAPP结构的指针)
当操作完成后,GetQueuedCompletionStatus函数返回一个LPOVERLAPPED 类型的指针,这个指针其实是指向开发人员定义的扩展OVERLAPPEDPLUS结构,包含着开发人员早先传入的全部信息。
注意:OVERLAPPED成员不一定要求是OVERLAPPEDPLUS扩展结构的一个成员,在获得OVERLAPPED指针之后,可以用CONTAINING_RECORD宏获得相应的扩展结构的指针。
 
 
种。然后还有应用程序的socket缓冲区,操作数据量,标志位以及其他开发人员认为有用的信息。
当进行重叠IO操作,把OVERLAPPEDPLUS结构作为重叠IO的参数lpOverlapp传递(如WSASend,WASRecv,等函数,有一个lpOverlapped参数,要求传入一个OVERLAPP结构的指针)
当操作完成后,GetQueuedCompletionStatus函数返回一个LPOVERLAPPED 类型的指针,这个指针其实是指向开发人员定义的扩展OVERLAPPEDPLUS结构,包含着开发人员早先传入的全部信息。
注意:OVERLAPPED成员不一定要求是OVERLAPPEDPLUS扩展结构的一个成员,在获得OVERLAPPED指针之后,可以用CONTAINING_RECORD宏获得相应的扩展结构的指针。
 
 
IO操作,把OVERLAPPEDPLUS结构作为重叠IO的参数lpOverlapp传递(如WSASend,WASRecv,等函数,有一个lpOverlapped参数,要求传入一个OVERLAPP结构的指针)
当操作完成后,GetQueuedCompletionStatus函数返回一个LPOVERLAPPED 类型的指针,这个指针其实是指向开发人员定义的扩展OVERLAPPEDPLUS结构,包含着开发人员早先传入的全部信息。
注意:OVERLAPPED成员不一定要求是OVERLAPPEDPLUS扩展结构的一个成员,在获得OVERLAPPED指针之后,可以用CONTAINING_RECORD宏获得相应的扩展结构的指针。
 
 
GetQueuedCompletionStatus函数返回一个LPOVERLAPPED 类型的指针,这个指针其实是指向开发人员定义的扩展OVERLAPPEDPLUS结构,包含着开发人员早先传入的全部信息。
注意:OVERLAPPED成员不一定要求是OVERLAPPEDPLUS扩展结构的一个成员,在获得OVERLAPPED指针之后,可以用CONTAINING_RECORD宏获得相应的扩展结构的指针。
 
 
: OVERLAPPED成员不一定要求是OVERLAPPEDPLUS扩展结构的一个成员,在获得OVERLAPPED指针之后,可以用CONTAINING_RECORD宏获得相应的扩展结构的指针。
 
 
典型的Worker Thread结构
Worker Thread结构
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
ULONG_PTR *PerHandleKey;
OVERLAPPED *Overlap;
OVERLAPPEDPLUS *OverlapPlus,
*newolp;
DWORD dwBytesXfered;
while (1)
{
ret = GetQueuedCompletionStatus(
hIocp,
&dwBytesXfered,
(PULONG_PTR)&PerHandleKey,
&Overlap,
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);
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.
break;
} // switch
} // while
} // WorkerThread
 
--------------------------------------------------------------------------------
查看以上代码,注意如果Overlapped操作立刻失败(比如,返回SOCKET_ERROR或其他非WSA_IO_PENDING的错误),则没有任何完成通知时间会被放到完成端口队列里。反之,则一定有相应的通知时间被放到完成端口队列。
更完善的关于Winsock的完成端口机制,可以参考MSDN的Microsoft PlatFormSDK,那里有完成端口的例子。访问http://msdn.microsoft.com/library/techart/msdn_servrapp.htm.可以获得更多信息。
 
 
 
Overlapped操作立刻失败(比如,返回SOCKET_ERROR或其他非WSA_IO_PENDING的错误),则没有任何完成通知时间会被放到完成端口队列里。反之,则一定有相应的通知时间被放到完成端口队列。
更完善的关于Winsock的完成端口机制,可以参考MSDN的Microsoft PlatFormSDK,那里有完成端口的例子。访问http://msdn.microsoft.com/library/techart/msdn_servrapp.htm.可以获得更多信息。
 
 
 
Winsock的完成端口机制,可以参考MSDN的Microsoft PlatFormSDK,那里有完成端口的例子。访问http://msdn.microsoft.com/library/techart/msdn_servrapp.htm.可以获得更多信息。
 
 
 
 

TAG: winsock Winsock 可扩展性 完成端口 服务程序 高性能

31/3123>
 

评分:0

我来说两句

seccode