美高梅娱乐4858.com[转载]IOCP+WinSock2初函数打造大性能SOCKET池

出自:
http://gamebabyrocksun.blog.163.com/blog/static/57153463201021554716831/

     
因为前面一样段刚去矣千篇一律趟杭州之阿里TD时代交流,就从来不好意思在部门间申请Qcon的门票。但结尾还是没耐得住心里的瘙痒,请假去放了,这里差不多谢把票让给我之XX同学(应他求,马赛克了名字)。

 

     
今年底Qcon延续了往的品质,讲师大部分凡是业界里较为公认的权威,牛人,选题也特别绝着近期看好:架构,运维,大数据,机器上,安全,敏捷,动态语言,云,移动支付,前端技术,测试,软件过程,可以说凡是全面。各个领域的专家以马上三龙里不停的争奇斗艳,热潮一浪高过一浪。今年底口吧是比较往常多矣众多,一个工作人员哥们告诉自己,今年发出1.7k之临场者,怪不得每次中场休息走廊里大多还倒不动,上只男性厕所还要排队5分钟吧。在京都这IT精(diao)英(si)聚集之城,Qcon的生机显然是远强大的。

每当头里无异篇文章《WinSock2编程的制一体化的SOCKET池
》中,介绍了WinSock2的部分新函数,并着重详细介绍了呀是SOCKET池,有矣这定义,现在就随即进行更深入之讨论。

     
Qcon其实是一个偏于IT具体技术的一个交流盛会,对于测试人员来说,餐会有点儿硬。哥虽然现在一个月份啊写不了两三百履代码,工作备受80%底时日也未是以和技术细节死磕,但吃几十万尽代码的稿本,还有雷同粒热(si)爱(xing)技(bu)术(gai)的心目。还是能够挺享受饕餮的。测试是始终本行,所以听的早晚记录了众多测试相关的文化及感,在这里开一整理,也毕竟学而时习之。

率先这里要根本重申一下便是,SOCKET池主要借助的是动面向连接的协商的场面下,最常用之就算是待管住大量底TCP连接的时刻。常见的尽管是Web服务器、FTP服务器等。

     
有关运维:于互联网时代,可以说晚的运营才是互联网经营之第一,前期的付出只是基础之根基。就比如:微软卖于你office拷贝以后,直接就是足以以到钱,交付后定期打打补丁就实行了,但是无论是一个网游,就得保证服务一直是好的,因为玩家付费的的确由是–“我直接会耍得爽”。因此“软件/产品之生命周期“不再是从需求及付,而是由要求及最终下线。运维的显要更加高,地位也越来越大,因为她们承载了还多对”质量“的医护职责。运维平台因此呢易得愈强,相应的,也不怕转换得尤为重。Qcon上,BAT,新浪,搜狗等多各项运维专家还提了她们对此运维的更心得。总结起来有这样几只趋势:

下面就是分步骤的事无巨细介绍如何最终实现SOCKET池。

  • 运维朝着大规模自动化发展,降低人工操作所占有的比例,这样做的利益来次:一凡是故障恢复,自动比手动抢;二凡自动化赋予了运维人员管理再多机器的力。
  • 冲自己企业之IT架构构筑了运维平台,完成复杂的调度、维护工作。这种发展是于给现实逼迫和自身追求的搅和着逐渐落实之。
  • 初的IT架构越来越考虑运维需求,如Docker,充分考虑了运维方面的便利。
  • 运维的基础还是:错误归类,总结,提取,搞定,优化这五管,用一个成语来说即使是百炼成钢。这种思路其实适用于各种运营类工作,比如供水供电。
  • 乘构筑在互联网上的物越来越重要,对于可靠性的渴求吗是一再创新大,小数点后面要求挂的9一发多。其实挂9是一个难度指数级递增的干活,多挂一个,付出的也许持续十加倍之投入。
  • 一对思维:运维相关的效能实在在过剩的盘算惯性中凡免属功能测试内容的,在系统测试过程被,很多团组织为会见把它忽略掉。在几位讲师的讲演受到,都关心被feature,而尚未教测试相关的题目。讲解的暂停,与大多各项运维工程师还有讲师交流,感觉到运维工程师的思量方法是:试错+演练。而无是专门同情被下大量标准的测试来以上线前保持运维相关feature质量之保。我猜测也许是工作性质,还有老以无比前沿游走,艺高人胆大造就的这种工作思路。我交流的总人口未敷多,可能无备代表性,但本身之发上,运维测试是当前游人如织集团还见面忽略的盲点。在自己的工作受到,我们会与运维工程师合作,抽取运维feature的测试点,作为系统测试的必选项,但也是在旅途,因为我们的运维工程师也非赞同被投入极其多日及首的测试合作面临,因为她们最忙碌了:)

 

     至于新技巧:

同、WinSock2环境的初始化:

   
 IT业是世界上提高极快速的行业。钱多,自由,创新这些特点吸引了诸多强智力的武器挤上前这个圈子,成就了各种实际技术更新换代以月计算的快慢。下面胡乱侃侃:

 

  • 前面几年即曾紫透了底讲,今年越紫的黑黝黝,已经发生那么些底风俗企业开为云上搬家。搭建在云上的化解方案吗更是丰富,导致各种aas满天飞。但称也是独相对年轻的技巧,身上多事物还尚无长全,各种基础技术支撑点上还有好多软项,比如支持的功底网络,分布式存储、计算,安全解决方案,可靠性,传统IT系统迁移的方案等等,都还是大有可为。
  • Docker今年算大放异彩,从上年之崭露头角,到了本年多人开大规模利用。Redis也成了无数丁之初宠。我起码在5场演讲受到听到了讲师所在团队对Redis的行使,这也难怪。上心灵,跑的呢快,谁休喜?
  • 这次Qcon有3个专题是在走前端上行使Native和web融合技术的尽。可见开发人员早已垂涎html的好了,毕竟趋利避害是天性。移动端cpu再抢点儿,web性能上重复加把油,其实会是大趋势。
  • 本年底Qcon上node.js有个别萎靡,也许是最最过简短,太过好用,已经没啥可摆了。但是中小网站开发上,node已经是战争燎原了。
  • n种动态语言由于个别的优势化为了成千上万新兴创业公司的挑,比如小米网使用Go语言实现了它的秒杀系统,使用的初衷就是是:像C上心灵,建模容易,对出现支持好。云巴采用了Erlang来打他们之系,看中的即使是其对大产出的强支撑(当然还发出任何很好之特性,云巴CEO的那场我杀喜欢,因为做了交易中间件的测试,对中间的好多内容十分有谢)。
  • 老语言PHP也以直接进步。终于盼了传说被之Laruence,他说话了PHP7开发的一对工作,由于对php不了解加上底层功力太肤浅,只放清楚了聊一半。但是,咱们国人有食指以做这样牛逼的做事,觉得还是殊自豪之。
  • Amazon对python的支撑真是自己:)各种给力,Netflix的案例挺不错。个人认为要后台对计量逻辑要求很复杂,对贸易的起要求又非是最好刻薄。全python技术栈将会晤是单可怜好之选项。
  • 想:对于层出不穷的初技巧,我看it人员不用患有恐惧症。一则,深研一家,比如java,玩至炉火纯青,就算别的还稍会,一年为个30w还是特别轻之。二则,全是改头换面,懂基础就是能够高效搞定所有,编译原理牛逼的话,动态语言那些炫酷特性秒懂本质,5分钟及亲手;linux环境编程牛逼的话,你见面发觉Docker神马的就是当整治一打出系统调用,Erlang神马的虽是以打闹进程,云平台也没啥神秘。现在去出来的软件之趋向还是左手越来越爱,比如JAVA时代动辄上千个布局起的庞大已经在为公众唾弃了,新的东西都是微若得意,学习成本会无限死。例如,今年消费了3完善来读书node.js。每天免顶2钟头,3圆满了后,已经好以阿里云直达勾单稍网站了,新技巧都是更进一步简单的自由化。三则,一般it工作者都是不断学习型人才,用到啊,学就是了。

假如使WinSock2就需要事先初始化Socket2.0的条件,不赘述,上代码:

     
作为一个测试人员,该怎么开吧?其实一样,也得无停歇的攻,不然你的为测物改变了,而你没就掌握有关文化,怎么测试得好吧?因此,如果组织采用了redis,测试人员至少得理解简单的治本命令,会采用一个客户端程序(如jedis)来贯彻对数据库的概括操作;如果组织以了docker,测试人员就要懂得当docker下您的受测物部署细节之改变;如果你的组织于开发andriod程序,测试人员最好会读懂andriod的框架程序,并且掌握对应之测试技术栈(andriod
instrumentation等)。

WSADATA wd = {0};

     关于敏捷:

int iError = WSAStartup(MAKEWORD(2,0), &wd);

   
最后一天在快捷专场听了3会。对京东底那无异摆不是老大有谢,觉得无出口来什么实际的事物来。袁店明的对立有意思一些,把他以前看的森坑还抠了出让大家看,由于前少年与了庄的高速转型,又跟了多独敏捷团队的行事法,对有的话题或蛮有谢的。吴穹先生的话题挺风趣,他竟是拿“复杂理论”和求实的管理工作结合了起,让丁耳目一新。他领取的几乎本书我呢都扣留了,但是怎么不会见来他那样的idea呢?这应当是涉世和程度的题目,还得继续修炼。

if( 0 != iError )

   
敏捷在炎黄早就过了一个怒的大循环,归于平静。一员讲师在发问,谁的团伙在于是高速模型的下,在场一多半总人口且选了手。但是真正快了么?袁店明以亮他的坑的当儿,大部分丁还会心一笑。这无异于乐大心酸。因为当时象征,还有非常丰富的里程如果倒。

{//出现谬误,最好跟踪看下错误码是有些

   
 全效集团,自组织集体,所有干系人(包括支付组织的上下游,如客户,业务人员,运维人员,客服等)都快起来,各种IT技术的支持(自动化测试,持续集成等)在切切实实的路上,整个行业还有漫长路要活动。

       return FALSE;

 

}

  关于测试:

if ( LOBYTE(lpwsaData->wVersion) != 2 )

   
 其实Qcon还是生几许集市测试专题的,但是都并未失去参加。作为一个测试人员,这好像有点合常理。但是专题内容接近以前还任了。且时刻宝贵,还是把再多之目光投到多少熟悉的圈子开辟眼界吧,测试的专场等ppt放出去了再度举行回顾。

{//非2.0上述条件 退出了事 可能是不行的WinCE系统

 

       WSACleanup();

      对此工作直接的提携:

       return FALSE;

  •  会进一步增长对运维相关测试的关切,在团结的工作屡遭做改进。运维可测性是一个课题。
  •  对片新技巧来矣询问,会花费有日子召开上。虽然所处的风俗习惯行业对新技巧响应较迟缓,但是曾经冒出了新的来头,要持续跟踪公司技术动态变更,争取不给取下,甚至走以面前。

}

 

末了重复未使用WinSock之后还使记调用一下WSACleanup()这个函数;

  零散的有些感触:

 

  •  这个行当的牛人太多矣,比你聪明太多,还于你奋力,而且发生好眼光,善于抓住机会。看多了未免会生出零星羡慕妒忌妒恨,但是急需还原情绪,切忌跟自的寻常为敌,努力办好团结就行。
  • 看来了n多特大型系统的案例,而且针对可靠性要求更加强了。互联网行业,特别是同样线的互联网行业对线上缺点的容忍度越来越小,测试的要会更加强(不见得是tester,因为发平等栽思潮还是“烧大专职tester”,虽然我看当下是蹭的,因为是世界上导致不至那基本上吗还见面的卓著,还是日常团队多),质量系的做事会晤尤其多,反正要有人干,tester们,振作起来吧,干不好就告一段落了,干好了,成事儿的就是是咱们。
  • 行发展最好抢,持续学习的能力最重大。
  • 有人吐槽Qcon的showgirl一上600冠起场费,不淡定了。其实乃自己竟一终,工资1.5w的言语你同天的出场费是700状元左右,还无用站同一天,然后被同堆积拖鞋短裤双肩背谢顶眼镜男不停歇的偷瞄yy。it人员努努力就交1.5w了,人如果满足,要看妹子们格外辛苦,来少了,并且用少了。
  • 得多锻炼身体,多吃黑豆。程序员谢顶率太强了,看得有一定量心惊肉跳。

二、装载WinSock2函数:

美高梅娱乐4858.com 1 
         
 本图是也配合标题而盗窃的希冀。版权属微博博主。

 


一篇稿子被吃出了一个装载WinSock2函数之近乎,这里说明介绍下装载的切实可行过程,要提拔的就是,凡是类里演示了动态装载的函数,最好且像那样动态载
入,然后再调用。以免出现上网发帖跪求高手赐教为什么AcceptEx函数无法编译通过当题材。看罢马上篇稿子详细而免见面再错过发帖寻找答案了,呵呵呵,好了,
上代码:

//定义一个好用之载入函数 摘自CGRSMsSockFun 类

BOOL LoadWSAFun(GUID&funGuid,void*& pFun)

{//本函数利用参数返回函数指针

       DWORD dwBytes = 0;

       pFun = NULL;

       //随便创建一个SOCKET供WSAIoctl使用 并不一定要如下这样创建

       SOCKET skTemp = ::WSASocket(AF_INET,

                     SOCK_STREAM, IPPROTO_TCP, NULL,

                     0, WSA_FLAG_OVERLAPPED);

       if(INVALID_SOCKET == skTemp)

       {//通常表示尚无正规的初始化WinSock环境

              return FALSE;

       }

       ::WSAIoctl(skTemp, SIO_GET_EXTENSION_FUNCTION_POINTER,

                     &funGuid,sizeof(funGuid),&pFun,

                     sizeof(pFun), &dwBytes, NULL,NULL);

       ::closesocket(skTemp);

       return NULL != pFun;

}

//演示如何动态载入AcceptEx函数

……

LPFN_ACCEPTEX pfnAcceptEx; //首先声明函数指针

GUID GuidAcceptEx = WSAID_ACCEPTEX;

LoadWSAFun(GuidAcceptEx,(void*&)pfnAcceptEx); //载入

……

//使用丰富的参数调用

……

pfnAcceptEx(sListenSocket,sAcceptSocket,lpOutputBuffer,

             
dwReceiveDataLength,dwLocalAddressLength,dwRemoteAddressLength,

lpdwBytesReceived,lpOverlapped);

              //或者:

              SOCKET skAccept =
::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,

                                                 NULL,
0,WSA_FLAG_OVERLAPPED);

              PVOID pBuf = new BYTE[sizeof(sockaddr_in) + 16];

              pfnAcceptEx(skServer, skAccept,pBuf,

0,//将吸纳缓冲置为0,令AcceptEx直接返回,防止拒绝服务攻击

                     sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) +
16, NULL,

                                                
(LPOVERLAPPED)pAcceptOL);

……

      
以上是一个简便的示范,如何动态载入一个WinSock2扩展函数,并调用的,其它函数的详实例子可以看前一篇稿子中CGRSMsSockFun类的落实有。如果利用CGRSMsSockFun
类的讲话当然还简便,像下这样调用即可:

              CGRSMsSockFun MsSockFun;

              MsSockFun.AcceptEx(skServer, skAccept,pBuf,

0,//将收到缓冲置为0,令AcceptEx直接回到,防止拒绝服务攻击

                     sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) +
16, NULL,

                     (LPOVERLAPPED)pAcceptOL);

若是如采用此类似,那么得有窜,主要是甚处理部分,自己注释掉,或者用其他异常代替掉即可,这个对于发出根基的读者来说不是啊难题。

 

三、定义OVERLAPPED结构:

 

设惦记“IOCP”就设起定义OVERLAPPED,这是清玩转IOCP的不次模拟门,可以这么说:“江湖上产生些许种起定义的OVERLAPPED派生结构体,就生出多少种IOCP的包!”

OVERLAPPED 本身是Windows
IOCP机制内部用的一个结构体,主要用来记录每个IO操作的“完成状态”,其情对调用者来说是尚未意思之,但是洋洋时段我们把它作为一个“火车
头”,因为她可便宜的管每个IO操作的相干数据略的“从调用处运输及就回调函数中”,这是一个可怜实惠的特点,哪么如何被这火车头发挥运输的用意
呢?其实挺简单:让她变成一个自定义的重新甚结构体的第一独成员。然后据此强制类型转换,将起定义的结构体转换成OVERLAPPED指针即可。当然不自然非
要是初结构体的第一只成员,也可以是其余第n个分子,这时用VC头文件被预定义的一个宏CONTAINING_RECORD再度反转回来即可。

说交此地有C++基础差一点的读者估计已充分晕了,更非晓得自家加以什么,那么自己不怕拿好人做到底吧,来诠释下是来龙去脉。

率先就是盖我们将要利用的AcceptEx函数为例子看看她的原型吧(知道孙悟空的火眼金睛用来涉及嘛的啊?就是之所以来拘禁原型的,哈哈哈):

BOOL AcceptEx(

  __in          SOCKET sListenSocket,

  __in          SOCKET sAcceptSocket,

  __in          PVOID lpOutputBuffer,

  __in          DWORD dwReceiveDataLength,

  __in          DWORD dwLocalAddressLength,

  __in          DWORD dwRemoteAddressLength,

  __out         LPDWORD lpdwBytesReceived,

  __in          LPOVERLAPPED lpOverlapped

);

注 意最后一个参数,是一个OVERLAPPED结构体的指针(LP的意是Long
Pointer,即对32号地址长指针,注意不是“老婆”拼音的缩写),本身是参数的意思就是是分配一片OVERLAPPED大小的内存,在IOCP调
用方式下传递让AcceptEx函数用,调用者不用去关爱里面的其它内容,而在完成过程被(很多时候是其它一个线程中之政工了),通常调用
GetQueuedCompletionStatus函数后,会重赢得这个指针,接着被咱吧省它的原型:

BOOL WINAPI GetQueuedCompletionStatus(

  __in          HANDLE CompletionPort,

  __out         LPDWORD lpNumberOfBytes,

  __out         PULONG_PTR lpCompletionKey,

  __out         LPOVERLAPPED* lpOverlapped,

  __in          DWORD dwMilliseconds

);


意这里的LPOVERLAPPED多了一个*化为了指针的指针,并且前面的认证很清楚Out!很亮了咔嚓,不亮即使真的Out了。这里就可以重新得到调用
AcceptEx传入的LPOVERLAPPED指针,也即是得了此“火车头”,因为只有是一个指针,并不曾详细的界定能起差不多那个,所以可以当火车头的后
面放很多事物。

双重细致察看GetQueuedCompletionStatus
函数的参数,会意识,这时只能解一个IO操作了了,但是究竟是谁操作完了,或者是孰SOCKET句柄上的操作了了,并从未法知道。通常这个
信息挺重要,因为只有当IO操作实际到位后才会自由发送或收受等操作的缓冲区。

这些信方可定义成如下的一个恢宏OVERLAPPED结构:

struct MYOVERLAPPED

{

    OVERLAPPED m_ol;          

    int                    m_iOpType;      

//操作类型 0=AcceptEx 1=DisconnectEx       2=ConnectEx 3=WSARecv等等

    SOCKET          m_skServer;       //服务端SOCKET

    SOCKET         m_skClient;        //客户端SOCKET

    LPVOID           m_pBuf;            //本次IO操作的缓冲指针

    ……                                           //其它需要的信息

};

使用时:

MYOVERLAPPED* pMyOL = new MYOVERLAPPED;

ZeroMemory(pMyOL,sizeof(MYOVERLAPPED));

pMyOL->m_iOpType = 0;        //AcceptEx操作

pMyOL->m_skServer = skServer;

pMyOL->m_skClient = skClient;

BYTE* pBuf = new BYTE[256];//一个缓冲

……………… //朝缓冲中描写副东西

pMyOL->m_pBuf = pBuf;

……………//其它的代码

AcceptEx(skServer, skClient,pBuf,

0,//将接纳缓冲置为0,令AcceptEx直接回到                     

256,256,NULL,(LPOVERLAPPED)pMyOL));//注意最后是强制类型转换

 

       在完成过程回调线程函数中,这样以:

 

UINT CALLBACK Client_IOCPThread(void* pParam)

       {//IOCP线程函数

              …………………

              DWORD dwBytesTrans = 0;

              DWORD dwPerData = 0;

              LPOVERLAPPED lpOverlapped = NULL;

             

              while(1)

              {//又见死循环 呵呵呵

                     BOOL bRet = GetQueuedCompletionStatus(

                            pThis->m_IOCP,&dwBytesTrans,&dwPerData,

                            &lpOverlapped,INFINITE);

                     if( NULL == lpOverlapped )

                     {//没有真的的就

                            SleepEx(20,TRUE);//故意置化可警告状态

                            continue;

                     }

                     //找回“火车头”以及背后的具备东西

                     MYOVERLAPPED*  pOL =
CONTAINING_RECORD(lpOverlapped

, MYOVERLAPPED, m_ol);

                     switch(pOL->m_iOpType)

{

case 0: //AcceptEx结束

{//有链接进来了 SOCKET句子柄就是 pMyOL->m_skClient

   

}

break;

……………………….

}

……………………

} //end while

……………………

       }//end fun

 

迄今为止,关于这“火车头”如何采取,应该是圈明白了,其实就算是从函数传入,又由函数返回。只不过其间可能早已更换了线程环境,是殊之线程了。

此再次续一个AcceptEx容易被遗漏的一个细节问题,那即便是于AcceptEx完成回后,如下在特别连入的客户端SOCKET上调用一下:

       int nRet = ::setsockopt(

             
pOL->m_skClient,SOL_SOCKET,SO_UPDATE_ACCEPT_CONTEXT,

              (char *)&pOL->m_skServer,sizeof(SOCKET));

然才好持续于是代表客户端连接的pOL->m_skClient上此起彼伏调整用WSARecv和WSASend。

另外,在AcceptEx完成后,通常可以用:

LPSOCKADDR addrHost = NULL;      //服务端地址

LPSOCKADDR addrClient = NULL;     //客户端地址

int lenHost = 0;

int lenClient = 0;

GetAcceptExSockaddrs(

       pOL->m_pBuf,0,sizeof(sockaddr_in) + 16,sizeof(sockaddr_in)

  • 16,

       (LPSOCKADDR*) &addrHost,&lenHost,(LPSOCKADDR*)
&addrClient,&lenClient);

即时
样来获得连入的客户端地址,以及连入的服务端地址,通常是地方可以和之客户端的SOCKET绑定以共同从而map或hash表保存,方便查询,就绝不再行
调用特别getpeername得到客户端的地方了。要注意的凡GetAcceptExSockaddrs也是一个WinSock2扩展函数,专门配合
AcceptEx使用的,需要像AcceptEx那样动态载入一下,然后再次调用,详情请见前一篇稿子中之CGRSMsSockFun类。

至此AcceptEx算讨论完整了,OVERLAPPED的派生定义为操了了,让我们继承下同样步。

 

季、编写线程池回调函数:

 


讨论扩展定义OVERLAPPED结构体时,给出了非线程池版的线程函数的大约框架,也便是人情IOCP使用的自建线程使用办法,这种措施要自己创造好
端口句柄,自己以SOCKET句柄绑定到好端口,这里就无在赘述,主要介绍下调用BindIoCompletionCallback函数时,应怎样编写
这个线程池的回调函数,其实她同前面老线程函数是雅类似的。先来瞧回调函数增长个什么样子:

VOID CALLBACK FileIOCompletionRoutine(

  [in]                 DWORD dwErrorCode,

  [in]                 DWORD dwNumberOfBytesTransfered,

  [in]                 LPOVERLAPPED lpOverlapped

);

第一独参数就是一个错误码,如果是0恭喜你,操作一切ok,如果发生摩擦吗不用慌,前同一篇稿子被曾经介绍了何等翻译与看懂这个错误码。照在做就是是了。


亚单参数就是说这次IO操作一共就了略微字节的数传任务,这个字段有只非常含义,如果您发现一个Recv操作了了,并且是参数为0,那么就是是
说,客户端断开了连(注意对的是TCP方式,整个SOCKET池就是吧TCP方式设计的)。如果这个情形有了,在SOCKET池中尽管该回收这个
SOCKET词柄。

老三单参数现在不要多说了,立刻就懂得怎么用其了。跟方调用GetQueuedCompletionStatus函数得到的指针是一个含义。

下就来拘禁一个贯彻这回调的例子:

VOID CALLBACK MyIOCPThread(DWORD dwErrorCode

,DWORD dwBytesTrans,LPOVERLAPPED lpOverlapped)

       {//IOCP回调函数

              …………………

              if( NULL == lpOverlapped )

              {//没有当真的完结

                     SleepEx(20,TRUE);//故意置化可警告状态

                     return;

              }

              //找回“火车头”以及后的装有东西

              MYOVERLAPPED*  pOL = CONTAINING_RECORD(lpOverlapped

, MYOVERLAPPED, m_ol);

              switch(pOL->m_iOpType)

{

case 0: //AcceptEx结束

{//有链接进来了 SOCKET句柄就是 pMyOL->m_skClient

   

}

break;

……………………….

}

……………………

       }//end fun

 


起来特别简短吧?好像少了哟?对了那个该死的巡回,这里并非了,因为这个是由线程池回调的一个函数而已,线程的活动状态完全是因为系统间控制,只管认为要
有IO操作就了,此函数就会被调用。这里关注之热点就是了的放了好之后的操作上,而什么线程啊,完成端口句柄啊什么的就是还非需了(甚至足以淡忘
记)。

此处要专注一个题目,正使以《IOCP编程的“双节棍”》中涉嫌的,这个函数执行时不要了长,否则会并发掉线啊,连接不上啊之类奇怪之事情。

其他一个如果留意的题目就是,这个函数最好套上结构化异常处理,尽可能的大都拦截与处理非常,防止系统线程池的线程因为您不好之回调函数而壮烈牺牲,如果在了出现控制,还要注意防止死锁,不然你的服务器会“死”的不胜丢脸。

辩驳及来说,你一直可拿这个函数看做一个及线程池函数等价格的函数,只是他而硬着头皮的“短”(指执行时)而严密(结构清晰少出错)。

末,回调函数定义好了,就好调用BindIoCompletionCallback函数,将一个SOCKET句柄丢进好端口的线程池了:

BindIoCompletionCallback((HANDLE)skClient,MyIOCPThread,0);


意最后一个参数到目前为止,你不怕招入0吧。这个函数的神奇就是遗失了CreateIoCompletionPort的调用,不见了
CreateThread的调用,不见了GetQueuedCompletionStatus等等的调用,省去了n多繁琐且便于失误的步子,一个函数就全
部搞定了。

 

五、服务端调用:

 

如上的富有手续在一点一滴知晓后,最终深受咱们看SOCKET池如何实现的。

1、按照民俗,要先行监听到某某IP的指定端口上:

SOCKADDR_IN    saServer = {0};

//创建监听Socket

SOCKET skServer = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP

, NULL, 0, WSA_FLAG_OVERLAPPED);

//把监听SOCKET扔进线程池,这个好简单              
::BindIoCompletionCallback((HANDLE)skServer,MyIOCPThread, 0);

//必要时打开SO_REUSEADDR属性,重新绑定到是监听地址

BOOL   bReuse=TRUE;                 
::setsockopt(m_skServer,SOL_SOCKET,SO_REUSEADDR

,(LPCSTR)&bReuse,sizeof(BOOL));

saServer.sin_family = AF_INET;

saServer.sin_addr.s_addr = INADDR_ANY;

// INADDR_ANY这个价的魅力是监听所有地方IP的一致端口

saServer.sin_port = htons(80);      //用80得永生

::bind(skServer,(LPSOCKADDR)&saServer,sizeof(SOCKADDR_IN));

//监听,队列长为默认最充分连接SOMAXCONN

listen(skServer, SOMAXCONN);

 

2、就是来同样万分堆的AcceptEx调用:

for(UINT i = 0; i < 1000; i++)

{//调用1000次

//创建同客户端通讯的SOCKET,注意SOCKET的创导方式

skAccept = ::WSASocket(AF_INET,

                                             
SOCK_STREAM,

                                             
IPPROTO_TCP,

                                             
NULL,

                                             
0,

                                             
WSA_FLAG_OVERLAPPED);

//2011-07-28:以上为原文,下面为改写后底代码

skClient = ::WSASocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED );

//丢进线程池中

BindIoCompletionCallback((HANDLE)skAccept
,MyIOCPThread,0);

//2011-07-28:以上为原文写法,下面为修改后代码,主要为重新凑巧变量名,方便大家了解

BindIoCompletionCallback((HANDLE)skClient
,MyIOCPThread,0);

//创建一个自定义的OVERLAPPED扩展结构,使用IOCP方式调用

pMyOL= new MYOVERLAPPED;

pMyOL->m_iOpType = 0;        //AcceptEx操作

pMyOL->m_skServer = skServer;

pMyOL->m_skClient = skClient;

BYTE* pBuf = new BYTE[256];//一个缓冲

ZeroMemory(pBuf,256*sizeof(BYTE));

pMyOL->m_pBuf = pBuf;

//发出AcceptEx调用

//注意将AcceptEx函数接收连续数据缓冲的高低设定成了0

//这将造成这个函数立即回到,虽然与不设定成0的法门而言,

//这致使了一个比较低下的频率,但是如此提高了安全性

//所以这种频率牺牲是必须的

//=================================================================================

//2011-07-28日修改了底的代码 把原来的老二个参数skAccept 改吗 skClient
为便宜大家读与喻

AcceptEx(skServer, skClient,pBuf,

    0,//将接缓冲置为0,令AcceptEx直接回到,防止拒绝服务攻击

    256,256,NULL,(LPOVERLAPPED)pMyOL);

 

}

当即
样就闹1000独AcceptEx在提前等着客户端的连了,即使1000单冒出连接为就是了,当然要再次BT点那么就是放1w个,什么您一旦推广2w独?那便
要看看您的这个IP段的端口还够不敷了,还有你的体系外存够不敷用。一定要专注和一个IP地址上反驳及端口最大值是65535,也不怕是6w多独,这个要合
理的摊,如果起管理超过6w个以上的接连时,怎么惩罚吧?那便再次插块网卡租个新的IP,然后重新朝着好IP端绑定并监听即可。因为使用了
INADDR_ANY,所以一律监听虽是具有地方IP的相同端口,如果服务器的IP有前后网之分,为了安全及分起见可以明显指定监听哪个IP,单IP时就是
要小心本IP空闲端口底数额问题了。

 

3、AcceptEx返回后,也就算是线程函数中,判定是AcceptEx操作返回后,首先需要之调用就是:

GetAcceptExSockaddrs(pBuf,0,sizeof(sockaddr_in) + 16,

       sizeof(sockaddr_in) + 16,(LPSOCKADDR*) &addrHost,&lenHost,

       (LPSOCKADDR*) &addrClient,&lenClient);

int nRet = ::setsockopt(pOL->m_skClient, SOL_SOCKET,

       SO_UPDATE_ACCEPT_CONTEXT,(char
*)&m_skServer,sizeof(m_skServer));

尔后便得WSASend或者WSARecv了。

      
4、这些调用了晚,就可当这个m_skClient上收发数据了,如果收发数据截止或者IO错误,那么就是回收SOCKET进入SOCKET池:

       DisconnectEx(m_skClient,&pData->m_ol, TF_REUSE_SOCKET, 0);

      
5、当DisconnectEx函数完成操作之后,在回调的线程函数中,像下这样还于这个SOCKET进入监听状态,等待下一个用户连接上,至此组建SOCKET池的目的就着实达到了:

//创建一个自定义的OVERLAPPED扩展结构,使用IOCP方式调用

pMyOL= new MYOVERLAPPED;

pMyOL->m_iOpType = 0;        //AcceptEx操作

pMyOL->m_skServer = skServer;

pMyOL->m_skClient = skClient;

BYTE* pBuf = new BYTE[256];//一个缓冲

ZeroMemory(pBuf,256*sizeof(BYTE));

pMyOL->m_pBuf = pBuf;

AcceptEx(skServer, skClient,pBuf , 0,256,256,NULL,

    (LPOVERLAPPED)pMyOL);

//注意在这SOCKET被再次祭后,后面的复打到完成端口的操作会返回一个曾设置//的缪,这个似是而非直接让忽视即可

         ::BindIoCompletionCallback((HANDLE)skClient,Server_IOCPThread,
0);

      
至此服务端的线程池就算搭建完成了,这个SOCKET池也便是环AcceptEx和DisconnectEx展开的,而创造操作就满门且在劳动启动的瞬
间形成,一次性投递肯定数额之SOCKET进入SOCKET池即可,这个数目为即是通常所说之极端深并发连接数,你爱小就是装粗吧,如果连续多数量就
大些,如果IO操作多,连接断开请求不多便少碰,剩下就是是调节了。

 

六、客户端调用:

 

1、  主要是环绕利用ConnectEx开始调用:

SOCKET skConnect = ::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_IP,

                            NULL,0,WSA_FLAG_OVERLAPPED);

//把SOCKET扔进IOCP

BindIoCompletionCallback((HANDLE)skConnect,MyIOCPThread,0);

//本地随便绑个端口

SOCKADDR_IN LocalIP = {};

LocalIP.sin_family = AF_INET;

LocalIP.sin_addr.s_addr = INADDR_ANY;

LocalIP.sin_port = htons( (short)0 );    //使用0让系统活动分配

int result =::bind(skConnect,(LPSOCKADDR)&LocalIP,sizeof(SOCKADDR_IN));

pMyOL= new MYOVERLAPPED;

pMyOL->m_iOpType = 2;            //ConnectEx操作

pMyOL->m_skServer = NULL;    //没有服务端的SOCKET

pMyOL->m_skClient = skConnect;

ConnectEx(skConnect,(const sockaddr*)pRemoteAddr,sizeof(SOCKADDR_IN),

       NULL,0,NULL,(LPOVERLAPPED)pOL) )

假设喜欢就足以将地方的进程置于循环中去,pRemoteAddr就是长途服务器的IP和端口,你可再连接n多单,然后疯狂下充斥东西(别说自家报你的哈,人家的服务器宕机了追寻你承担)。注意充分绑定一定要是起,不然调用会失败的。

2、  接下去就在线程函数中判断是ConnectEx操作,通过判定m_iOpType ==
2就好解,然后这样做:

setsockopt( pOL->m_skClient, SOL_SOCKET,
SO_UPDATE_CONNECT_CONTEXT,

                     NULL, 0 );

然后便是自由之按用调用WSASend或者WSARecv。

3、 
最后使及服务端相似之逻辑调用DisconnectEx函数,收回SOCKET并一直还调用ConnectEx连接到其他一样服务器或平等之同样服务器即可。


此客户端的SOCKET池也搭建得了,创建SOCKET的干活吗是在同等初步之一次性就做到了,后面还是动ConnectEx和
DisconnectEx函数不断的总是-收发数据-回收-再连接来开展的。客户端的是SOCKET池可以用来HTTP下载文件之客户端还是FTP下载
的服务端(反向服务端)或者客户端,甚至可看作一个网游的机器人系统,也足以看做一个压力测试的客户端核心之型。

 

七、总结及增长:


上就是是比较完好的焉切实实现SOCKET池的全部内容,因为篇幅的案由就是不糊全部底代码了,我相信各位看客看罢后心中应该生出个盖的框架,并且也可以
进行实际的代码编写工作了。可以据此纯c来促成啊足以为此C++来实现。但是这里而说明某些就是是DisconnectEx函数和ConnectEx函数似乎就
能在XP
SP2以上以及2003Server上述之平台达成运,对于服务端的话这不是啊问题,但是于客户端的话,使用SOCKET池时还要考虑一个兼容性问题,
不得已还是如舍弃当客户端应用SOCKET池。

SOCKET池的全方位花就在
提前创建同批判SOCKET,然后就无休止的再次回收再使,比从传统的非SOCKET池方式,节省了大气之持续创造及销毁SOCKET对象的基石操作,同
时借用IOCP函数AcceptEx、ConnectEx和DisconnectEx等的异步IO完成特性提升了总体性,非常适合用于一些消大规模
TCP连接管理的状况,如:HTTP Server FTP Server和玩服务器等。

SOCKET池的实质就是是尽量的施用了IOCP模型的几所有优势,因此如果为此好SOCKET池就要深入之解IOCP模型,这是前提。有题目要跟帖讨论。

(本文原创,转载请注明出处。)

发表评论

电子邮件地址不会被公开。 必填项已用*标注