adding multiple listeners into TcpServer

This commit is contained in:
hyung-hwan 2018-06-26 14:43:43 +00:00
parent eb2755fa6b
commit d7bdc63690
7 changed files with 395 additions and 147 deletions

View File

@ -205,6 +205,30 @@ public:
/** redefines a structure of a character pointer and length */ /** redefines a structure of a character pointer and length */
typedef qse_mcstr_t mcstr_t; typedef qse_mcstr_t mcstr_t;
/** defines common error codes */
enum ErrorCode
{
E_ENOERR, /**< no error */
E_EOTHER, /**< other error */
E_ENOIMPL, /**< not implemented */
E_ESYSERR, /**< subsystem error */
E_EINTERN, /**< internal error */
E_EEXCEPT, /**< exception */
E_ENOMEM,
E_EINVAL,
E_EACCES,
E_EPERM,
E_ENOENT,
E_EEXIST,
E_ENOTDIR,
E_EINTR,
E_EPIPE,
E_EINPROG, /* in progress */
E_EAGAIN /* resource unavailable unavailable */
};
}; };
///////////////////////////////// /////////////////////////////////

View File

@ -40,27 +40,6 @@ QSE_BEGIN_NAMESPACE(QSE)
class Socket: public Uncopyable, public Types class Socket: public Uncopyable, public Types
{ {
public: public:
enum ErrorCode
{
E_ENOERR, /**< no error */
E_EOTHER, /**< other error */
E_ENOIMPL, /**< not implemented */
E_ESYSERR, /**< subsystem error */
E_EINTERN, /**< internal error */
E_ENOMEM,
E_EINVAL,
E_EACCES,
E_EPERM,
E_ENOENT,
E_EEXIST,
E_ENOTDIR,
E_EINTR,
E_EPIPE,
E_EINPROG, /* in progress */
E_EAGAIN /* resource unavailable unavailable */
};
enum Trait enum Trait
{ {
T_NONBLOCK = (1 << 0), T_NONBLOCK = (1 << 0),
@ -76,12 +55,25 @@ public:
int open (int domain, int type, int protocol, int traits = 0) QSE_CPP_NOEXCEPT; int open (int domain, int type, int protocol, int traits = 0) QSE_CPP_NOEXCEPT;
void close () QSE_CPP_NOEXCEPT; void close () QSE_CPP_NOEXCEPT;
qse_sck_hnd_t getHandle() const QSE_CPP_NOEXCEPT { return this->handle; }
int shutdown (int how = 2) QSE_CPP_NOEXCEPT;
int getOption (int level, int optname, void* optval, qse_sck_len_t* optlen) QSE_CPP_NOEXCEPT; int getOption (int level, int optname, void* optval, qse_sck_len_t* optlen) QSE_CPP_NOEXCEPT;
int setOption (int level, int optname, const void* optval, qse_sck_len_t optlen) QSE_CPP_NOEXCEPT; int setOption (int level, int optname, const void* optval, qse_sck_len_t optlen) QSE_CPP_NOEXCEPT;
int setDebug (int n) QSE_CPP_NOEXCEPT;
int setReuseAddr (int n) QSE_CPP_NOEXCEPT;
int setReusePort (int n) QSE_CPP_NOEXCEPT;
int setKeepAlive (int n, int keepidle = 0, int keepintvl = 0, int keepcnt = 0) QSE_CPP_NOEXCEPT;
int setBroadcast (int n) QSE_CPP_NOEXCEPT;
int setSendBuf (unsigned int n) QSE_CPP_NOEXCEPT;
int setRecvBuf (unsigned int n) QSE_CPP_NOEXCEPT;
int setLingerOn (int sec) QSE_CPP_NOEXCEPT;
int setLingerOff () QSE_CPP_NOEXCEPT;
int setTcpNodelay (int n) QSE_CPP_NOEXCEPT;
int setOobInline (int n) QSE_CPP_NOEXCEPT;
int setIpv6Only (int n) QSE_CPP_NOEXCEPT;
int shutdown (int how = 2) QSE_CPP_NOEXCEPT;
int connect (const SocketAddress& target) QSE_CPP_NOEXCEPT; int connect (const SocketAddress& target) QSE_CPP_NOEXCEPT;
int bind (const SocketAddress& target) QSE_CPP_NOEXCEPT; int bind (const SocketAddress& target) QSE_CPP_NOEXCEPT;
int listen (int backlog = 128) QSE_CPP_NOEXCEPT; int listen (int backlog = 128) QSE_CPP_NOEXCEPT;

View File

@ -37,43 +37,46 @@ QSE_BEGIN_NAMESPACE(QSE)
class SocketAddress class SocketAddress
{ {
public: public:
SocketAddress (); SocketAddress () QSE_CPP_NOEXCEPT;
SocketAddress (int family); SocketAddress (int family) QSE_CPP_NOEXCEPT;
SocketAddress (const qse_skad_t* skad); SocketAddress (const qse_skad_t* skad) QSE_CPP_NOEXCEPT;
SocketAddress (const qse_nwad_t* nwad); SocketAddress (const qse_nwad_t* nwad) QSE_CPP_NOEXCEPT;
int getFamily () const; int getFamily () const QSE_CPP_NOEXCEPT;
qse_skad_t* getAddrPtr() qse_skad_t* getAddrPtr() QSE_CPP_NOEXCEPT
{ {
return &this->skad; return &this->skad;
} }
const qse_skad_t* getAddrPtr() const const qse_skad_t* getAddrPtr() const QSE_CPP_NOEXCEPT
{ {
return &this->skad; return &this->skad;
} }
int getAddrSize () const int getAddrSize () const QSE_CPP_NOEXCEPT
{ {
return qse_skadsize(&this->skad); return qse_skadsize(&this->skad);
} }
int getAddrCapa() const int getAddrCapa() const QSE_CPP_NOEXCEPT
{ {
return QSE_SIZEOF(this->skad); return QSE_SIZEOF(this->skad);
} }
void setIpaddr (const qse_ip4ad_t* ipaddr); void setIpaddr (const qse_ip4ad_t* ipaddr) QSE_CPP_NOEXCEPT;
void setIpaddr (const qse_ip6ad_t* ipaddr); void setIpaddr (const qse_ip6ad_t* ipaddr) QSE_CPP_NOEXCEPT;
qse_uint16_t getPort() const; // in network-byte order qse_uint16_t getPort() const QSE_CPP_NOEXCEPT; // in network-byte order
void setPort (qse_uint16_t port); // in network-byte order void setPort (qse_uint16_t port) QSE_CPP_NOEXCEPT; // in network-byte order
int set (const qse_skad_t* skad) QSE_CPP_NOEXCEPT;
int set (const qse_nwad_t* nwad) QSE_CPP_NOEXCEPT;
int set (const qse_mchar_t* str) QSE_CPP_NOEXCEPT;
int set (const qse_wchar_t* str) QSE_CPP_NOEXCEPT;
int set (const qse_mchar_t* str, qse_size_t len) QSE_CPP_NOEXCEPT;
int set (const qse_wchar_t* str, qse_size_t len) QSE_CPP_NOEXCEPT;
int set (const qse_skad_t* skad);
int set (const qse_nwad_t* nwad);
int set (const qse_mchar_t* str);
int set (const qse_wchar_t* str);
protected: protected:
qse_skad_t skad; qse_skad_t skad;

View File

@ -39,11 +39,11 @@ QSE_BEGIN_NAMESPACE(QSE)
// The TcpServer class implements a simple block TCP server that start a thread // The TcpServer class implements a simple block TCP server that start a thread
// for each connection accepted. // for each connection accepted.
class TcpServer: public QSE::Uncopyable class TcpServer: public Uncopyable, public Types
{ {
public: public:
TcpServer (); TcpServer () QSE_CPP_NOEXCEPT;
TcpServer (const SocketAddress& address); TcpServer (const SocketAddress& address) QSE_CPP_NOEXCEPT;
virtual ~TcpServer () QSE_CPP_NOEXCEPT; virtual ~TcpServer () QSE_CPP_NOEXCEPT;
enum enum
@ -55,9 +55,13 @@ public:
ERR_EXCEPTION = 4 ERR_EXCEPTION = 4
}; };
virtual int start (int* err_code = QSE_NULL) QSE_CPP_NOEXCEPT; virtual int start (const qse_mchar_t* addrs) QSE_CPP_NOEXCEPT;
virtual int start (const qse_wchar_t* addrs) QSE_CPP_NOEXCEPT;
virtual int stop () QSE_CPP_NOEXCEPT; virtual int stop () QSE_CPP_NOEXCEPT;
ErrorCode getErrorCode () const QSE_CPP_NOEXCEPT { return this->errcode; }
void setErrorCode (ErrorCode errcode) QSE_CPP_NOEXCEPT { this->errcode = errcode; }
bool isServing () const QSE_CPP_NOEXCEPT bool isServing () const QSE_CPP_NOEXCEPT
{ {
return this->server_serving; return this->server_serving;
@ -114,19 +118,11 @@ public:
this->thread_stack_size = tss; this->thread_stack_size = tss;
} }
bool getReopenSocketUponError () const QSE_CPP_NOEXCEPT
{
return this->reopen_socket_upon_error;
}
void setReopenSocketUponError (bool v) QSE_CPP_NOEXCEPT
{
this->reopen_socket_upon_error = v;
}
protected: protected:
class Listener: public QSE::Socket class Listener: public QSE::Socket
{ {
public:
SocketAddress address;
Listener* next_listener; Listener* next_listener;
}; };
@ -146,15 +142,29 @@ protected:
SocketAddress address; SocketAddress address;
}; };
Listener* listener_head; struct ListenerList
Listener* listener_tail; {
ListenerList(): ep_fd(-1), head(QSE_NULL), tail(QSE_NULL), count(0)
{
this->mux_pipe[0] = -1;
this->mux_pipe[1] = -1;
}
int ep_fd;
int mux_pipe[2];
Listener* head;
Listener* tail;
qse_size_t count;
} listener;
ErrorCode errcode;
SocketAddress binding_address; SocketAddress binding_address;
bool stop_requested; bool stop_requested;
bool server_serving; bool server_serving;
qse_size_t max_connections; qse_size_t max_connections;
qse_size_t thread_stack_size; qse_size_t thread_stack_size;
bool reopen_socket_upon_error;
typedef QSE::LinkedList<Client*> ClientList; typedef QSE::LinkedList<Client*> ClientList;
ClientList client_list; ClientList client_list;
@ -165,7 +175,9 @@ protected:
private: private:
void delete_dead_clients () QSE_CPP_NOEXCEPT; void delete_dead_clients () QSE_CPP_NOEXCEPT;
void delete_all_clients () QSE_CPP_NOEXCEPT; void delete_all_clients () QSE_CPP_NOEXCEPT;
int open_tcp_socket (Socket& socket, int* err_code) QSE_CPP_NOEXCEPT;
int setup_listeners (const qse_char_t* addrs) QSE_CPP_NOEXCEPT;
void free_all_listeners () QSE_CPP_NOEXCEPT;
}; };

View File

@ -126,20 +126,6 @@ void Socket::close () QSE_CPP_NOEXCEPT
} }
} }
int Socket::shutdown (int how) QSE_CPP_NOEXCEPT
{
QSE_ASSERT (this->handle != QSE_INVALID_SCKHND);
if (::shutdown(this->handle, how) == -1)
{
this->setErrorCode (syserr_to_errnum(errno));
return -1;
}
return 0;
}
int Socket::getOption (int level, int optname, void* optval, qse_sck_len_t* optlen) QSE_CPP_NOEXCEPT int Socket::getOption (int level, int optname, void* optval, qse_sck_len_t* optlen) QSE_CPP_NOEXCEPT
{ {
QSE_ASSERT (this->handle != QSE_INVALID_SCKHND); QSE_ASSERT (this->handle != QSE_INVALID_SCKHND);
@ -156,6 +142,118 @@ int Socket::setOption (int level, int optname, const void* optval, qse_sck_len_t
return n; return n;
} }
int Socket::setDebug (int n) QSE_CPP_NOEXCEPT
{
return this->setOption (SOL_SOCKET, SO_DEBUG, (char*)&n, QSE_SIZEOF(n));
};
int Socket::setReuseAddr (int n) QSE_CPP_NOEXCEPT
{
return this->setOption (SOL_SOCKET, SO_REUSEADDR, (char*)&n, QSE_SIZEOF(n));
}
int Socket::setReusePort (int n) QSE_CPP_NOEXCEPT
{
#if defined(SO_REUSEPORT)
return this->setOption (SOL_SOCKET, SO_REUSEPORT, (char*)&n, QSE_SIZEOF(n));
#else
this->setErrorCode (E_ENOIMPL);
return -1;
#endif
}
int Socket::setKeepAlive (int n, int keepidle, int keepintvl, int keepcnt) QSE_CPP_NOEXCEPT
{
if (this->setOption (SOL_SOCKET, SO_KEEPALIVE, (char*)&n, QSE_SIZEOF(n)) <= -1) return -1;
// the following values are just hints.
// i don't care about success and failure
#if defined(TCP_KEEPIDLE) && defined(SOL_TCP)
if (keepidle > 0) this->setOption (SOL_TCP, TCP_KEEPIDLE, (char*)&keepidle, QSE_SIZEOF(keepidle));
#endif
#if defined(TCP_KEEPINTVL) && defined(SOL_TCP)
if (keepintvl > 0) this->setOption (SOL_TCP, TCP_KEEPINTVL, (char*)&keepintvl, QSE_SIZEOF(keepintvl));
#endif
#if defined(TCP_KEEPCNT) && defined(SOL_TCP)
if (keepcnt > 0) this->setOption (SOL_TCP, TCP_KEEPCNT, (char*)&keepcnt, QSE_SIZEOF(keepcnt));
#endif
return 0;
}
int Socket::setBroadcast (int n) QSE_CPP_NOEXCEPT
{
return this->setOption (SOL_SOCKET, SO_BROADCAST, (char*)&n, QSE_SIZEOF(n));
}
int Socket::setSendBuf (unsigned int size) QSE_CPP_NOEXCEPT
{
return this->setOption (SOL_SOCKET, SO_SNDBUF, (char*)&size, QSE_SIZEOF(size));
}
int Socket::setRecvBuf (unsigned int size) QSE_CPP_NOEXCEPT
{
return this->setOption (SOL_SOCKET, SO_RCVBUF, (char*)&size, QSE_SIZEOF(size));
}
int Socket::setLingerOn (int sec) QSE_CPP_NOEXCEPT
{
struct linger lng;
lng.l_onoff = 1;
lng.l_linger = sec;
return this->setOption (SOL_SOCKET, SO_LINGER, (char*)&lng, QSE_SIZEOF(lng));
}
int Socket::setLingerOff () QSE_CPP_NOEXCEPT
{
struct linger lng;
lng.l_onoff = 0;
lng.l_linger = 0;
return this->setOption (SOL_SOCKET, SO_LINGER, (char*)&lng, QSE_SIZEOF(lng));
}
int Socket::setTcpNodelay (int n) QSE_CPP_NOEXCEPT
{
#if defined(TCP_NODELAY)
return this->setOption (IPPROTO_TCP, TCP_NODELAY, (char*)&n, QSE_SIZEOF(n));
#else
this->setErrorCode (E_ENOIMPL);
return -1;
#endif
}
int Socket::setOobInline (int n) QSE_CPP_NOEXCEPT
{
#if defined(SO_OOBINLINE)
return this->setOption (SOL_SOCKET, SO_OOBINLINE, (char*)&n, QSE_SIZEOF(n));
#else
this->setErrorCode (E_ENOIMPL);
return -1;
#endif
}
int Socket::setIpv6Only (int n) QSE_CPP_NOEXCEPT
{
#if defined(IPV6_V6ONLY)
return this->setOption (IPPROTO_IPV6, IPV6_V6ONLY, (char*)&n, QSE_SIZEOF(n));
#else
this->setErrorCode (E_ENOIMPL);
return -1;
#endif
}
int Socket::shutdown (int how) QSE_CPP_NOEXCEPT
{
QSE_ASSERT (this->handle != QSE_INVALID_SCKHND);
if (::shutdown(this->handle, how) == -1)
{
this->setErrorCode (syserr_to_errnum(errno));
return -1;
}
return 0;
}
int Socket::connect (const SocketAddress& target) QSE_CPP_NOEXCEPT int Socket::connect (const SocketAddress& target) QSE_CPP_NOEXCEPT
{ {
QSE_ASSERT (this->handle != QSE_INVALID_SCKHND); QSE_ASSERT (this->handle != QSE_INVALID_SCKHND);

View File

@ -90,34 +90,34 @@ QSE_BEGIN_NAMESPACE(QSE)
///////////////////////////////// /////////////////////////////////
SocketAddress::SocketAddress () SocketAddress::SocketAddress () QSE_CPP_NOEXCEPT
{ {
QSE_MEMSET (&this->skad, 0, QSE_SIZEOF(this->skad)); QSE_MEMSET (&this->skad, 0, QSE_SIZEOF(this->skad));
} }
SocketAddress::SocketAddress (int family) SocketAddress::SocketAddress (int family) QSE_CPP_NOEXCEPT
{ {
QSE_MEMSET (&this->skad, 0, QSE_SIZEOF(this->skad)); QSE_MEMSET (&this->skad, 0, QSE_SIZEOF(this->skad));
FAMILY(&this->skad) = family; FAMILY(&this->skad) = family;
} }
SocketAddress::SocketAddress (const qse_skad_t* skad) SocketAddress::SocketAddress (const qse_skad_t* skad) QSE_CPP_NOEXCEPT
{ {
this->set (skad); this->set (skad);
} }
SocketAddress::SocketAddress (const qse_nwad_t* nwad) SocketAddress::SocketAddress (const qse_nwad_t* nwad) QSE_CPP_NOEXCEPT
{ {
this->set (nwad); this->set (nwad);
} }
int SocketAddress::getFamily () const int SocketAddress::getFamily () const QSE_CPP_NOEXCEPT
{ {
return FAMILY(&this->skad); return FAMILY(&this->skad);
//return qse_skadfamily (&this->skad); //return qse_skadfamily (&this->skad);
} }
void SocketAddress::setIpaddr (const qse_ip4ad_t* ipaddr) void SocketAddress::setIpaddr (const qse_ip4ad_t* ipaddr) QSE_CPP_NOEXCEPT
{ {
#if defined(AF_INET) #if defined(AF_INET)
if (FAMILY(&this->skad) == AF_INET) if (FAMILY(&this->skad) == AF_INET)
@ -128,7 +128,7 @@ void SocketAddress::setIpaddr (const qse_ip4ad_t* ipaddr)
#endif #endif
} }
void SocketAddress::setIpaddr (const qse_ip6ad_t* ipaddr) void SocketAddress::setIpaddr (const qse_ip6ad_t* ipaddr) QSE_CPP_NOEXCEPT
{ {
#if defined(AF_INET6) #if defined(AF_INET6)
if (FAMILY(&this->skad) == AF_INET6) if (FAMILY(&this->skad) == AF_INET6)
@ -139,7 +139,7 @@ void SocketAddress::setIpaddr (const qse_ip6ad_t* ipaddr)
#endif #endif
} }
qse_uint16_t SocketAddress::getPort () const qse_uint16_t SocketAddress::getPort () const QSE_CPP_NOEXCEPT
{ {
switch (FAMILY(&this->skad)) switch (FAMILY(&this->skad))
{ {
@ -163,7 +163,7 @@ qse_uint16_t SocketAddress::getPort () const
return 0; return 0;
} }
void SocketAddress::setPort (qse_uint16_t port) void SocketAddress::setPort (qse_uint16_t port) QSE_CPP_NOEXCEPT
{ {
switch (FAMILY(&this->skad)) switch (FAMILY(&this->skad))
{ {
@ -187,32 +187,46 @@ void SocketAddress::setPort (qse_uint16_t port)
} }
} }
int SocketAddress::set (const qse_skad_t* skad) int SocketAddress::set (const qse_skad_t* skad) QSE_CPP_NOEXCEPT
{ {
this->skad = *skad; this->skad = *skad;
return 0; return 0;
} }
int SocketAddress::set (const qse_nwad_t* nwad) int SocketAddress::set (const qse_nwad_t* nwad) QSE_CPP_NOEXCEPT
{ {
return qse_nwadtoskad(nwad, &this->skad); return qse_nwadtoskad(nwad, &this->skad);
} }
int SocketAddress::set (const qse_mchar_t* str) int SocketAddress::set (const qse_mchar_t* str) QSE_CPP_NOEXCEPT
{ {
qse_nwad_t nwad; qse_nwad_t nwad;
if (qse_mbstonwad(str, &nwad) <= -1) return -1; if (qse_mbstonwad(str, &nwad) <= -1) return -1;
return qse_nwadtoskad(&nwad, &this->skad); return qse_nwadtoskad(&nwad, &this->skad);
} }
int SocketAddress::set (const qse_wchar_t* str) int SocketAddress::set (const qse_wchar_t* str) QSE_CPP_NOEXCEPT
{ {
qse_nwad_t nwad; qse_nwad_t nwad;
if (qse_wcstonwad(str, &nwad) <= -1) return -1; if (qse_wcstonwad(str, &nwad) <= -1) return -1;
return qse_nwadtoskad(&nwad, &this->skad); return qse_nwadtoskad(&nwad, &this->skad);
} }
int SocketAddress::set (const qse_mchar_t* str, qse_size_t len) QSE_CPP_NOEXCEPT
{
qse_nwad_t nwad;
if (qse_mbsntonwad(str, len, &nwad) <= -1) return -1;
return qse_nwadtoskad(&nwad, &this->skad);
}
int SocketAddress::set (const qse_wchar_t* str, qse_size_t len) QSE_CPP_NOEXCEPT
{
qse_nwad_t nwad;
if (qse_wcsntonwad(str, len, &nwad) <= -1) return -1;
return qse_nwadtoskad(&nwad, &this->skad);
}
///////////////////////////////// /////////////////////////////////
QSE_END_NAMESPACE(QSE) QSE_END_NAMESPACE(QSE)
///////////////////////////////// /////////////////////////////////

View File

@ -26,9 +26,20 @@
#include <qse/si/TcpServer.hpp> #include <qse/si/TcpServer.hpp>
#include <qse/si/os.h> #include <qse/si/os.h>
#include <qse/cmn/str.h>
#include "../cmn/mem-prv.h"
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
QSE_BEGIN_NAMESPACE(QSE) QSE_BEGIN_NAMESPACE(QSE)
#include "../cmn/syserr.h"
IMPLEMENT_SYSERR_TO_ERRNUM (TcpServer::ErrorCode, TcpServer::)
TcpServer::Client::Client (TcpServer* server) TcpServer::Client::Client (TcpServer* server)
{ {
this->server = server; this->server = server;
@ -75,22 +86,21 @@ int TcpServer::Client::stop () QSE_CPP_NOEXCEPT
return 0; return 0;
} }
TcpServer::TcpServer (): TcpServer::TcpServer () QSE_CPP_NOEXCEPT:
errcode(E_ENOERR),
stop_requested(false), stop_requested(false),
server_serving(false), server_serving(false),
max_connections(0), max_connections(0),
thread_stack_size (0), thread_stack_size (0)
reopen_socket_upon_error(false)
{ {
} }
TcpServer::TcpServer (const SocketAddress& address): TcpServer::TcpServer (const SocketAddress& address) QSE_CPP_NOEXCEPT:
binding_address(address), binding_address(address),
stop_requested(false), stop_requested(false),
server_serving(false), server_serving(false),
max_connections(0), max_connections(0),
thread_stack_size (0), thread_stack_size (0)
reopen_socket_upon_error(false)
{ {
} }
@ -100,38 +110,159 @@ TcpServer::~TcpServer () QSE_CPP_NOEXCEPT
this->delete_all_clients (); this->delete_all_clients ();
} }
int TcpServer::open_tcp_socket (Socket& socket, int* err_code) QSE_CPP_NOEXCEPT void TcpServer::free_all_listeners () QSE_CPP_NOEXCEPT
{ {
if (socket.open(this->binding_address.getFamily(), QSE_SOCK_STREAM, Socket::T_CLOEXEC | Socket::T_NONBLOCK) <= -1) Listener* lp;
struct epoll_event dummy_ev;
::epoll_ctl (this->listener.ep_fd, EPOLL_CTL_DEL, this->listener.mux_pipe[0], &dummy_ev);
while (this->listener.head)
{ {
if (err_code) *err_code = ERR_OPEN; lp = this->listener.head;
this->listener.head = lp->next_listener;
this->listener.count--;
::epoll_ctl (this->listener.ep_fd, EPOLL_CTL_DEL, lp->getHandle(), &dummy_ev);
lp->close ();
delete lp;
}
QSE_ASSERT (this->listener.ep_fd >= 0);
::close (this->listener.ep_fd);
this->listener.ep_fd = -1;
}
int TcpServer::setup_listeners (const qse_char_t* addrs) QSE_CPP_NOEXCEPT
{
const qse_char_t* addr_ptr, * comma;
int ep_fd = -1, fcv;
struct epoll_event ev;
int pfd[2] = { -1, - 1 };
SocketAddress sockaddr;
ep_fd = ::epoll_create(1024);
if (ep_fd <= -1)
{
this->setErrorCode (syserr_to_errnum(errno));
return -1; return -1;
} }
//socket.setReuseAddr (true); #if defined(O_CLOEXEC)
//socket.setReusePort (true); fcv = ::fcntl(ep_fd, F_GETFD, 0);
if (fcv >= 0) ::fcntl(ep_fd, F_SETFD, fcv | O_CLOEXEC);
#endif
if (socket.bind(this->binding_address) <= -1) if (::pipe(pfd) <= -1)
{ {
if (err_code) *err_code = ERR_BIND; this->setErrorCode (syserr_to_errnum(errno));
return -1; goto oops;
} }
if (socket.listen() <= -1) #if defined(O_CLOEXEC)
fcv = ::fcntl(pfd[0], F_GETFD, 0);
if (fcv >= 0) ::fcntl(pfd[0], F_SETFD, fcv | O_CLOEXEC);
fcv = ::fcntl(pfd[1], F_GETFD, 0);
if (fcv >= 0) ::fcntl(pfd[1], F_SETFD, fcv | O_CLOEXEC);
#endif
#if defined(O_NONBLOCK)
fcv = ::fcntl(pfd[0], F_GETFL, 0);
if (fcv >= 0) ::fcntl(pfd[0], F_SETFL, fcv | O_NONBLOCK);
fcv = ::fcntl(pfd[1], F_GETFL, 0);
if (fcv >= 0) ::fcntl(pfd[1], F_SETFL, fcv | O_NONBLOCK);
#endif
QSE_MEMSET (&ev, 0, QSE_SIZEOF(ev));
ev.events = EPOLLIN | EPOLLHUP | EPOLLERR;
ev.data.fd = pfd[0];
if (::epoll_ctl(ep_fd, EPOLL_CTL_ADD, pfd[0], &ev) <= -1)
{ {
if (err_code) *err_code = ERR_LISTEN; this->setErrorCode (syserr_to_errnum(errno));
return -1; goto oops;
} }
//socket.enableTimeout (1000); addr_ptr = addrs;
while (1)
{
qse_size_t addr_len;
Listener* lsck;
Socket sock;
comma = qse_strchr(addr_ptr, QSE_T(','));
addr_len = comma? comma - addr_ptr: qse_strlen(addr_ptr);
/* [NOTE] no whitespaces are allowed before and after a comma */
if (sockaddr.set(addr_ptr, addr_len) <= -1)
{
/* TOOD: logging */
goto next_segment;
}
try
{
lsck = new Listener();
}
catch (...)
{
/* TODO: logging... */
goto next_segment;
}
if (lsck->open(sockaddr.getFamily(), QSE_SOCK_STREAM, Socket::T_CLOEXEC | Socket::T_NONBLOCK) <= -1)
{
/* TODO: logging */
goto next_segment;
}
lsck->setReuseAddr (1);
lsck->setReusePort (1);
if (lsck->bind(sockaddr) <= -1 || lsck->listen() <= -1)
{
/* TODO: logging */
lsck->close ();
goto next_segment;
}
QSE_MEMSET (&ev, 0, QSE_SIZEOF(ev));
ev.events = EPOLLIN | EPOLLHUP | EPOLLERR;
ev.data.fd = lsck->getHandle();
if (::epoll_ctl(ep_fd, EPOLL_CTL_ADD, lsck->getHandle(), &ev) <= -1)
{
/* TODO: logging */
lsck->close ();
goto next_segment;
}
lsck->address = sockaddr;
lsck->next_listener = this->listener.head;
this->listener.head = lsck;
this->listener.count++;
next_segment:
if (!comma) break;
addr_ptr = comma + 1;
}
if (!this->listener.head) goto oops;
this->listener.ep_fd = ep_fd;
this->listener.mux_pipe[0] = pfd[0];
this->listener.mux_pipe[1] = pfd[1];
return 0; return 0;
oops:
if (this->listener.head) this->free_all_listeners ();
if (pfd[0] >= 0) close (pfd[0]);
if (pfd[1] >= 0) close (pfd[1]);
if (ep_fd >= 0) close (ep_fd);
return -1;
} }
int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT int TcpServer::start (const qse_char_t* addrs) QSE_CPP_NOEXCEPT
{ {
this->server_serving = true; this->server_serving = true;
if (err_code != QSE_NULL) *err_code = ERR_NONE;
this->setStopRequested (false); this->setStopRequested (false);
Client* client = QSE_NULL; Client* client = QSE_NULL;
@ -140,7 +271,7 @@ int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT
{ {
Socket socket; Socket socket;
if (this->open_tcp_socket(socket, err_code) <= -1) if (this->setup_listeners(addrs) <= -1)
{ {
this->server_serving = false; this->server_serving = false;
this->setStopRequested (false); this->setStopRequested (false);
@ -183,33 +314,7 @@ int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT
// don't "delete client" here as i want it to be reused // don't "delete client" here as i want it to be reused
// in the next iteration after "continue" // in the next iteration after "continue"
/* TODO: logging */
if (this->reopen_socket_upon_error)
{
// closing the listeing socket causes the
// the pending connection to be dropped.
// this poses the risk of reopening failure.
// imagine the case of EMFILE(too many open files).
// accept() will fail until an open file is closed.
qse_size_t reopen_count = 0;
socket.close ();
reopen:
if (this->open_tcp_socket (socket, err_code) <= -1)
{
if (reopen_count >= 100)
{
qse_ntime_t interval;
if (reopen_count >= 200) qse_inittime (&interval, 0, 100000000); // 100 milliseconds
else qse_inittime (&interval, 0, 10000000); // 10 milliseconds
qse_sleep (&interval);
}
if (this->isStopRequested()) break;
reopen_count++;
goto reopen;
}
}
continue; continue;
} }
@ -237,7 +342,7 @@ int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT
this->delete_all_clients (); this->delete_all_clients ();
if (client != QSE_NULL) delete client; if (client != QSE_NULL) delete client;
if (err_code) *err_code = ERR_EXCEPTION; this->setErrorCode (E_EEXCEPT);
this->server_serving = false; this->server_serving = false;
this->setStopRequested (false); this->setStopRequested (false);