//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
最终效果展示: