首先说明,下面的代码仅是一个IOCP的demo,很多地方的设计非常差,当然也有一些设计还算可以:)。此篇仅供对IOCP有些了解但又不深入的、需要一个稍微完整示例的、对网络编程感兴趣的同学参考。点击这里下载代码

整个程序的流程如下:

流程完全是无阻塞的,主线程里,将收到的消息全都一次性取出后,然后派发。所有欲发送的消息都缓存起来,等到更新的时候一起发送。有些地方代码没有完善,比如断开连接后,socket、内存等资源的关闭回收。要注意MAXRECEIVEDBUFFLENGTH这个宏,它是定义每个socket消息收发时缓冲区的大小。如果很大,会非常吃内存的。我在这里也没有做粘包、分包的情况处理。工程还带了一个C#写的测试客户端。在我的机器上,能有12W的连接。

关键代码如下:

void MyIOCP::Execute()
{
PerHandleData* pPerHandleData = nullptr;
PerIOData* pPerIOData = nullptr;
LPOVERLAPPED lpOverLapped = nullptr;
DWORD byteTransferred;
BOOL bRet = FALSE; while (true)
{
bRet = GetQueuedCompletionStatus(mCompletionPort,
&byteTransferred,
(PULONG_PTR)&pPerHandleData,
/*(LPOVERLAPPED*)&pPerIOData*/&lpOverLapped,
INFINITE);
pPerIOData = (PerIOData*)CONTAINING_RECORD(lpOverLapped, PerIOData, overlapped);
if (bRet == FALSE)
{
if (pPerIOData == nullptr)
{
// 退出线程。服务器要关了。。
break;
}
// 对方主动断开了 continue;
} // 处理成功的完成端口请求
switch (pPerIOData->operationType)
{
case CDH::E_INVALID:
{
}
break;
case CDH::E_ACCEPT:
{
/************************************************************************/
/*
inet_ntoa(ClientAddr->sin_addr) 是客户端IP地址 ntohs(ClientAddr->sin_port) 是客户端连入的端口 inet_ntoa(LocalAddr ->sin_addr) 是本地IP地址 ntohs(LocalAddr -AZ>sin_port) 是本地通讯的端口 pIoContext->m_wsaBuf.buf 是存储客户端发来第一组数据的缓冲区
*/
/************************************************************************/
SOCKADDR_IN* ClientAddr = NULL;
SOCKADDR_IN* LocalAddr = NULL;
int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);
mlpfnGetAcceptExSockAddrs(pPerIOData->buffer, , sizeof(SOCKADDR_IN)+, sizeof(SOCKADDR_IN)+, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen); WrapSocket* connectedSocket = GetSocketAndEraseFromTheMap(pPerIOData->remoteSocket, WRAPESOCKETTYPE::E_READYTOBECONNECTED);
if (connectedSocket == nullptr)
{
closesocket(pPerIOData->remoteSocket);
continue;
}
if (!AssociateDeviceWithCompletionPort(connectedSocket))
{
closesocket(pPerIOData->remoteSocket);
continue;
}
// 添加到已经连接列表
InsertWrapSocketToMap(connectedSocket, WRAPESOCKETTYPE::E_CONNECTED); PostReceive(connectedSocket); // 这次消耗了一个准备好了的socket, 现在再生成一个socket待连接。
PostAcceptEx(connectedSocket->GetPerHandleData().hasListenSocket);
}
break;
case CDH::E_RECV:
{
int socket = pPerHandleData->selfSocket;
WrapSocket* wrapSocket = nullptr;
GetSocketForSendOrRecvData(socket, wrapSocket); if (wrapSocket != nullptr)
{
wrapSocket->GetPerIODataReceive().bufferLen = byteTransferred;
Receive(wrapSocket);
}
}
break;
case CDH::E_SEND:
{
int socket = pPerHandleData->selfSocket;
WrapSocket* wrapSocket = nullptr;
GetSocketForSendOrRecvData(socket, wrapSocket);
if (wrapSocket != nullptr)
{
wrapSocket->SendingData(false);
} }
break;
case CDH::E_CONNECT:
break;
default:
break;
} }
}

注意CDH::E_RECV,收到消息后,将消息缓存,然后直接又进行投递PostReceive(),这其实是非常不好的。应该分情况来选择是否立即投递。

以上,整个代码,基本是用C++的格式写C代码。希望以后能有机会与大家共同分享一个比较完整的、面向对象风格的IOCP。

分享我写的IOCP:源码+思路的更多相关文章

  1. 【学】jQuery的源码思路1——后代选择器

    jQuery的源码思路1--后代选择器 这里探讨一下jQuery中后代选择器的封装原理,并自己写一下 getEle('#div1 ul li .box');接受的参数就是个后代选择器,类似于这样: # ...

  2. 手写Koa.js源码

    用Node.js写一个web服务器,我前面已经写过两篇文章了: 第一篇是不使用任何框架也能搭建一个web服务器,主要是熟悉Node.js原生API的使用:使用Node.js原生API写一个web服务器 ...

  3. 手写@koa/router源码

    上一篇文章我们讲了Koa的基本架构,可以看到Koa的基本架构只有中间件内核,并没有其他功能,路由功能也没有.要实现路由功能我们必须引入第三方中间件,本文要讲的路由中间件是@koa/router,这个中 ...

  4. 【学】jQuery的源码思路2——$符号是如何封装的

    jQuery中的$符号功能很强大,原因在于对函数参数的个数以及种类的控制,还有对于面向对象思想的运用 function jQuery(args){ //接受参数,并对其判断 this.elements ...

  5. 分享45个android实例源码,很好很强大

    分享45个android实例源码,很好很强大 http://www.apkbus.com/android-20978-1-1.html 分享45个android实例源码,很好很强大http://www ...

  6. C写的扫描器源码

    Title:C写的扫描器源码 --2010-10-27 20:02 无意间看见的一个源代码,弄回来读下. ----------------------------------------------- ...

  7. 分享45个android实例源码,很好很强大.收藏吧!!!

    andriod闹钟源代码 http://www.apkbus.com/android-20974-1-1.html android源码分享之指南针程序 http://www.apkbus.com/an ...

  8. Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)

    Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)    转 https://blog.csdn.net/lhl1124281072/article/details/800 ...

  9. 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...

  10. 手写Express.js源码

    上一篇文章我们讲了怎么用Node.js原生API来写一个web服务器,虽然代码比较丑,但是基本功能还是有的.但是一般我们不会直接用原生API来写,而是借助框架来做,比如本文要讲的Express.通过上 ...

随机推荐

  1. [AngularJS] Using ngModel in Custom Directives

    You can use ngModel in your own directives, but there are a few things you'll need to do to get it w ...

  2. 将Uploads文件夹移到其它地方

    1.在创建目录 cd /mnt/xvdb mkdir qingtong 2.复制目录和权限 cp -rp /Uploads/ ./ -r 包括目录 p权限 3.创建软链接 ln -s /mnt/xvd ...

  3. 工作vs.学�

    近一两年来,我先后对[工作与学习]的复杂过程有过多次的头脑风暴,而且感觉在这方面略有所成(看这里和这里):当然既然仅仅是头脑风暴,所谓的所成也仅仅是一些粗糙的想法,一些没有实证过的如果,算是积累而已, ...

  4. StarlingMVC Framework中文教程

    配置与开始 将Starling项目配置为StarlingMVC项目,仅需几行代码.在继承于starling.display.Sprite的起始类里,创建一个StarlingMVC的实例,并传递给它三个 ...

  5. mysql 日期函数格式

    mysql中的from_unixtime函数的format表达式字段描述: %M 月名字(January……December) %W 星期名字(Sunday……Saturday)  %D 有英语前缀的 ...

  6. yum服务器设置

    转自:http://blog.chinaunix.net/uid-22141042-id-1789605.html 不得不说,RedHat的确很邪恶,如果我们直接用他自带的系统碟做YUM源的话,总是会 ...

  7. 目录操作函数opendir、readdir和closedir

    首先,明确一个类型DIR的含义: #include <dirent.h> DIR    A type representing a directory stream. DIR是在目录项格式 ...

  8. CCTableView的使用和注意事项

    #include "cocos-ext.h" using namespace cocos2d::extension; class TableViewTestLayer: publi ...

  9. idl 批量裁剪代码

    PRO Subset_via_shp_update COMPILE_OPT idl2 ENVI,/restore_base_save_files envi_batch_init,LOG_FILE='b ...

  10. truncate与delete 、drop的区别

    注意事项 1.在oracle中数据删除后还能回滚是因为它把原始数据放到了undo表空间. 2.DML语句使用undo表空间,DDL语句不使用undo,  而delete是DML语句,truncate是 ...