基于select的IO多路复用样例

NTcp.h文件

#ifndef NTcp_H
#define NTcp_H
#include <iostream>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <thread>
#include <string>
#include <atomic>
#include <map>

enum E_network_model
{
    NETWORK_MODEL_SERVER = 0,
    NETWORK_MODEL_CLIENT
};

class NTcp
{
public:
    NTcp(const std::string &ip, int port, enum E_network_model model = NETWORK_MODEL_CLIENT);
    ~NTcp();
    bool network_open(int listen = 10);
    int network_send(const char *buf, int size);
    int network_recv(char *buf, int size, int timeout_ms = 0);

    void start();
    void stop_server();
private:
    bool _setup();

    void _service();

private:
    int m_socket;
    int m_listen;
    fd_set m_set;
    std::string m_ip;
    int m_port;
    enum E_network_model m_model;
    struct timeval m_tv;

    std::thread *m_thread;
    std::atomic<bool> m_run;
    std::map<int,struct sockaddr_in> m_client_map;
};
#endif // NTcp_H

NTcp.cpp文件

/*******************************************************************************
 * *@ Author: Noch
 * *@ Description:
 * *@ Date: 2021-10-11 17:04:52
 * *@ LastEditTime: 2021-10-15 15:29:44
 * *@ LastEditors: Noch
 * *@ FilePath: /cli_modbus/NTcp.cpp
 * *@ 三十功名尘与土,八千里路云和月
 *******************************************************************************/
#include "NTcp.h"
#include <cerrno>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <errno.h>
#include <set>
#include "liblog/liblog.h"

const int g_timeout_s = 60*60;
const int g_timeout_us = 0;

NTcp::NTcp(const std::string &ip, int port, E_network_model model)
    : m_ip(ip), m_port(port), m_model(model), m_run(true)
{
}

NTcp::~NTcp()
{
    close(m_socket);
    m_thread->join();
}

bool NTcp::network_open(int listen)
{
    m_listen = listen;
    return _setup();
}

bool NTcp::_setup()
{
    int time = 2;
INITIALIZE_SOCKET:
    close(m_socket);
    struct sockaddr_in p_sockaddr;
    m_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == m_socket)
    {
        //        perror("socket.");
        loge("socket %s", strerror(errno));
        return false;
    }
    memset(&p_sockaddr, 0, sizeof(p_sockaddr));
    p_sockaddr.sin_family = AF_INET;
    p_sockaddr.sin_port = htons(m_port);
    switch (m_model)
    {
    case NETWORK_MODEL_SERVER:
    {
        p_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(m_socket, (struct sockaddr *)&p_sockaddr, sizeof(p_sockaddr)) < 0)
        {
            loge("socket绑定失败");
            return false;
        }
        if (listen(m_socket, m_listen) < 0)
        {
            loge("socket监听失败");
            return false;
        }
        m_thread = new std::thread(&NTcp::_service,this);
        break;
    }
    case NETWORK_MODEL_CLIENT:
    {
        p_sockaddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
        if (connect(m_socket, (struct sockaddr *)&p_sockaddr, sizeof(p_sockaddr)) < 0)
        {
            loge("未与服务器建立连接,将在[%d]秒后进行重连", time);
            std::this_thread::sleep_for(std::chrono::milliseconds(1000 * time));
            time *= 2;
            goto INITIALIZE_SOCKET;
            return false;
        }
        break;
    }
    default:
        break;
    }
    return true;
}

void NTcp::_service()
{
//    int select_fd = m_socket;
    m_tv.tv_sec = g_timeout_s;
    m_tv.tv_usec = g_timeout_us;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    logi("服务端初始化完成,等待客户端连接...");
    std::set<int> select_fd;
    select_fd.insert(m_socket);
    while (m_run.load())
    {
        FD_ZERO(&m_set);
        FD_SET(*select_fd.rbegin(),&m_set);
        //添加监听套接字
        for (std::pair<int,struct sockaddr_in> it : m_client_map)
        {
            FD_SET(it.first,&m_set);
        }
        //监测套接字
        int ret = select(*select_fd.rbegin() + 1, &m_set,NULL,NULL,&m_tv);
        if (0 > ret)
        {
            loge("select:%s",strerror(errno));
            break;
        }
        else if (0 == ret)
        {
            loge("select timeout");
            continue;
        }
        //判断是否有新的连接
        if (FD_ISSET(m_socket,&m_set))
        {
            int client = accept(m_socket,(struct sockaddr*)&client_addr,&client_len);
            if (0 >= client)
            {
                loge("accept:%s",strerror(errno));
                continue;
            }
            if (m_client_map.size() < m_listen)
            {
                m_client_map[client] = client_addr;
                logi("新客户端-[%s:%d],当前连接数-[%d]",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),m_client_map.size());
//                if (client > select_fd)
//                {
//                    select_fd = client;
//                }
                select_fd.insert(client);
            }
            else
            {
                logw("超出服务器最大连接上限[%d],断开客户端[%s:%d]的连接",m_listen,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
                close(client);
                break;
            }
        }

        //判断套接字可读
        for (std::pair<int,struct sockaddr_in> it : m_client_map)
        {
            if (FD_ISSET(it.first,&m_set))
            {
                char buf[256] = {0};
                int ret = recv(it.first,buf,256,MSG_NOSIGNAL);
                if (ret <= 0)
                {
                    logw("读取长度:[%d],客户端[%s:%d]断开连接",ret,inet_ntoa(it.second.sin_addr),ntohs(it.second.sin_port));
                    close(it.first);
                    FD_CLR(it.first,&m_set);
                    select_fd.erase(it.first);
                    m_client_map.erase(it.first);
                }
                else
                {
                    if (ret < 256)
                    {
                        memset(&buf[ret],'\0',1);
                    }
                    logi("客户端[%s:%d]接收数据:[%d]-[%s]",inet_ntoa(it.second.sin_addr),ntohs(it.second.sin_port),ret,buf);
                }
            }
        }
    }
}

int NTcp::network_send(const char *buf, int size)
{
    int len = send(m_socket, buf, size, MSG_NOSIGNAL);
    if (-1 == len)
    {
        _setup();
    }
    return len;
}

int NTcp::network_recv(char *buf, int size, int timeout_ms)
{
    int len = 0;
    fd_set p_select_read;

    struct timeval time;
    time.tv_sec = 0;
    time.tv_usec = timeout_ms * 1000;

    FD_ZERO(&p_select_read);
    FD_SET(m_socket, &p_select_read);
    int p_fs_sel = select(m_socket + 1, &p_select_read, NULL, NULL, &time);
    if (p_fs_sel)
    {
        return recv(m_socket, buf, size, MSG_NOSIGNAL);
        // if (-1 == len)
        // {
        //     // _setup();
        // }
        // return len;
    }

    return -1;
}

void NTcp::start()
{
    while(1)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

void NTcp::stop_server()
{
    m_run = false;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值