假设现在无数客户端连接上来下载一大文件,那么读此文件显然也必须异步进行,想采用完成端口模式,读完一块发送一块,有没有代码示例?
解决方案 »
- 关于sqlite3的问题
- 用MFC,知道一个URL,如何获得这个URL的IP信息呢?
- 利用socket开发基于tcp/ip协议的网络程序时,有必要设置发送超时和读取超时吗?
- CComboBox动态创建问题,不能往编辑框输入东西
- 一个困扰很久的问题:
- 关于检测目录的问题,请各位指教!
- 如何得到某个网页中Frame的IDispatch接口?
- 急!如何在Service程序中访问另一台机器上的共享磁盘?
- 天问
- 請問我在一個局域網中用Win2000 Server連接網絡,怎麼能訪問本地(可以Ping通自己),不能訪問其他計算機?好象HUB和外部的網絡都沒問題?
- 组合框问题
- 请教各位高手一个SQL的问题“超时已过期”
Select+异步不行吗?
完成端口有一定的难度,为什么不先看看书?
BOOL TransmitFile(
SOCKET hSocket,
HANDLE hFile,
DWORD nNumberOfBytesToWrite,
DWORD nNumberOfBytesPerSend,
LPOVERLAPPED lpOverlapped,
LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
DWORD dwFlags
);
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
}
???
}
OVERLAPPED ol;
SOCKET s, sclient;
int OpCode;
WSABUF wbuf;
DWORD dwBytes, dwFlags;
// other useful information
} OVERLAPPEDPLUS;#define OP_READ 0
#define OP_WRITE 1
#define OP_ACCEPT 2
下面让我们来看看Figure2里工作者线程的情况。
Figure 2 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
资源的限制条件
在设计任何服务器应用程序时,其强健性是主要的目标。也就是说,你的应用程序要能够应对任何突发的问题,例如并发客户请求数达到峰值、可用内存临时出现不足、以及其它短时间的现象。这就要求程序的设计者注意Windows NT和2000系统下的资源限制条件的问题,从容地处理突发性事件。你可以直接控制的、最基本的资源就是网络带宽。通常,使用用户数据报协议(UDP)的应用程序都可能会比较注意带宽方面的限制,以最大限度地减少包的丢失。然而,在使用TCP连接时,服务器必须十分小心地控制好,防止网络带宽过载超过一定的时间,否则将需要重发大量的包或造成大量连接中断。关于带宽管理的方法应根据不同的应用程序而定,这超出了本文讨论的范围。虚拟内存的使用也必须很小心地管理。通过谨慎地申请和释放内存,或者应用lookaside lists(一种高速缓存)技术来重新使用已分配的内存,将有助于控制服务器应用程序的内存开销(原文为“让服务器应用程序留下的脚印小一点”),避免操作系统频繁地将应用程序申请的物理内存交换到虚拟内存中(原文为“让操作系统能够总是把更多的应用程序地址空间更多地保留在内存中”)。你也可以通过SetWorkingSetSize()这个Win32 API让操作系统分配给你的应用程序更多的物理内存。在使用Winsock时还可能碰到另外两个非直接的资源不足情况。一个是被锁定的内存页面的极限。如果你把AFD.SYS的缓冲关闭,当应用程序收发数据时,应用程序缓冲区的所有页面将被锁定到物理内存中。这是因为内核驱动程序需要访问这些内存,在此期间这些页面不能交换出去。如果操作系统需要给其它应用程序分配一些可分页的物理内存,而又没有足够的内存时就会发生问题。我们的目标是要防止写出一个病态的、锁定所有物理内存、让系统崩溃的程序。也就是说,你的程序锁定内存时,不要超出系统规定的内存分页极限。在Windows NT和2000系统上,所有应用程序总共可以锁定的内存大约是物理内存的1/8(不过这只是一个大概的估计,不是你计算内存的依据)。如果你的应用程序不注意这一点,当你的发出太多的重叠收发调用,而且I/O没来得及完成时,就可能偶尔发生ERROR_INSUFFICIENT_RESOURCES的错误。在这种情况下你要避免过度锁定内存。同时要注意,系统会锁定包含你的缓冲区所在的整个内存页面,因此缓冲区靠近页边界时是有代价的(译者理解,缓冲区如果正好超过页面边界,那怕是1个字节,超出的这个字节所在的页面也会被锁定)。另外一个限制是你的程序可能会遇到系统未分页池资源不足的情况。所谓未分页池是一块永远不被交换出去的内存区域,这块内存用来存储一些供各种内核组件访问的数据,其中有的内核组件是不能访问那些被交换出去的页面空间的。Windows NT和2000的驱动程序能够从这个特定的未分页池分配内存。<<回复3次,可还没结束。谁来顶一下,我好继续贴>>
其实你这样考虑应该没问题了(相对于优化条件来说)。
但我不知道你的东西能不能优化的余地。如果是不无数个客户端读不同的或同一文件不同的区域时,必然要为它们开线程,主要原因有二:1硬盘速度相对来说比较慢,在某程度上会阻塞完成线程的。2:单线程统一对所有磁盘的操作估计很复杂,一般不值得你这样做。如果你承认上边的方案的话,那继续看下去。你要知道线程数,每个线程对磁盘的操作,网络带宽,这三个主要问题必然是影响你服务端的问题。那么就要监视开对“磁盘操作的线程”了。这样一来,就不能实现你的对无数客户端进行磁盘的操作了,其实这样做也没错,因为“无数磁盘的操作”必然使你的服务器down掉的。更不用谈这“无数”在执行中的线程了,cpu负荷得起?
如果不采取完成端口的模式,势必要为每个读写文件开一个线程,这样就不能为大批量客户端服务,线程数100以上估计CPU切换比较愚钝,更多就更不要说了。
其实我认为,readfile和wsarecv对于完成端口来说没有什么区别,反正都是等待完成,微软在后面去玩什么鬼咱不管,我只需要知道什么时候完成了,然后操作的内容在哪里就行了
微软真他妈的弱,GetQueuedCompletionStatus函数第4个参数LPOVERLAPPED是一个指向 Overlapped结构的指针,然后它说,可以把这个结构改一下(后面加上内容),让它变成一个伪Overlapped结构, 调用这个函数时把它的指针传进去,然后完成的内容就会在这个结构体里搞出来,的确没有错,是可以出来,但是msdn上对如何定义这个结构体也没有统一的说法,大家随便定义,总之只要保证第一项是那个原来的结构体就行。让我不解的是,我定义的伪结构里有WSABUF结构,这个缓冲区里放完成的内容,我的位置不固定,微软是如何知道这个的,假如我不定义这个,它又把完成内容放到哪里去,怪。