23 return (a_Socket != INVALID_SOCKET);
25 return (a_Socket >= 0);
38 m_ListenCallbacks(
std::move(a_ListenCallbacks)),
39 m_ConnListener(nullptr),
40 m_SecondaryConnListener(nullptr),
85 for (
const auto & conn: Conns)
107 res->m_SelfPtr = res;
108 if (res->Listen(a_Port))
114 res->m_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg);
115 res->m_SelfPtr.reset();
131 bool NeedsTwoSockets =
false;
133 evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
138 err = EVUTIL_SOCKET_ERROR();
139 LOGD(
"Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err));
140 MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
144 m_ErrorMsg = fmt::format(FMT_STRING(
"Cannot create a server socket for port {}: {} ({})"),
151 if (evutil_make_listen_socket_reuseable(MainSock) != 0)
154 m_ErrorMsg = fmt::format(FMT_STRING(
"Port {} cannot be made reusable: {} ({}). Restarting the server might not work."),
162 memset(&name, 0,
sizeof(name));
163 name.sin_family = AF_INET;
164 name.sin_port = ntohs(a_Port);
165 if (bind(MainSock,
reinterpret_cast<const sockaddr *
>(&name),
sizeof(name)) != 0)
168 m_ErrorMsg = fmt::format(FMT_STRING(
"Cannot bind IPv4 socket to port {}: {} ({})"),
171 evutil_closesocket(MainSock);
181 int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char *
>(&Zero),
sizeof(Zero));
182 err = EVUTIL_SOCKET_ERROR();
183 NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT));
185 setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char *
>(&Zero),
sizeof(Zero));
189 if (evutil_make_listen_socket_reuseable(MainSock) != 0)
192 m_ErrorMsg = fmt::format(FMT_STRING(
"Port {} cannot be made reusable: {} ({}). Restarting the server might not work."),
200 memset(&name, 0,
sizeof(name));
201 name.sin6_family = AF_INET6;
202 name.sin6_port = ntohs(a_Port);
203 if (bind(MainSock,
reinterpret_cast<const sockaddr *
>(&name),
sizeof(name)) != 0)
207 evutil_closesocket(MainSock);
211 if (evutil_make_socket_nonblocking(MainSock) != 0)
214 m_ErrorMsg = fmt::format(FMT_STRING(
"Cannot make socket on port {} non-blocking: {} ({})"), a_Port,
m_ErrorCode, evutil_socket_error_to_string(
m_ErrorCode));
215 evutil_closesocket(MainSock);
218 if (listen(MainSock, SOMAXCONN) != 0)
222 evutil_closesocket(MainSock);
228 if (!NeedsTwoSockets)
234 LOGD(
"Creating a second socket for IPv4");
235 evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
239 err = EVUTIL_SOCKET_ERROR();
240 LOGD(
"socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
245 if (evutil_make_listen_socket_reuseable(SecondSock) != 0)
248 m_ErrorMsg = fmt::format(FMT_STRING(
"Port {} cannot be made reusable (second socket): {} ({}). Restarting the server might not work."),
255 if (evutil_make_socket_nonblocking(SecondSock) != 0)
257 err = EVUTIL_SOCKET_ERROR();
258 LOGD(
"evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err));
259 evutil_closesocket(SecondSock);
265 memset(&name, 0,
sizeof(name));
266 name.sin_family = AF_INET;
267 name.sin_port = ntohs(a_Port);
268 if (bind(SecondSock,
reinterpret_cast<const sockaddr *
>(&name),
sizeof(name)) != 0)
270 err = EVUTIL_SOCKET_ERROR();
271 LOGD(
"Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
272 evutil_closesocket(SecondSock);
276 if (listen(SecondSock, SOMAXCONN) != 0)
278 err = EVUTIL_SOCKET_ERROR();
279 LOGD(
"Cannot listen on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
280 evutil_closesocket(SecondSock);
304 switch (a_Addr->sa_family)
308 sockaddr_in * sin =
reinterpret_cast<sockaddr_in *
>(a_Addr);
309 evutil_inet_ntop(AF_INET, &(sin->sin_addr), IPAddress,
ARRAYCOUNT(IPAddress));
310 Port = ntohs(sin->sin_port);
315 sockaddr_in6 * sin6 =
reinterpret_cast<sockaddr_in6 *
>(a_Addr);
316 evutil_inet_ntop(AF_INET6, &(sin6->sin6_addr), IPAddress,
ARRAYCOUNT(IPAddress));
317 Port = ntohs(sin6->sin6_port);
324 if (LinkCallbacks ==
nullptr)
327 evutil_closesocket(a_Socket);
332 setsockopt(a_Socket, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const char *
>(&one),
sizeof(one));
335 cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->
m_SelfPtr, a_Addr,
static_cast<socklen_t
>(a_Len));
340 LinkCallbacks->OnLinkCreated(Link);
356 if (itr->get() == a_Link)
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
void LOG(std::string_view a_Format, const Args &... args)
std::shared_ptr< cServerHandle > cServerHandlePtr
std::shared_ptr< cTCPLinkImpl > cTCPLinkImplPtr
std::vector< cTCPLinkImplPtr > cTCPLinkImplPtrs
std::shared_ptr< cServerHandleImpl > cServerHandleImplPtr
static bool IsValidSocket(evutil_socket_t a_Socket)
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
std::shared_ptr< cCallbacks > cCallbacksPtr
static cServerHandlePtr Listen(UInt16 a_Port, cListenCallbacksPtr a_ListenCallbacks)
Opens up the specified port for incoming connections.
std::shared_ptr< cListenCallbacks > cListenCallbacksPtr
static cNetworkSingleton & Get(void)
Returns the singleton instance of this class.
void AddServer(const cServerHandlePtr &a_Server)
Adds the specified link to m_Servers.
void RemoveServer(const cServerHandle *a_Server)
Removes the specified server from m_Servers.
cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks)
Creates a new instance with the specified callbacks.
evconnlistener * m_ConnListener
The LibEvent handle representing the main listening socket.
static void Callback(evconnlistener *a_Listener, evutil_socket_t a_Socket, sockaddr *a_Addr, int a_Len, void *a_Self)
The callback called by LibEvent upon incoming connection.
evconnlistener * m_SecondaryConnListener
The LibEvent handle representing the secondary listening socket (only when side-by-side listening is ...
AString m_ErrorMsg
Contains the error message for the failure to listen.
cServerHandleImplPtr m_SelfPtr
The SharedPtr to self, so that it can be passed to created links.
cTCPLinkImplPtrs m_Connections
Container for all currently active connections on this server.
cCriticalSection m_CS
Mutex protecting m_Connections againt multithreaded access.
virtual void Close(void) override
Stops the server, no more incoming connections will be accepted.
static cServerHandleImplPtr Listen(UInt16 a_Port, cNetwork::cListenCallbacksPtr a_ListenCallbacks)
Creates a new server instance listening on the specified port.
virtual ~cServerHandleImpl() override
Closes the server, dropping all the connections.
bool m_IsListening
Set to true when the server is initialized successfully and is listening for incoming connections.
int m_ErrorCode
Contains the error code for the failure to listen.
cNetwork::cListenCallbacksPtr m_ListenCallbacks
The callbacks used to notify about incoming connections.
void RemoveLink(const cTCPLinkImpl *a_Link)
Removes the specified link from m_Connections.