单线程实现同时监听多个端口(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 ...
随机推荐
- JS 对象(Object)和字符串(String)互转方法
利用原生JSON对象,将对象转为字符串 1 2 3 4 5 6 var jsObj = {}; jsObj.testArray = [1,2,3,4,5]; jsObj.name = 'CSS3'; ...
- shell工具-sort
sort sort命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出 基本语法 sort [选项] [参数] 选项说明 选项 说明 -n 依照数值大小排序 -r 以相反的顺序排序 - ...
- 修复msvcp120.dll
https://www.microsoft.com/zh-CN/download/details.aspx?id=40784
- Visualise the Argyris basis functions
""" Author: kinnala Visualise the Argyris basis functions. """ from sk ...
- 《javascript高级程序设计》 touch事件的一个小错误
最近一段时候都在拜读尼古拉斯大神的<javascript高级程序设计>,真的是一本好书,通俗易懂,条理比<javascript权威指南>好理解一些,当然<javascri ...
- 由异常掉电问题---谈xfs文件系统
由异常掉电问题---谈xfs文件系统 本文皆是作者自己的学习总结或感悟(linux环境),如有不对,欢迎提出一起探讨!! 目录结构 一.相关知识 二.问题提出 三.处理方法 四.最终结果 一.相关知识 ...
- 如何利用JUnit开展一个简单的单元测试(测试控制台输出是否正确)
待测类(CreateString)如下: public class CreateString { public void createString() { //Output the following ...
- Windows核心编程:第14章 探索虚拟内存
Github https://github.com/gongluck/Windows-Core-Program.git //第14章 探索虚拟内存.cpp: 定义应用程序的入口点. // #inclu ...
- 记录一次错误处理 (xml序列化和反序列化相关)
XML序列化后,反序列化时出现错误 报错现象 System.InvalidOperationException: XML 文档(40, 11)中有错误. ---> System.Xml.XmlE ...
- Python3.5 学习二十二
回顾: 发送请求时:发送请求头和请求数据 request.META和request.request.body 响应请求时:响应头和响应返回数据 response.HEADER和response.bod ...