Linux 网络编程 select复用模式

本文探讨了网络编程中的多线程与Select复用模式的区别与应用,通过对比分析,提出了一种更高效的单线程Select复用模式实现方式,以应对高并发场景下的连接管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

起先用多线程进行网络编程,即在服务器上监听socket,当监听到新的客户端连接时,开启一个线程进行处理,直至结束。后来感觉如果客户端很多的话,就会有相应的线程,必定影响效率,所以就尝试了用select复用模式。在网上查了很多资料,感觉大部分都是误人子弟,而且还都是ctrl-c  ctrl-v的,连转载都没标。

在服务器上有两种描述字,一种是服务器自己的socket描述符,用于监听新的连接:

     int listenFD = socket(AF_INET, SOCK_STREAM, 0);

另一种就是收到的新的连接的描述符:

    int acceptFD = accept(listenFD,(struct sockaddr *)&clientAddr,&len);

网上很多教程是说把select()和accept()放在两个线程里去实现,即线程一:

while(true)
{
    int acceptFD = accept(listenFD,(struct sockaddr *)&clientAddr,&len);
    if(accpetSk < 0)
     {
         cout<<"accept failed!"<<endl;
         return ;
     }
     cout<<"accept a new socket"<<endl;
     FD_SET(acceptFD,&mFdRead);
 }

线程2:

FD_ZERO(&mFdRead);
while(true)
{
    int ret =select(maxFd + 1,&mFdRead,NULL,NULL,NULL);
    //或者select(100,&mFdRead,NULL,NULL,&tv);
}

其中mFdRead是fd_set类型的描述字集合。
这种方法的话如果收到一个新的连接,不能马上进行读写和发送,甚至永远阻塞在select上,因为当线程二运行到select()时,就一直阻塞在那里(前提是最后一个参数为NULL或者0),即使accept()到一个新的描述字,并FD_SET()放入集合mFdRead中,但select所用的集合还是原来的旧集合,而且maxFD也是旧的,所以即使新的描述字读写状态发生变化了,select()根本不会检测到。当select用超时设置时,那也是等超时到了,在下一个周期中进行重新检测,这会导致数据丢失。

所以最好的办法是将服务器用于监听的套间字也放入到描述字集合中和收到的对端的描述字进行统一的监测。这样的话也不需要将accept()和select()分到两个线程中,只需一个就可以了。步骤为:
1.生成监听套间字。
2.将套间字set到描述字集合中;
3.进行select()监测;
4.监测到描述字状态变化并可读写时,先判断变化的描述符是监听套间字还是接受到的对端描述符。
5.如果是监听套间字,则进行accept()操作;
6.如果是对端描述符则进行recv() send()操作;

代码为:

int listenFD = 0;
int maxFd = 0;
fd_set fdRead;
vector<int> vecFd;
listenFd = socket(AF_INET, SOCK_STREAM, 0);
bind(.......);
listen(.......);

maxFd = listenFd;
while(true)
{
        FD_ZERO(&fdRead);
        FD_SET(listenFD,&fdRead);
        for(int i = 0 ; i < vecFd.size(); ++i)
        {
            FD_SET(vecFd[i],&fdRead);
        }
        struct timeval tv = {10,0};
        int selRet = select(maxFd + 1,&fdRead,NULL,NULL,&tv);
        if(selRet <= 0)
        {
            cout<<"select failed or overtime!!"<<endl;
            continue;
        }
        //首先先查看是不是监听套间字检测到状态变化
        if(FD_ISSET(listenFd,&fdRead))      
        {
            struct sockaddr_in clientAddr;
            socklen_t  len = sizeof(clientAddr);
            int acceptFd = accept(listenFD,(struct sockaddr *)&clientAddr,&len);
            if(acceptFd < 0)
            {
                continue;
            }
            FD_SET(acceptFd,&fdRead);  //将新的描述符set到集合中
            vecFd.push_back(acceptFd);  
            maxFd = max(maxFd,acceptFd);  //获得描述符中最大的值
        }

        for(int i = 0; i < vecFd.size(); ++i)
        {
            if(FD_ISSET(vecFd[i],&fdRead))
            {
                int recRet = recv(......);
                int sendRet = send(......);
            }
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值