By PiggyXP(小猪)完成端口(CompletionPort)详解http://blog.csdn.net/neicole/article/details/7549497
http://blog.csdn.net/han_jiang_xue/article/details/17960827
一个简单的IOCP(IO完成端口)服务器/客户端类
(中文版)http://blog.csdn.net/han_jiang_xue/article/details/18269203
(英文版)http://blog.csdn.net/han_jiang_xue/article/details/18269067
可伸缩的IO完成端口服务器模型(IOCP)
(中文版)http://blog.csdn.net/han_jiang_xue/article/details/18270155
(英文版)http://blog.csdn.net/han_jiang_xue/article/details/18270213
Build your own cryptographically safeserver/client protocol
http://blog.csdn.net/han_jiang_xue/article/details/18269655
IOCP编程小结
http://blog.csdn.net/han_jiang_xue/article/details/18269675
IOCP模型与网络编程
http://blog.csdn.net/neicole/article/details/7549497
http://blog.csdn.net/markman101/article/details/6235516
http://blog.csdn.net/zhongguoren666/article/details/7386479
http://www.cnblogs.com/duzouzhe/archive/2010/01/13/1646514.html
http://www.cnblogs.com/duzouzhe/archive/2010/01/13/1646514.html
英文文献:
Multi-threaded Client/Server Socket Class
http://blog.csdn.net/cuijinquan/article/details/17741701
Full Multi-thread Client/Server SocketClass with ThreadPool
http://blog.csdn.net/cuijinquan/article/details/17741849
IOCPNet - Ultimate IOCP
http://blog.csdn.net/cuijinquan/article/details/17754977
Single Server With Multiple Clients : aSimple C++ Implementation
http://blog.csdn.net/cuijinquan/article/details/17741877
Simple client-server network using C++ andWindows Winsock
http://blog.csdn.net/cuijinquan/article/details/17741831
百万级访问量网站的技术准备工作
http://blog.csdn.net/q969403688/article/details/7884888
漫步者de Blog
IOCP普及篇(一)
http://blog.csdn.net/zcq108/article/details/6555167
IOCP普及篇(二)
http://blog.csdn.net/zcq108/article/details/6555172
IOCP 完整
http://blog.csdn.net/zcq108/article/details/6555188
Windows完成端口编程
http://blog.csdn.net/zcq108/article/details/6555016
IOCP 的RECV和SEND
http://blog.csdn.net/zcq108/article/details/6555040
IOCP注意事项
http://blog.csdn.net/dongzhongshu/article/details/5614630
IOCP模型总结(转)
http://blog.csdn.net/dongzhongshu/article/details/5614617
IOCP 中WSARecvFrom返回10045的问题
http://blog.csdn.net/dongzhongshu/article/details/6317843
IOCP The WSAENOBUFS error problem
http://www.codeproject.com/KB/IP/iocp_server_client.aspx
http://blog.pfan.cn/xman/45130.html
http://blog.pfan.cn/xman/45129.html
用空连接或IO负载很低的连接来测试并发性能根本没有意义
微软自己都承认,使用Winsock的网络应用,有效并发大于250左右操作系统性能就会开始急剧下降所以才搞出了IOCP机制。IOCP承载10000以上长连接并保持20000次IO/秒,一般没啥问题
http://topic.csdn.net/u/20090916/10/07d0c9b5-2653-4af2-b5e3-19d2eb058502.html?58683
网络编程之完成端口(CSDN上摘抄的)
http://hi.baidu.com/tonyfirst1/blog/item/70b887dd71f618315882dd30.html
dkink的专栏
Winsock程序设计入门(1)修订版-简单的TCP服务器
http://blog.csdn.net/dkink/article/details/3940580
Winsock程序设计入门(2)修订版-简单的TCP客户端
http://blog.csdn.net/dkink/article/details/3940516
Winsock程序设计入门(3)修订版-多线程TCP服务器和客户端
http://blog.csdn.net/dkink/article/details/3945750
qqinbaby 的BLOG
基于多线程的客户端/服务器套接字类
http://820808.blog.51cto.com/328558/65599
使用新的线程池 API 提高可伸缩性
http://820808.blog.51cto.com/328558/64959
3.3 小节
讲这么点就完了?你一定认为我介绍的东西并没有超过原书中的内容,实事上完成端口编程的精髓就是上面的代码和原书中的有关叙述。如果我再把他们完整的重复一遍,那又有什么意思呢?根据我的经验,设计网络服务器的真正难点,不在于完成端口技术,所以我想利用小节把自己编程中的一些经验告诉大家。
首先是服务器的管理,一个服务器首先要分析它的设计目标是应对很多的连接还是很大的数据传送量。这样在设计工作者线程时就可以最大限度的提高性能。管理客户端方面,我们可以将客户端的数据捆绑到Perhand-Data数据结构上,如果还有需要,可以建一个表来记录客户端的宏观情况。
在Ares引擎中,我将文件传送和大容量数据传送功能也封装进了服务器和客户端。我建议服务器和客户端都应该封装这些功能,尽管我们并不是做FTP服务器,但是当客户端需要和服务器交换文件和大块数据时,你会发现这样做,灵活性和性能都能做得比用单纯的FTP协议来更好,所以在你的服务器和客户端可以传送数据包以后,把他们都做进去吧。
为了服务器不被黑客攻击,或被BUG弄崩溃,我们还需要认真设计服务器的认证机制,以及密切注意程序中的溢出,一定要在每一个使用缓冲区的地方加上检查代码。可以说并没有现成的办法来解决这个问题,不然就没有人研究网络安全了,所以我们要做的是尽量减少错误,即使出现错误也不会造成太大损失,在发现错误的时候能够很快纠正同类错误。
还有就是对客户端情况的检测,比如客户端的正常和非正常断开连接。如果不注意这一点,就会造成服务器资源持续消耗而最终崩溃,因为我们的服务器不可能总是重启,而是要持续的运行,越久越好。还有比如客户端断开连接后又尝试连接,但是在服务器看来这个客户“仍然在线“,这个时候我们不能单纯的拒绝客户端的连接,也不能单纯的接收。
讲了几点服务器设计中的问题,他们只是众多问题中的一小部分,限于时间原因,在这个版本的文章中就说这么多。你一定会发现,其实网络编程最困难和有成就的地方,并不是服务器用了什么模式等等,而是真正深入设计的时候碰到的众多问题。正是那些没有标准答案的问题,值得我们去研究和解决。
IOCP普及篇(一)[转]
http://6yy.net/article/progLog/72.htm
100分求完成端口发送数据的方法
http://topic.csdn.net/t/20041130/20/3603093.html
IOCP应用框架的设计
http://blog.csdn.net/robotom/archive/2009/02/23/3929582.aspx
IOCP编写心得
http://www.slenk.net/viewthread.php?tid=4175
IOCP的封装类[By Sodme]
http://doserver.net/post/iocp-sodme.php#entrymore
进程服务器模型和线程服务器模型
http://doserver.net/read.php/2022.htm
浅析Delphi实现IOCP后的优化
http://fxh7622.blog.51cto.com/63841/106035
DELPHI中完成端口(IOCP)的简单分析(3)
http://fxh7622.blog.51cto.com/63841/15578
fxh7622 的BLOG
http://fxh7622.blog.51cto.com/63841/p-1
小猪的网络编程+花猫的漫画世界
http://blog.csdn.net/PiggyXP/default.aspx?PageNumber=2
牧野的Blog
http://www.cnblogs.com/wzd24/archive/2007/12/24/1011932.html
http://blog.sina.com.cn/s/blog_484102dd0100bf19.html
关于socket的粘包 (2008-11-22 08:53:07)
以前也被这个困扰,折腾了很久。网上讨论的也巨火,仿佛对socket的性能颇加质疑,总觉得不方便。有用各种方法延时的,有一收一发的,还有检测发送结果的,反正貌似就要跟这个粘包的socket干上了...
我以前着这么试过,能解决问题,但换来的是性能成倍的下降,现在总算是想通了,我讲讲我的理解。
首先要承认,TCP的特性就是流传输,所以粘包不是一个不能理解和接受的事实,自动的分片和连续的传输反而是为了提高性能而设计的,这个要承认。要处理粘包的情况,八成是为了接收有结构的数据吧,要么怕收到半个,要么就是1个多,反正是对不上自己的结构,所以会出错。
其实想想,人家连在一起了,你就真的没法处理了吗?我的做法是,你在内存里再开一个缓冲区,把收到的数据不管是半个还是1个半,都按顺序拼到缓冲区里,然后你从缓冲区里找出一个一个完成的结构,这不就完了。
这样socket就不用再延时了,可以一直发,性能是有保证的,而我们也省心些,就可以把注意力从让人头疼的网络,转移到对自己内存数据的处理上来了。
心跳包的接收处理
http://topic.csdn.net/t/20050916/13/4275031.html
请教一下IOCP和线程池搭配使用
http://topic.csdn.net/u/20070425/11/b75cfbee-3f6b-4163-9a77-d61a6d3d4aa8.html
IOCP在有客户端连到服务器的时候,怎么检索到有哦几个客户端连接到iocp的呢?
http://topic.csdn.net/u/20090223/17/71d5ec7e-906b-49b2-a377-6f1b495a0b88.html
使用了IOCP的ECHO程序的疑惑
http://topic.csdn.net/t/20040727/11/3213842.html
理解I/O完成端口模型 (感谢 PiggyXP 和 nonocast)
http://topic.csdn.net/t/20040512/08/3056877.html
完成端口的构架图(tcp和udp的讨论)
http://topic.csdn.net/t/20040507/10/3037970.html
多年前写的一个iocp应用框架
http://www.oschina.net/code/snippet_83492_9833
zougangx blog:
socket IO完成端口模型详解
http://blog.csdn.net/zougangx/article/details/5561284
完成端口模型IOCP详解(一)
http://blog.csdn.net/zougangx/article/details/5563872
socket IO完成端口模型详解(例子)
http://blog.csdn.net/xiangzhang321/article/details/7452381
利用WinInet类进行TCP/IP通信
http://blog.csdn.net/weiwangchao_/article/details/7089590
CreateIoCompletionPort和完成端口
http://blog.csdn.net/hionceshine/article/details/3362669
Windows Sockets 2.0:使用完成端口高性能,可扩展性Winsock服务程序
http://blog.csdn.net/encoder1234/article/details/11809791
完成端口
http://blog.csdn.net/CAICHAO1234/article/details/8932359
用完成端口开发大响应规模的Winsock应用程序
http://blog.csdn.net/wutong_login/article/details/7826990
Socket 错误分析及错误码
http://blog.csdn.net/cuijinquan/article/details/17769837
--------------------------------------------------------------------------------
1. AcceptEx:
BOOL
PASCAL FAR
AcceptEx (
INSOCKET sListenSocket,
INSOCKET sAcceptSocket,
INPVOID lpOutputBuffer,
INDWORD dwReceiveDataLength,
INDWORD dwLocalAddressLength,
INDWORD dwRemoteAddressLength,
OUT LPDWORD lpdwBytesReceived,
INLPOVERLAPPED lpOverlapped
);
用来发起一个异步的调用, 接受客户端将要发出的连接请求. 与 accept 不同的是, 你必须先手动创建一个 socket 提供给 AcceptEx, 用来接受连接 ( accept 是内在地创建一个 socket 接受连接, 并返回值 ). 而且, accept 创建的 socket 会自动继承监听 socket 的属性, AcceptEx 却不会. 因此如果有必要, 在 AcceptEx 成功接受了一个连接之后, 我们必须调用:
setsockopt( hAcceptSocket, SOL_SOCKET,SO_UPDATE_ACCEPT_CONTEXT, ( char*)&( hListenSocket ), sizeof( hListenSocket ) );
来做到这一点.
AcceptEx 允许在接受连接的同时接收对方发来的第一组数据, 这当然是出于性能的考虑. 但是这时候 AcceptEx 最少要接收到一个字节的数据才会返回, 一旦碰到恶意连接它就永远不会返回了. 关闭这项功能的方式是: 把参数 dwReceiveDataLength 至为 0, 打开则相反. 当然了 , 如果一定要启用这个功能, 我们也有防御的办法. 启动一个线程定时地检测每一个 AcceptEx 是否已经连接, 连接时间为多久, 以此判断对方是否是恐怖分子:
int iSecs;
int iBytes = sizeof( int );
getsockopt( hAcceptSocket, SOL_SOCKET, SO_CONNECT_TIME, (char*)&iSecs, &iBytes );
iSecs 为 -1 表示还未建立连接, 否则就是已经连接的时间.
调用 AcceptEx 的方式:
#include
typedef BOOL ( WINAPI * PFNACCEPTEX ) (SOCKET, SOCKET, PVOID, DWORD, DWORD, DWORD, LPDWORD, LPOVERLAPPED );
PFNACCEPTEX pfnAcceptEx;
DWORD dwBytes;
GUID guidAcceptEx = WSAID_ACCEPTEX;
::WSAIoctl( hListenSocket,SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof( guidAcceptEx ),&pfnAcceptEx, sizeof( pfnAcceptEx ), &dwBytes, NULL, NULL );
DWORD uAddrSize = sizeof( SOCKADDR_IN ) +16;
DWORD uDataSize = 0;
BOOL bRes = pfnAcceptEx( hListenSocket,hAcceptSocket, buffer, uDataSize, uAddrSize, uAddrSize, &uAddrSize, (LPWSAOVERLAPPED )overlapped );
其中, buffer 和 overlapped 要根据你自己的用途来定了,这里只是拿来充数.
一旦 AcceptEx 调用完成 ( 通过完成端口通知你 ), 接下来的步骤就是 1. 上面讲的SO_UPDATE_ACCEPT_CONTEXT;
2. 将 hAcceptSocket 绑定到完成端口.
--------------------------------------------------------------------------------
2. TransmitFile
TransmitFile 顾名思义是用来进行文件传输的函数, 全自动无需干涉的. 在 Win NT 专业版/家庭版上无法发挥全部性能. 在这里只讨论它的一个功能: 关闭一个 SOCKET 并再次初始化它. 创建一个 SOCKET 是很耗时的, 因此这么做可以节约大量时间:
#include
typedef BOOL ( WINAPI * PFNTRANSMITFILE ) (SOCKET, HANDLE, DWORD, DWORD, LPOVERLAPPED, LPTRANSMIT_FILE_BUFFERS, DWORD );
PFNACCEPTEX pfnTransmitFile;
DWORD dwBytes;
GUID guidTransmitFile = WSAID_TRANSMITFILE;
::WSAIoctl( hListenSocket,SIO_GET_EXTENSION_FUNCTION_POINTER, &guidTransmitFile,
sizeof(guidTransmitFile ), &pfnTransmitFile,
sizeof(pfnTransmitFile ), &dwBytes, NULL, NULL );
pfnTransmitFile( hAcceptSocket, NULL, 0, 0,NULL, NULL, TF_DISCONNECT | TF_REUSE_SOCKET );
经过这个函数处理的SOCKET 可以作为接受连接的 socket 提交给 AcceptEx 再次使用. 当这样的 socket 接受连接成功后, 如果往完成端口上绑定会出错 - 因为上次接受连接成功时已经绑定过了, 这个错误可以忽略.
--------------------------------------------------------------------------------
3. FD_ACCEPT
即使我们在程序启动时发起了再多的 AcceptEx , 也有可能碰到数目不够用户连不上来的情况. 在 Win2000 或更高版本的系统上, 我们可以通过 WSAEventSelect 注册一个 FD_ACCEPT 事件. 当 AcceptEx 数目不足以应付大量的连接请求时, 这个事件会被触发. 于是我们就可以发出更多的 AcceptEx, 而且我们还可以抽空辨别一下 AcceptEx 为什么这么快就用光了, 是不是碰上攻击者了( 辨别方法见上文所述 ) ?
HANDLE hAcceptExThreadEvent =::CreateEvent( NULL, TRUE, FALSE, _T("AcceptExThreadEvent") );
::WSAEventSelect( hListenSocket,hAcceptExThreadEvent, FD_ACCEPT );
DWORD WINAPI AcceptExThread( LPVOIDlpParameter )
{
// 负责保证有足够多的 AcceptEx 可以接受连接请求的线程
for( UINT i = 0; i < 10; i ++ ) // 程序启动时发起的AcceptEx
{
pfnAcceptEx( hListenSocket, ... );
}
while( TRUE )
{
DWORD dwRes =::WaitForSingleObject( hAcceptExThreadEvent, INFINITE );
if( dwRes == WAIT_FAILED )
{
break;
}
::ResetEvent( hAcceptExThreadEvent );
if( m_sbWaitForExit )
{ // 当然, 退出线程也是这个 Event 通知
break;
}
pfnAcceptEx( hListenSocket, ... );
//
// ... 在此检查是否被攻击
//
}
return 0;
}
要说明的是,WSAEventSelect() 所需的 WSAEVENT 和 CreateEvent() 所创建的 EVENT 是通用的.
--------------------------------------------------------------------------------
4. WSASend 和 WSARecv
默认情况下, 每一个 socket 在系统底层都拥有一个发送和接收缓冲区.
我们发送数据时, 实际上是把数据复制到发送缓冲区中, 然后 WSASend 返回. 但是如果发送缓冲区已经满了, 那么我们在 WSASend 中指定的缓冲区就会被锁定到系统的非分页内存池中, WSASend 返回 WSA_IO_PENDING. 一旦网络空闲下来, 数据将会从我们提交的缓冲区中直接被发送出去, 与 " 我们的缓冲区->发送缓冲区->网络 " 相比节省了一次复制操作.
WSARecv 也是一样, 接收数据时直接把接收缓冲区中的数据复制出来. 如果接收缓冲区中没有数据, 我们在 WSARecv 中指定的缓冲区就会被锁定到系统的非分页内存池以等待数据, WSARecv 返回 WSA_IO_PENDING. 如果网络上有数据来了, 这些数据将会被直接保存到我们提供的缓冲区中.
如果一个服务器同时连接了许多客户端, 对每个客户端又调用了许多 WSARecv, 那么大量的内存将会被锁定到非分页内存池. 锁定这些内存时是按照页面边界来锁定的, 也就是说即使你WSARecv 的缓存大小是 1 字节, 被锁定的内存也将会是 4k. 非分页内存池是由整个系统共用的 , 如果用完的话最坏的情况就是系统崩溃. 一个解决办法是, 使用大小为 0 的缓冲区调用 WSARecv. 等到调用成功时再换用非阻塞的 recv 接收到来的数据, 直到它返回 WSAEWOULDBLOCK 表明数据已经全部读完. 在这个过程中没有任何内存需要被锁定, 但坏处是效率稍低.