博文
完成端口中的单句柄数据结构与单IO数据结构的理解与设计(2010-07-08 21:16:00)
摘要:本文作者:sodme
本文出处:http://blog.csdn.net/sodme
声明:本文可以不经作者同意任意转载、复制、传播,但任何对本文的引用均须保留本文的作者、出处及本行声明信息!谢谢!
完成端口模型,针对于WIN平台的其它异步网络模型而言,最大的好处,除了性能方面的卓越外,还在于完成端口在传递网络事件的通知时,可以一并传递与此事件相关的应用层数据。这个应用层数据,体现在两个方面:一是单句柄数据,二是单IO数据。
GetQueuedCompletionStatus函数的原型如下:
WINBASEAPI
BOOL
WINAPI
GetQueuedCompletionStatus(
IN HANDLE CompletionPort,
OUT LPDWORD lpNumberOfBytesTransferred,
OUT PULONG_PTR lpCompletionKey,
OUT LPOVERLAPPED *lpOverlapped,
IN DWORD dwMilliseconds
);
其中,我们把第三个参数lpCompletionKey称为完成键,由它传递的数据称为单句柄数据。我们把第四个参数lpOverlapped称为重叠结构体,由它传递的数据称为单IO数据。
以字面的意思来理解,lpCompletionKey内包容的东西应该是与各个socket一一对应的,而lpOverlapped是与每一次的wsarecv或wsasend操作一一对应的。
在网络模型的常见设计中,当一个客户端连接到服务器后,服务器会通过accept或AcceptEx创建一个socket,而应用层为了保存与此socket相关的其它信息(比如:该socket所对应的sockaddr_in结构体数据,该结构......
一点感慨:作网络通信,其实就是在作内存(缓冲区)管理(2010-07-08 21:15:00)
摘要:最近看代码,感慨很多。作一个高性能的网络通信模块,除了要选择高性能的网络通信模型之外,同样重要的就是:你的内存(缓冲区)是如何管理的。
在网络通信模型方面,现在的技术都是比较成熟的了,在win下使用iocp,在linux下使用epoll。它们的使用方法,相对来说,还是比较有章可循的。
内存之于高性能,大致有以下两个方面需要特别注意:
1.尽可能地减少内存的动态申请和释放;
2.尽可能地减少内存数据的复制;
解决第1个问题,我们可以使用内存池;而解决第2个问题,就会牵涉到程序的架构设计了。
理想的情况是:
在数据接收方面,一个包,只要从网络模块接收下来后,直到它被上层逻辑使用完毕之后才会丢弃,而不应该在网络接收模块和上层逻辑模块之间增加任何的有关复制该包的行为和操作;
在数据发送方面,发送的时所用的缓冲区,不应该是即时申请的,而是从内存池中取的可用缓冲区,用完后再放回。
而我们看到,对于前者,也只有在架构方面作一些精巧布局才能达到目的。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sodme/archive/2005/09/27/490905.aspx......
IOCP中的socket错误和资源释放处理方法(2010-07-08 21:14:00)
摘要:本文作者:sodme
本文出处:http://blog.csdn.net/sodme
声明:本文可以不经作者同意任意转载、复制、传播,但任何对本文的引用均须保留本文的作者、出处及本行声明信息!谢谢!
前言:
错误处理和socket释放, 是IOCP编程中的一大难点. 本文试图就IOCP设计中经常遇到的这个难题展开论述并寻找其解决方案, 事实上, 文中所述的解决方式不仅仅适用于IOCP, 它同样适用于EPOLL等多种服务器编程的网络模型中, 前提是: 领会这种处理方式的实质.
正文:
在使用IOCP开发时, 大家经常遇到的一个难题是与socket相关的缓冲区释放不当带来的错误, 这种错误通常是由于多次对同一个指针执行了delete操作引起的. 比如, 当在执行wsasend或wsarecv返回了非pending的错误信息时, 我们就要对此错误进行处理, 通常情况下, 我们会想到执行这两步操作:
a. 释放此次操作使用的缓冲区数据(如果不释放可能造成内存泄漏);
b. 关闭当前操作所使用的socket.
而另一方面, 我们可能也会在get函数(GetQueuedCompletionStatus)的处理中, 当get函数返回值为FALSE时也作这两步相同的操作. 此时, 就会造成对同一缓冲区的重复释放, 问题由此产生.
解决的方法, 可以有这几种:
1. 对数据缓冲区使用引用计数机制;
2. 在clientsock的对象设计机制上使释放操作线性化.
关于这两种方法, 任何一种如果要详细说清, 可能篇幅都会比较长, 笔者并无耐心和精力将每一个细节都一一道来, 在此仅选第2种方案的关键步骤和核心思想来与大家分享.
由前面对问题的描述可以看出, 造成多次释放的原因可能是在执行收发操作和GET函数返回值为FALSE时, 我们重复执行了释放操作. 很自然地, 我们会想到, 能不能把这两次释放合并成一次释放, 这样不就没问题了吗? yes, 这个思路是没问题的. 但要想让这个思路能变成现实, 需要在设计机制上对这个思路进行一定的支......
精确定时代码(2010-07-08 21:13:00)
摘要:__int64 Count(void)
{
_asm _emit 0x0F
_asm _emit 0x31
}......
Winsock错误码(MSDN)(2010-07-08 21:12:00)
摘要:http://blog.csdn.net/sodme/archive/2005/04/20/354991.aspx......
完成端口中WSAENOBUFS错误的解决方案(2010-07-08 21:09:00)
摘要:摘自CSDN论坛
一、 WSAENOBUFS 错误问题。
这个问题通常很难靠直觉发现,因为当你第一次看见的时候你或许认为是一个内存泄露错误。假定已经开发完成了你的完成端口服务器并且运行的一切良好,但是当你对其进行压力测试的时候突然发现服务器被中止而不处理任何请求了,如果你运气好的话你会很快发现是因为WSAENOBUFS 错误而影响了这一切。
每当我们重叠提交一个send或receive操作的时候,其中指定的发送或接收缓冲区就被锁定了。当内存缓冲区被锁定后,将不能从物理内存进行分页。操作系统有一个锁定最大数的限制,一旦超过这个锁定的限制,那么就会产生WSAENOBUFS 错误了。
如果一个服务器提交了非常多的重叠的receive在每一个连接上,那么限制会随着连接数的增长而变化。如果一个服务器能够预先估计可能会产生的最大并发连接数,服务器可以投递一个使用零缓冲区的receive在每一个连接上。因为当你提交操作没有缓冲区时,那么也不会存在内存被锁定了。使用这种办法后,当你的receive操作事件完成返回时,该socket底层缓冲区的数据会原封不动的还在其中而没有被读取到receive操作的缓冲区来。此时,服务器可以简单的调用非阻塞式的recv将存在socket缓冲区中的数据全部读出来,一直到recv返回 WSAEWOULDBLOCK 为止。
这种设计非常适合那些可以牺牲数据吞吐量而换取巨大并发连接数的服务器。当然,你也需要意识到如何让客户端的行为尽量避免对服务器造成影响。在上一个例子中,当一个零缓冲区的receive操作被返回后使用一个非阻塞的recv去读取socket缓冲区中的数据,如果服务器此时可预计到将会有爆发的数据流,那么可以考虑此时投递一个或者多个receive来取代非阻塞的recv来进行数据接收。(这比你使用1个缺省的8K缓冲区来接收要好的多。)
总结:
解决方法一:
投递使用空缓冲区的 recevie操作,当操作返回后,使用非阻塞的recv来进行真实数据的读取。因此在完成端口的每一个连接中需要使用......
epoll与iocp的异同之处(2010-07-08 21:07:00)
摘要:本文作者:sodme
本文出处:http://blog.csdn.net/sodme
目前国内的网游研发,在服务器使用的开发平台方面,win和Linux的比例各占多少,我一时半会也没有准确数据,但从我了解的这么多公司情况来看,用win系统的还是比较多一点,这些企业一般都是比较单纯的网游公司,而用linux的则多数是一些传统的互联网公司,比如网易和腾讯。
网游服务器用win还是linux,向来都是大家关注的话题。我想,原因可能很多,但此处不想过多论述这个问题,为避免多费口舌,我还是明确表明一下自己的观点:我是推荐用linux作开发的,虽然我也是刚转来作linux平台下的开发。
那么,说具体一点。但凡作过比较深入的网络编程的人,都会知道,在win平台下,高效的IO模型是IOCP,而在linux底下则是epoll。那么,epoll与iocp之间到底有哪些异同之处呢?
首先,我们看一下它们相同的地方。
两者都是处理异步IO的高效模型,这种高效,除了“异步处理”这个共同的特征之外,二者都可以通过指针携带应用层数据:在IOCP里,应用层数据可以通过单句柄数据和单IO数据来与IOCP底层通信;而在epoll里,可以通过epoll_data里的"void *ptr"来传递。这是一种很重要的思想,也是它们高效的原因所在:当事件的通知到来时,它不仅告诉你发生了什么样的事件,还同时告诉这次事件所操作的数据是哪些。
那么,epoll和iocp到底又有什么不同呢?
以我目前粗浅的使用经验来看,至少可以得到以下结论:
1.iocp是在IO操作完成之后,才通过get函数返回这个完成通知的;而epoll则不是在IO操作完成之后才通知你,它的工作原理是,你如果想进行IO操作时,先向epoll查询是否可读或可写,如果处于可读或可写状态后,epoll会通过epoll_wait函数通知你,此时你再进行进一步的recv或send操作。
2.在1的基础上,我们其实可以看到,epoll仅仅是一个异步事件的通知机制,其......
IOCP Tips(2010-07-08 21:05:00)
摘要:
IOCP Tips
Tip 1 : 使用WSASend/WSARecv来收发数据,而不是使用ReadFile/WriteFile
一句话,前者具有更好的性能
Tip 2: 理解IOCP的最大并发线程数和工作线程数
应该让工作线程数(调用GetQueuedCompletionStatus那些线程)大于等于在CreateIoCompletionPort 指定的NumberOfConcurrentThreads数。
标准做法是永远设置NumberOfConcurrentThreads=0
Tip 3: 利用GetQueuedCompletionStatus的completion key和overlapped structure参数在异步操作中来传递信息
通常completion key用来传递和handle/socket/session的信息
而overlapped structure用来传递每次异步I/O的一些信息,通常的做法是会定义一个structure来派生于OVERLAPPED
struct MY_IO_DATA : public OVERLAPPED
Tip 4: 理解IOCP的完成包的排队行为
从GetQueuedCompletionStatus得到完成包的次序可能跟调用WSASend/WSARecv的次序不一样。
微软唯一保证是如果调用WSASend/WSARecv得到SUCCESS或者IO_PENDING,就一定会有一个完成包出现在IOCP的队列上,不管这个socket是否关闭了。
如果关闭socket,那么之后的WSASend/WSARecv调用就一定返回失败的结果。
关于IOCP包可能次序错乱和解决方法,有一篇文章可以参考: http://www.codeproject.com/KB/IP/reusablesocketserver4.aspx
我的做法是避免多次调用WSARecv
Tip 5: IOCP的清除
最重要的一点是,在I/O完成之前,不要释放overlapped structure。可以用HasOverlappedIoCompleted......
GetWindowsDirectory()获取系统安装目录(2010-07-05 21:21:00)
摘要:获取windows系统安装目录
代码如下
#include <iostream>
#include <windows.h>
int main(int argc, char* argv[])
{
TCHAR szPath[MAX_PATH];
GetWindowsDirectory(szPath,MAX_PATH);
std::cout << szPath << std::endl;
return 0;
}
Output:
C:\WINDOWS
......
TCHAR*(IP格式字符串)转 struct in_addr(2010-06-29 16:24:00)
摘要:本文代码完成"192.168.1.1"这类的字符串转换到struct in_addr中存储
// TCAHR to char
BOOL WCharToMByte(LPCWSTR lpcwszStr, LPSTR lpszStr, DWORD dwSize)
{
DWORD dwMinSize;
dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
if(dwSize < dwMinSize)
{
return FALSE;
}
WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpszStr,dwSize,NULL,FALSE);
return TRUE;
}
//////////////////////////////////////////////////////////////////
char temp[18];
DWORD dwAddr;
struct in_addr devIP;
TCHAR szIP[] = _T("......