adding multiple listeners into TcpServer
This commit is contained in:
parent
eb2755fa6b
commit
d7bdc63690
@ -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 */
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
@ -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;
|
||||||
return -1;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//socket.setReuseAddr (true);
|
QSE_ASSERT (this->listener.ep_fd >= 0);
|
||||||
//socket.setReusePort (true);
|
::close (this->listener.ep_fd);
|
||||||
|
this->listener.ep_fd = -1;
|
||||||
if (socket.bind(this->binding_address) <= -1)
|
|
||||||
{
|
|
||||||
if (err_code) *err_code = ERR_BIND;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (socket.listen() <= -1)
|
|
||||||
{
|
|
||||||
if (err_code) *err_code = ERR_LISTEN;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//socket.enableTimeout (1000);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(O_CLOEXEC)
|
||||||
|
fcv = ::fcntl(ep_fd, F_GETFD, 0);
|
||||||
|
if (fcv >= 0) ::fcntl(ep_fd, F_SETFD, fcv | O_CLOEXEC);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (::pipe(pfd) <= -1)
|
||||||
|
{
|
||||||
|
this->setErrorCode (syserr_to_errnum(errno));
|
||||||
|
goto oops;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
this->setErrorCode (syserr_to_errnum(errno));
|
||||||
|
goto oops;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 (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);
|
||||||
|
|
||||||
@ -298,11 +403,11 @@ void TcpServer::delete_all_clients () QSE_CPP_NOEXCEPT
|
|||||||
p = np->value;
|
p = np->value;
|
||||||
QSE_ASSERT (p != QSE_NULL);
|
QSE_ASSERT (p != QSE_NULL);
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
while (p->state() == Thread::RUNNING) qse_sleep (300);
|
while (p->state() == Thread::RUNNING) qse_sleep (300);
|
||||||
#else
|
#else
|
||||||
p->join ();
|
p->join ();
|
||||||
#endif
|
#endif
|
||||||
delete p;
|
delete p;
|
||||||
np2 = np; np = np->getNextNode();
|
np2 = np; np = np->getNextNode();
|
||||||
this->client_list.remove (np2);
|
this->client_list.remove (np2);
|
||||||
|
Loading…
Reference in New Issue
Block a user