Linux环境使用select实现IO多路复用

什么是端口复用

举一个简单地网络服务器的例子,如果你的服务器需要和多个客户端保持连接,处理客户端的请求,属于多进程的并发问题,如果创建很多个进程来处理这些IO流,会导致CPU占有率很高。

所以人们提出了I/O多路复用模型:一个线程,通过记录I/O流的状态来同时管理多个I/O。

select只是IO复用的一种方式,其他的还有:poll,epoll等。

实现代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <list>
#include <math.h>


std::list<int>  g_client_socks;

int main()
{
    fd_set m_set;
    int m_maxfd = -1;
    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    bool m_isListen = false;
    sockaddr_in addr;
    int len = sizeof(sockaddr_in);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("10.50.30.31");
    addr.sin_port = htons(8888);
    bind(serv_sock, (sockaddr*)&addr, sizeof(addr));

    while (1)
    {
        //每次都要重新设置集合才能激发事件,对已存在的socketfd重新设置
        FD_ZERO(&m_set);

        //只监听一次服务端口
        if(!m_isListen)
        {
            int ret = listen(serv_sock, 1);
            m_isListen = ret==0;
            printf("listen %s\n", ret==0?"ok":"error");
        }

        //填充FD_SET,并获取最大fd
        FD_SET(serv_sock, &m_set);
        m_maxfd = std::max(m_maxfd, serv_sock);
        for(auto item=g_client_socks.begin(); item!=g_client_socks.end(); ++item)
        {
            FD_SET(*item, &m_set);
            m_maxfd = std::max(m_maxfd, *item);
        }

        //开始select
        timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 50000; //50ms  每次都需要设置
        select(m_maxfd+1, &m_set, NULL, NULL, &tv);
        
        //监听接口有没有新连接的客户端
        if(FD_ISSET(serv_sock, &m_set))
        {
            int new_client = accept(serv_sock, (sockaddr*)&addr, (socklen_t*)&len);
            g_client_socks.push_back(new_client);
            printf("serv_socket is select, fd:%d\n", new_client);
        }

        //遍历客户端有没有接收到数据
        for(auto item=g_client_socks.begin(); item!=g_client_socks.end(); ++item)
        {
            if(FD_ISSET(*item, &m_set))
            {
                char buf[128] = {0};
                int ret = recv(*item, buf, sizeof(buf), 0);
                printf("recv data.ret:%d fd:%d recv:[%s]\n", ret, *item, buf);
                //ret为0代表断开连接
                if(ret == 0)
                {
                    printf("fd %d disconnnect\n", *item);
                    close(*item);
                    g_client_socks.erase(item++);
                }
            }
        }

        usleep(5000);
    }
    
    return 0;
}

效果

  • 可实现多路客户端同时连接
  • 打印任意客户端发送的数据
  • 可检测到客户端断开连接

仅用作简单的学习记录,存在逻辑考虑不周的地方希望大家多多指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值