分享我写的IOCP:源码+思路
首先说明,下面的代码仅是一个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:源码+思路的更多相关文章
- 【学】jQuery的源码思路1——后代选择器
jQuery的源码思路1--后代选择器 这里探讨一下jQuery中后代选择器的封装原理,并自己写一下 getEle('#div1 ul li .box');接受的参数就是个后代选择器,类似于这样: # ...
- 手写Koa.js源码
用Node.js写一个web服务器,我前面已经写过两篇文章了: 第一篇是不使用任何框架也能搭建一个web服务器,主要是熟悉Node.js原生API的使用:使用Node.js原生API写一个web服务器 ...
- 手写@koa/router源码
上一篇文章我们讲了Koa的基本架构,可以看到Koa的基本架构只有中间件内核,并没有其他功能,路由功能也没有.要实现路由功能我们必须引入第三方中间件,本文要讲的路由中间件是@koa/router,这个中 ...
- 【学】jQuery的源码思路2——$符号是如何封装的
jQuery中的$符号功能很强大,原因在于对函数参数的个数以及种类的控制,还有对于面向对象思想的运用 function jQuery(args){ //接受参数,并对其判断 this.elements ...
- 分享45个android实例源码,很好很强大
分享45个android实例源码,很好很强大 http://www.apkbus.com/android-20978-1-1.html 分享45个android实例源码,很好很强大http://www ...
- C写的扫描器源码
Title:C写的扫描器源码 --2010-10-27 20:02 无意间看见的一个源代码,弄回来读下. ----------------------------------------------- ...
- 分享45个android实例源码,很好很强大.收藏吧!!!
andriod闹钟源代码 http://www.apkbus.com/android-20974-1-1.html android源码分享之指南针程序 http://www.apkbus.com/an ...
- Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)
Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码) 转 https://blog.csdn.net/lhl1124281072/article/details/800 ...
- 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...
- 手写Express.js源码
上一篇文章我们讲了怎么用Node.js原生API来写一个web服务器,虽然代码比较丑,但是基本功能还是有的.但是一般我们不会直接用原生API来写,而是借助框架来做,比如本文要讲的Express.通过上 ...
随机推荐
- Mouse_event 鼠标点击
//这样就是左键单击 mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );// 按下mouse_event( MOUSEEVENTF_LEFTUP, 0, ...
- Redis 脚本
Redis 脚本使用 Lua 解释器来执行脚本. Reids 2.6 版本通过内嵌支持 Lua 环境.执行脚本的常用命令为 EVAL. 语法 Eval 命令的基本语法如下: redis 127.0.0 ...
- 支持https请求以及https请求的抓包
iOS9推出的时候,苹果希望大家使用https协议,来提高数据传输之间的安全性.下面我就从最简单的代码介绍,如何在工程中设置,来支持https的请求. 一.证书准备篇 1.证书转换 在服务器人员,给你 ...
- 从wordcount 开始 mapreduce (C++\hadoop streaming模式)
序:终于开始接触hadoop了,从wordcount开始 1. 采用hadoop streamming模式 优点:支持C++ pathon shell 等多种语言,学习成本较低,不需要了解hadoop ...
- HTTP协议理解
HTTP(Hyper Text Transfer Protocol)超文本传输协议,是一种请求响应式协议,类似两国会晤中需要遵守的规则.那么,其中有什么内容特点呢? HTTP特点: 1)支持客户端/服 ...
- ie6双边距bug及其解决办法
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- byte[] bytes和string转换
public static string ToHexString ( byte[] bytes ) // 0xae00cf => "AE00CF " { ...
- javascript一些常用操作
一:验证日期 1:日期必须满足yyyy-MM-dd格式 2:日期必须是合法的日期,如2016-02-30就是不存在 //验证就诊日期 function checkVisitDate(date){ va ...
- LeetCode 264
Ugly Number II Write a program to find the n-th ugly number. Ugly numbers are positive numbers whose ...
- hdu 4251 划分树
思路:裸的划分树 #include<iostream> #include<algorithm> #include<cstdio> #include<cmath ...