单线程实现同时监听多个端口(windows平台c++代码)
前言
多年前开发了一套网络库,底层实现采用IOCP(完成端口)。该库已在公司多个程序中应用;经过多次修改,长时间检验,已经非常稳定高效。
最近把以前的代码梳理了一下,又加进了一些新的思路。代码结构更加合理,性能也有所提升。打算将该库一些的知识点写出来,以供参考。
服务端要在多个端口监听,这种场合并不多见。但作为一个完善的网络库,似乎有必要支持此功能的。
传统实现方法
如果监听端口个数很少,也可以采用传统的方法。因为accept函数是阻塞的,所以要实现在n个端口监听,就需要n个线程。如果监听端口个数不多,这也不是多大问题。如果监听端口多达几十个,这种方法就有些不妥。线程也是一种资源,线程过多占用资源会增加;也会导致系统负担加重。
更可行的实现方法
实现方法有些曲折,需要一步一步分析;基本的原理就是将socket句柄与事件(event)相关联。Windows有相关的函数可以对多个事件监听,当某个事件被触发,就知道相应的socket有事件到达。可以对该socket做accept,因为已经确定该socket有事件了,所以accept函数会立即返回。这样就达到对多个端口同时监听的目的。
1)生成socket,并与某个端口绑定
struct LISTEN_SOCKET_INFO
{
UINT16 listenPort; //监听端口
SOCKET listenSocket;//句柄
WSAEVENT netEvent; //socket对应事件
}; int IocpAccept::CreateListenInfo()
{
//m_listListenPort存储要监听的端口;总个数不超过64个
std::vector<UINT16>::iterator pos = m_listListenPort.begin();
for (;pos != m_listListenPort.end();++pos)
{
//生成socket
UINT16 listenPort = *pos;
LISTEN_SOCKET_INFO socketInfo;
socketInfo.listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
socketInfo.listenPort = listenPort; //绑定端口
sockaddr_in InetAddr;
InetAddr.sin_family = AF_INET;
InetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InetAddr.sin_port = htons(listenPort); int ret = bind(socketInfo.listenSocket, (SOCKADDR *)&InetAddr, sizeof(InetAddr));
if (SOCKET_ERROR == ret)
{
::closesocket(socketInfo.listenSocket);
//绑定失败
continue;
} //生成事件
socketInfo.netEvent = WSACreateEvent(); //将socket句柄与事件关联起来。只监视socket的accept和close消息
ret = WSAEventSelect(socketInfo.listenSocket, socketInfo.netEvent, FD_ACCEPT | FD_CLOSE);
if (SOCKET_ERROR == ret)
{
::closesocket(socketInfo.listenSocket);
continue;
} // 启动监听
ret = listen(socketInfo.listenSocket, );
if (SOCKET_ERROR == ret)
{
::closesocket(socketInfo.listenSocket);
continue;
} m_listListenInfo.push_back(socketInfo);
}
return ;
}
该函数已将需要的数据存储在列表m_listListenInfo中。
2)启动监听线程,对多个事件监听
对多个事件监听用到如下函数:
DWORD WSAAPI WSAWaitForMultipleEvents( DWORD cEvents, const WSAEVENT *lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable );该函数最多可以对64个事件做跟踪,所以一个线程最多可以对64个端口做监听。(同时对超过64个端口监听的场合非常少见。本文不考虑。)
//生成事件地址指针
int nEventTotal;
WSAEVENT* pEventArray = CreateNetEventArray(&nEventTotal);
if (nEventTotal == )
return ;
assert(nEventTotal <= WSA_MAXIMUM_WAIT_EVENTS); MSG msg;
while (m_bServerStart)
{
// 同时对多个事件做监听
DWORD index = WSAWaitForMultipleEvents(nEventTotal,
pEventArray,
FALSE,
,
FALSE);
if (!m_bServerStart)
return ; //查看是哪个事件触发函数返回
index = index - WSA_WAIT_EVENT_0;
//客户端连接事件
if ((index != WSA_WAIT_FAILED) && (index != WSA_WAIT_TIMEOUT))
{
//pEventArray排序与m_listListenInfo一样,所以可以根据index找到对应的socket。
//就是该socket导致函数返回
LISTEN_SOCKET_INFO socketInfo = m_listListenInfo[index]; //查看具体是什么事件导致函数返回
WSANETWORKEVENTS NetworkEvents;
WSAEnumNetworkEvents(socketInfo.listenSocket, pEventArray[index], &NetworkEvents); //如果是accept事件,说明有客户端连接此端口
if (NetworkEvents.lNetworkEvents == FD_ACCEPT
&& NetworkEvents.iErrorCode[FD_ACCEPT_BIT] == )
{
//这时调用accept函数,会立即返回
AcceptListenPort(socketInfo.listenSocket, socketInfo.listenPort);
}
if (NetworkEvents.lNetworkEvents == FD_CLOSE
&& NetworkEvents.iErrorCode[FD_CLOSE_BIT] == )
{
assert(false);
} }
else
{
//因为超时等其他原因引起函数返回
}
}
下文accept函数调用,并不会阻塞。
UINT IocpAccept::AcceptListenPort(SOCKET hListenSocket, UINT16 nListenPort)
{
SOCKET hClient = ;
SOCKADDR_IN localAddr;
int iaddrSize = sizeof(SOCKADDR_IN); hClient = accept(hListenSocket, (struct sockaddr *)&localAddr, &iaddrSize);
if (INVALID_SOCKET == hClient)
{
int nAccepetError = WSAGetLastError();
if (nAccepetError == WSAECONNRESET)
{
return ;
}
else
{
return ;
}
}
else
{
//获取了一个客户端连接
OnAcceptClient(hClient, nListenPort);
}
return ;
}
后记:同时对多个端口做监听,可能还有更好的方法。如果对几百个以上端口做监听,此方法可能就不太合适。通常情况下,对多个端口监听的场景比较少见,所以对更优化的处理方法也没深究。
代码下载地址:https://download.csdn.net/download/qq_29939347/10691921
单线程实现同时监听多个端口(windows平台c++代码)的更多相关文章
- Memcached监听多个端口_同一台Windows机器中启动多个Memcached服务
下载Memcached服务器 假设你解压在"C:\Program Files\memcached\memcached.exe" 那么可以如下创建多个服务监听不同的端口啦 监听第一个 ...
- wriesharek同时监听多个端口
之前的文章<wireshark解析自定义的protobuf协议> ,当时只监听了一个端口,而如果游戏同时有二个 socket 连接,比如一个是网关另外一个是其它的,怎么办呢? for i, ...
- linux下启动多个php,分别监听不同的端口。
在工作中,我们可能会遇到,服务器集群的搭建. 这个时候,我们不可能,每一台服务器都是lnmp的环境,我们会把nmp分别放在不同的服务器上,不同的服务器负责不同的功能.比如我们下面要说的php 加入ng ...
- prometheus 监控 jar应用服务 + 修改监听IP和端口
1.修改服务的启动脚本 [root@do1cloud01 init.d]# vim learn-school nohup ${JAVA_HOME}/bin/java -javaagent:/usr/l ...
- apache反向代理和监听多个端口设置
修改apache配置文件httpd.conf 一.监听多个端口 在Listen 80后添加监听端口,如 Listen 80 Listen 8080 Listen 8008 二.反向代理设置 1.取消一 ...
- 同一台机器oralce11g和12c公用一个监听器监听多个端口
启动数据库服务 (这里还没有启动监听器) 如上图OracleServiceORCL为11g的服务,服务名为orcl,OracleServiceWX为12c的服务名,服务名为wx,两个服务均已正常启动, ...
- ssh 监听多个端口
修改sshd的配置文件 默认位置:/etc/ssh/sshd_config 注释掉 Port 这行 然后添加 ListenAddress 行 e.g: ListenAddress 192.168 ...
- CentOS7 配置 SSH监听多个端口方法
一.修改ssh默认端口,防止暴力破解,让系统安全多一点点: i. 在配置文件/etc/ssh/sshd_config文件中修改 Port #AddressFamily any #ListenAddre ...
- 本地启动服务,两个进程分别监听两个端口,导致两个 URL 不同
问题描述: 本地启了两个服务:A(http://localhost:8001) B(http://localhost:8000),A 项目要怎么才能关联到 B 项目,也就是 A 项目请求怎么跳到 B ...
随机推荐
- Kotlin, Android的Swift
Kotlin, Android的Swift 苹果已经用Swift代替Objective-C,一种古老的语言,来进行iOS的开发了.明显Android开发也有这个趋势. 虽然现在已经可以选择Scala或 ...
- leetcode - [4]Sort List
Sort a linked list in O(n log n) time using constant space complexity. 思路:采用归并排序或者快速排序 #include < ...
- day30(对象转json(java))
转换之前需要知道什么是json json类似于map集合:键值对的方式存在,是一种轻量级数据交互格式. eg: {name:" ",age:15} [{name:" &q ...
- springMvc里的mvc:resources与静态资源的访问
在进行Spring MVC的配置时,通常我们会配置一个dispatcher servlet用于处理对应的URL.配置如下: <servlet> <servlet-name&g ...
- SSM_CRUD新手练习(4)修改生成的mapper.xml映射文件
我们为什么要修改呢,这是因为我们查询的时候,我们有时候需要连表查询,例如我们需要查询出员工表的信息(emp_id,emp_name...)与此同时,我们还想查询出该员工所在的部门(dept_name) ...
- hdu 4975 最大流快版
http://acm.hdu.edu.cn/showproblem.php?pid=4975 给出每行每列的和,问是否存在这样的表格:每个小格放的数字只能是0--9. 直接用第八场最大流模板. #in ...
- file新建文件及文件夹
1.获取包名的根目录:mRootPath = getFilesDir().getParent(); // ====mRootPath===/data/data/com.yoyu.file 获取SD卡 ...
- C# 使用Queue<T>代替递归算法遍历树
递归时候每次调用自身在堆栈上要记录返回地址,而堆栈的空间很少,调用次数多了后会产生堆栈溢出,以下代码是实际项目中,通过Queue<T>来避免递归算法的代码: /// <summary ...
- 《javascript高级程序设计》 touch事件的一个小错误
最近一段时候都在拜读尼古拉斯大神的<javascript高级程序设计>,真的是一本好书,通俗易懂,条理比<javascript权威指南>好理解一些,当然<javascri ...
- 笑话库存加网址http://www.jokeji.cn/list18_11.htm
19.富二代王晓伟成绩很差,老爸想给他找个家教老爸:“儿子,想找什么样的家教啊?”儿子:“要漂亮的,女的,衣服不能太保守,花样要多!”老爸:“儿子,你TM指的是岛国的苍老师吗?”@呦呦ta爹 20.哥 ...