adding multiple listeners into TcpServer
This commit is contained in:
@ -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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
SocketAddress::SocketAddress (int family)
|
||||
SocketAddress::SocketAddress (int family) QSE_CPP_NOEXCEPT
|
||||
{
|
||||
QSE_MEMSET (&this->skad, 0, QSE_SIZEOF(this->skad));
|
||||
FAMILY(&this->skad) = family;
|
||||
}
|
||||
|
||||
SocketAddress::SocketAddress (const qse_skad_t* skad)
|
||||
SocketAddress::SocketAddress (const qse_skad_t* skad) QSE_CPP_NOEXCEPT
|
||||
{
|
||||
this->set (skad);
|
||||
}
|
||||
|
||||
SocketAddress::SocketAddress (const qse_nwad_t* nwad)
|
||||
SocketAddress::SocketAddress (const qse_nwad_t* nwad) QSE_CPP_NOEXCEPT
|
||||
{
|
||||
this->set (nwad);
|
||||
}
|
||||
|
||||
int SocketAddress::getFamily () const
|
||||
int SocketAddress::getFamily () const QSE_CPP_NOEXCEPT
|
||||
{
|
||||
return FAMILY(&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 (FAMILY(&this->skad) == AF_INET)
|
||||
@ -128,7 +128,7 @@ void SocketAddress::setIpaddr (const qse_ip4ad_t* ipaddr)
|
||||
#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 (FAMILY(&this->skad) == AF_INET6)
|
||||
@ -139,7 +139,7 @@ void SocketAddress::setIpaddr (const qse_ip6ad_t* ipaddr)
|
||||
#endif
|
||||
}
|
||||
|
||||
qse_uint16_t SocketAddress::getPort () const
|
||||
qse_uint16_t SocketAddress::getPort () const QSE_CPP_NOEXCEPT
|
||||
{
|
||||
switch (FAMILY(&this->skad))
|
||||
{
|
||||
@ -163,7 +163,7 @@ qse_uint16_t SocketAddress::getPort () const
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SocketAddress::setPort (qse_uint16_t port)
|
||||
void SocketAddress::setPort (qse_uint16_t port) QSE_CPP_NOEXCEPT
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int SocketAddress::set (const qse_mchar_t* str)
|
||||
int SocketAddress::set (const qse_mchar_t* str) QSE_CPP_NOEXCEPT
|
||||
{
|
||||
qse_nwad_t nwad;
|
||||
if (qse_mbstonwad(str, &nwad) <= -1) return -1;
|
||||
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;
|
||||
if (qse_wcstonwad(str, &nwad) <= -1) return -1;
|
||||
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)
|
||||
/////////////////////////////////
|
||||
|
@ -26,9 +26,20 @@
|
||||
|
||||
#include <qse/si/TcpServer.hpp>
|
||||
#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)
|
||||
|
||||
|
||||
#include "../cmn/syserr.h"
|
||||
IMPLEMENT_SYSERR_TO_ERRNUM (TcpServer::ErrorCode, TcpServer::)
|
||||
|
||||
TcpServer::Client::Client (TcpServer* server)
|
||||
{
|
||||
this->server = server;
|
||||
@ -75,22 +86,21 @@ int TcpServer::Client::stop () QSE_CPP_NOEXCEPT
|
||||
return 0;
|
||||
}
|
||||
|
||||
TcpServer::TcpServer ():
|
||||
TcpServer::TcpServer () QSE_CPP_NOEXCEPT:
|
||||
errcode(E_ENOERR),
|
||||
stop_requested(false),
|
||||
server_serving(false),
|
||||
max_connections(0),
|
||||
thread_stack_size (0),
|
||||
reopen_socket_upon_error(false)
|
||||
thread_stack_size (0)
|
||||
{
|
||||
}
|
||||
|
||||
TcpServer::TcpServer (const SocketAddress& address):
|
||||
TcpServer::TcpServer (const SocketAddress& address) QSE_CPP_NOEXCEPT:
|
||||
binding_address(address),
|
||||
stop_requested(false),
|
||||
server_serving(false),
|
||||
max_connections(0),
|
||||
thread_stack_size (0),
|
||||
reopen_socket_upon_error(false)
|
||||
thread_stack_size (0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -100,38 +110,159 @@ TcpServer::~TcpServer () QSE_CPP_NOEXCEPT
|
||||
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;
|
||||
return -1;
|
||||
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;
|
||||
}
|
||||
|
||||
//socket.setReuseAddr (true);
|
||||
//socket.setReusePort (true);
|
||||
|
||||
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;
|
||||
QSE_ASSERT (this->listener.ep_fd >= 0);
|
||||
::close (this->listener.ep_fd);
|
||||
this->listener.ep_fd = -1;
|
||||
}
|
||||
|
||||
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;
|
||||
if (err_code != QSE_NULL) *err_code = ERR_NONE;
|
||||
|
||||
this->setStopRequested (false);
|
||||
|
||||
Client* client = QSE_NULL;
|
||||
@ -140,7 +271,7 @@ int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT
|
||||
{
|
||||
Socket socket;
|
||||
|
||||
if (this->open_tcp_socket(socket, err_code) <= -1)
|
||||
if (this->setup_listeners(addrs) <= -1)
|
||||
{
|
||||
this->server_serving = 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
|
||||
// in the next iteration after "continue"
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* TODO: logging */
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -237,7 +342,7 @@ int TcpServer::start (int* err_code) QSE_CPP_NOEXCEPT
|
||||
this->delete_all_clients ();
|
||||
if (client != QSE_NULL) delete client;
|
||||
|
||||
if (err_code) *err_code = ERR_EXCEPTION;
|
||||
this->setErrorCode (E_EEXCEPT);
|
||||
this->server_serving = false;
|
||||
this->setStopRequested (false);
|
||||
|
||||
@ -298,11 +403,11 @@ void TcpServer::delete_all_clients () QSE_CPP_NOEXCEPT
|
||||
p = np->value;
|
||||
QSE_ASSERT (p != QSE_NULL);
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(_WIN32)
|
||||
while (p->state() == Thread::RUNNING) qse_sleep (300);
|
||||
#else
|
||||
#else
|
||||
p->join ();
|
||||
#endif
|
||||
#endif
|
||||
delete p;
|
||||
np2 = np; np = np->getNextNode();
|
||||
this->client_list.remove (np2);
|
||||
|
Reference in New Issue
Block a user