C++封装select模型实现多路IO复用并发服务器

//select_tcp_server.h

#ifndef __select_tcp_server__
#define __select_tcp_server__

#include <iostream>
#include <functional>
#include <memory>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <string.h>

#include <thread>
#include <string>
#include <future>
#include <string>
#include <vector>

class select_tcp_server
{
public:
    select_tcp_server();

    ~select_tcp_server();

    const char* get_error() const;

    bool create_server(const std::string& addr, int port);

    void start_server();

    void stop_server();

    std::string get_server_addr() const;

    int get_server_port() const;

    int send(int id,const void* data,size_t size);

    std::string get_peer_addr(int id) const;

    int get_peer_port(int id) const;

    std::string get_peer_name(int id) const;

    std::string get_online_time(int id) const;

    std::string get_offline_time(int id) const;

    std::vector<std::string> get_online_list() const;

    std::function<void(int id)> on_connect = nullptr;

    std::function<void(int id,void* data,size_t size)> on_recv = nullptr;

    std::function<void(int id,const void* data,size_t size)> on_send = nullptr;

    std::function<void(int id)> on_close = nullptr;

    std::function<void(int id,int error)> on_error = nullptr;

protected:
    void set_error(const char* fmt,...);

    static void select_thread(select_tcp_server* arg);

    std::string format_time() const;
private:
    std::string m_error;

    std::string m_addr;

    std::future<void> m_future;

    bool m_quit = false;

    int m_port = 0;

    int m_fd = -1;

    struct peer_data
    {
        std::string addr;
        int port;
        int fd;

        std::unique_ptr<char> data;
        static const size_t size = 4096;

        std::string online_time;
        std::string offline_time;

        peer_data()
            :port(0),
            fd(-1),
            data(new char[size])
        {

        }

        ~peer_data()
        {

        }

    }m_peer[FD_SETSIZE];
};
#endif// __select_tcp_server__
//select_tcp_server.cpp

#include "select_tcp_server.h"
#include <asm-generic/socket.h>
#include <assert.h>
#include <chrono>
#include <cstdio>
#include <future>
#include <sys/select.h>
#include <sys/socket.h>
#include <thread>
#include <stdarg.h>
#include <vector>

select_tcp_server::select_tcp_server()
{

}

select_tcp_server::~select_tcp_server()
{
    stop_server();
}

const char* select_tcp_server::get_error() const
{
    return m_error.c_str();
}

bool select_tcp_server::create_server(const std::string& addr,int port)
{
    bool result = false;
    do{
        m_addr = addr;
        m_port = port;

        m_fd = socket(AF_INET,SOCK_STREAM,0);
        if (m_fd == -1)
        {
            set_error("create socket failure,errno %d",errno);
            break;
        }

        int v = 1;
        setsockopt(m_fd,SOL_SOCKET,SO_REUSEADDR,&v,sizeof(v));

        sockaddr_in addr = {0};
        addr.sin_addr.s_addr = inet_addr(m_addr.c_str());
        addr.sin_port = htons(m_port);
        addr.sin_family = AF_INET;

        if (bind(m_fd,(const sockaddr*)&addr,sizeof(addr)) == -1)
        {
            set_error("bind failure,errno %d",errno);
            break;
        }

        if (listen(m_fd,10) == -1)
        {
            set_error("listen failure,errno %d",errno);
            break;
        }

        result = true;
    }while(false);
    return result;
}

void select_tcp_server::start_server()
{
    if (!m_future.valid())
    {
        m_quit = false;
        m_future = std::async(select_thread,this);
        printf("server[%s:%d] start\n",m_addr.c_str(),m_port);
    }
}

void select_tcp_server::stop_server()
{
    if (m_future.valid())
    {
        m_quit = true;
        m_future.wait();
        m_future.get();
        printf("server[%s:%d] stop\n",m_addr.c_str(),m_port);
    }
}

int select_tcp_server::send(int id,const void* data,size_t size)
{
    if (id < 0 || id > FD_SETSIZE - 1)
        return -1;

    if (on_send)
        on_send(id,data,size);
    return ::send(m_peer[id].fd,data,size,0);
}

std::string select_tcp_server::get_server_addr() const
{
    return m_addr;
}

int select_tcp_server::get_server_port() const
{
    return m_port;
}

std::string select_tcp_server::get_peer_addr(int id) const
{
    if (id < 0 || id > FD_SETSIZE - 1)
        return std::string();
    return m_peer[id].addr;
}

int select_tcp_server::get_peer_port(int id) const
{
    if (id < 0 || id > FD_SETSIZE - 1)
        return -1;
    return m_peer[id].port;
}

std::string select_tcp_server::get_peer_name(int id) const
{
    if (id < 0 || id > FD_SETSIZE - 1)
        return std::string();
    auto&& addr = get_peer_addr(id);
    auto&& port = std::to_string(get_peer_port(id));
    return std::string(addr + ":" + port).c_str();
}

std::string select_tcp_server::get_online_time(int id) const
{
    if (id < 0 || id > FD_SETSIZE - 1)
        return std::string();
    return m_peer[id].online_time;
}

std::string select_tcp_server::get_offline_time(int id) const
{
    if (id < 0 || id > FD_SETSIZE - 1)
        return std::string();
    return m_peer[id].offline_time;
}

std::vector<std::string> select_tcp_server::get_online_list() const
{
    std::vector<std::string> list;
    for(int i = 0;i < FD_SETSIZE;++i)
    {
        if (m_peer[i].fd != -1)
        {
            list.push_back(get_peer_name(i));
        }
    }
    return list;
}

void select_tcp_server::set_error(const char* fmt,...)
{
    char buf[1024]={0};
    va_list ap;
    va_start(ap,fmt);
    vsnprintf(buf,sizeof(buf),fmt,ap);
    va_end(ap);
    m_error = buf;
}

void select_tcp_server::select_thread(select_tcp_server* arg)
{
    auto peer = arg->m_peer;
    int min_fd = arg->m_fd;
    int max_fd = min_fd;
    int max_i = -1;

    fd_set rset,rtset;
    FD_ZERO(&rset);
    FD_SET(max_fd,&rset);

    timeval tv = {0};

    while(1)
    {
        tv.tv_sec = 1;
        rtset = rset;

        int ready = ::select(max_fd + 1,&rtset,nullptr,nullptr,&tv);
        if (ready == 0)
        {
            if (arg->m_quit)
            {
                printf("server thread %d exit\n",gettid());

                for(int i = 0;i < FD_SETSIZE;++i)
                {
                    if (peer[i].fd != -1)
                    {
                        close(peer[i].fd);
                        peer[i].fd = -1;
                    }
                }
                close(min_fd);
                break;
            }
            continue;
        }
        else if (ready == -1)
        {
            break;
        }
        else
        {
            if (FD_ISSET(min_fd,&rtset))
            {
                sockaddr_in addr;
                socklen_t len = sizeof(addr);
                int fd = ::accept(min_fd,(sockaddr*)&addr,&len);
                if (fd != -1)
                {
                    int i = 0;
                    for(;i < FD_SETSIZE;++i)
                    {
                        if (peer[i].fd == -1)
                        {
                            peer[i].fd = fd;
                            peer[i].addr = inet_ntoa(addr.sin_addr);
                            peer[i].port = ntohs(addr.sin_port);
                            peer[i].online_time = arg->format_time();
                            /* printf("[%s:%d] online\n",peer[i].addr.c_str(),peer[i].port); */
                            break;
                        }
                    }

                    if (i == FD_SETSIZE - 1)
                    {
                        printf("over maximum limits %d\n",FD_SETSIZE);
                        close(fd);
                    }
                    else
                    {
                        if (i > max_i)
                        {
                            max_i = i;
                        }

                        if (fd > max_fd)
                        {
                            max_fd = fd;
                        }

                        FD_SET(fd,&rset);

                        if (arg->on_connect)
                            arg->on_connect(i);
                    }

                }

                if (!--ready)
                    continue;
            }

            for(int i = 0;i < max_i + 1;i++)
            {
                if (FD_ISSET(peer[i].fd,&rtset))
                {
                    memset(peer[i].data.get(),0,peer[i].size);
                    int n = recv(peer[i].fd,peer[i].data.get(),peer[i].size,0); 
                    if (n == -1)
                    {
                        if (arg->on_error)
                            arg->on_error(i,errno);

                        close(peer[i].fd);
                        FD_CLR(peer[i].fd,&rset);
                        peer[i].fd = -1;
                    }
                    else if (n == 0)
                    {
                        close(peer[i].fd);
                        FD_CLR(peer[i].fd,&rset);
                        peer[i].fd = -1;
                        peer[i].offline_time = arg->format_time();

                        if (arg->on_close)
                            arg->on_close(i);
                    }
                    else
                    {
                        if (arg->on_recv)
                            arg->on_recv(i,peer[i].data.get(),n);
                    }

                    if (!--ready)
                        break;
                }

            }
        }
    }
}

std::string select_tcp_server::format_time() const
{
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() -
        std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t time = std::chrono::system_clock::to_time_t(now);
    tm* t = localtime(&time);
    char buf[256]{};
    sprintf(buf,"%04d-%02d-%02d %02d:%02d:%02d.%03d",
            t->tm_year + 1900,
            t->tm_mon + 1,
            t->tm_mday,
            t->tm_hour,
            t->tm_min,
            t->tm_sec,
            (int)ms);
    return std::string(buf);
}
//main.cpp

#include "select_tcp_server.h"

int main()
{
    select_tcp_server s;
    s.create_server("0.0.0.0",10086);
    s.start_server();

    s.on_connect = [&s](int id){
        printf("[%s] %s online\n",s.get_peer_name(id).c_str(),s.get_online_time(id).c_str());
    };

    s.on_recv = [&s](int id,void* data,size_t size){
        printf("[%s] < %s\n",s.get_peer_name(id).c_str(),(const char*)data);
        s.send(id,data,size);
    };

    s.on_send = [&s](int id,const void* data,size_t size){
        printf("[%s] > %s\n",s.get_peer_name(id).c_str(),(const char*)data);
    };

    s.on_close = [&s](int id){
        printf("[%s] %s offline\n",s.get_peer_name(id).c_str(),s.get_offline_time(id).c_str());
    };

    s.on_error = [&s](int id,int error){
        printf("[%s] error,error number %d\n",s.get_peer_name(id).c_str(),error);
    };

    while(getchar() != '\n');
    s.stop_server();
    return 0;
}

编译:g++ select_tcp_server.h select_tcp_server.cpp main.cpp -lpthread -o select_tcp_server.out

最终效果展示:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值