1
0

cNetwork: Rewritten server listen into a factory method.

This commit is contained in:
Mattes D 2015-01-15 16:52:06 +01:00
parent 4647e6ad71
commit 646e636933

View File

@ -23,6 +23,8 @@ class cServerHandleImpl;
class cTCPLinkImpl;
typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
@ -161,19 +163,17 @@ class cServerHandleImpl:
friend class cTCPLinkImpl;
public:
/** Creates a new instance with the specified callbacks.
Initializes the internals, but doesn't start listening yet. */
cServerHandleImpl(
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
);
/** Closes the server, dropping all the connections. */
~cServerHandleImpl();
/** Starts listening on the specified port.
Both IPv4 and IPv6 interfaces are used, if possible. */
bool Listen(UInt16 a_Port);
/** Creates a new server instance listening on the specified port.
Both IPv4 and IPv6 interfaces are used, if possible.
Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */
static cServerHandleImplPtr Listen(
UInt16 a_Port,
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
);
// cServerHandle overrides:
virtual void Close(void) override;
@ -201,6 +201,24 @@ protected:
/** Mutex protecting m_Connections againt multithreaded access. */
cCriticalSection m_CS;
/** Contains the error code for the failure to listen. Only valid for non-listening instances. */
int m_ErrorCode;
/** Contains the error message for the failure to listen. Only valid for non-listening instances. */
AString m_ErrorMsg;
/** Creates a new instance with the specified callbacks.
Initializes the internals, but doesn't start listening yet. */
cServerHandleImpl(
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
);
/** Starts listening on the specified port.
Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */
bool Listen(UInt16 a_Port);
/** The callback called by LibEvent upon incoming connection. */
static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self);
@ -209,8 +227,6 @@ protected:
Called by cTCPLinkImpl when the link is terminated. */
void RemoveLink(const cTCPLinkImpl * a_Link);
};
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
@ -230,30 +246,6 @@ public:
// The following functions are implementations for the cNetwork class
/** Queues a TCP connection to be made to the specified host.
Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure.
The a_LinkCallbacks is passed to the newly created cTCPLink.
Returns true if queueing was successful, false on failure to queue.
Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. */
bool Connect(
const AString & a_Host,
const UInt16 a_Port,
cNetwork::cConnectCallbacksPtr a_ConnectCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
);
/** Opens up the specified port for incoming connections.
Calls an OnAccepted callback for each incoming connection.
A cTCPLink with the specified link callbacks is created for each connection.
Returns a cServerHandle that can be used to query the operation status and close the server. */
cServerHandlePtr Listen(
const UInt16 a_Port,
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
);
/** Queues a DNS query to resolve the specified hostname to IP address.
Calls one of the callbacks when the resolving succeeds, or when it fails.
Returns true if queueing was successful, false if not.
@ -321,6 +313,15 @@ protected:
/** Removes the specified link from m_Connections.
Used by the underlying link implementation when the link is closed / errored. */
void RemoveLink(const cTCPLinkImpl * a_Link);
/** Adds the specified link to m_Servers.
Used by the underlying server handle implementation when a new listening server is created.
Only servers that succeed in listening are added. */
void AddServer(cServerHandleImplPtr a_Server);
/** Removes the specified server from m_Servers.
Used by the underlying server handle implementation when the server is closed. */
void RemoveServer(const cServerHandleImpl * a_Server);
};
@ -770,12 +771,16 @@ void cTCPLinkImpl::UpdateRemoteAddress(void)
////////////////////////////////////////////////////////////////////////////////
// cServerHandleImpl:
cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks):
cServerHandleImpl::cServerHandleImpl(
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
):
m_ListenCallbacks(a_ListenCallbacks),
m_LinkCallbacks(a_LinkCallbacks),
m_ConnListener(nullptr),
m_SecondaryConnListener(nullptr),
m_IsListening(false)
m_IsListening(false),
m_ErrorCode(0)
{
}
@ -825,10 +830,26 @@ void cServerHandleImpl::Close(void)
cServerHandleImplPtr cServerHandleImpl::Listen(
UInt16 a_Port,
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
)
{
cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks, a_LinkCallbacks)};
if (res->Listen(a_Port))
{
cNetworkSingleton::Get().AddServer(res);
}
return res;
}
bool cServerHandleImpl::Listen(UInt16 a_Port)
{
ASSERT(!m_IsListening);
// Set up the main socket:
// It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available.
bool NeedsTwoSockets = false;
@ -842,8 +863,8 @@ bool cServerHandleImpl::Listen(UInt16 a_Port)
MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (!IsValidSocket(MainSock))
{
int err = EVUTIL_SOCKET_ERROR();
LOGWARNING("%s: Cannot create a socket for neither IPv6 nor IPv4: %d (%s)", __FUNCTION__, err, evutil_socket_error_to_string(err));
m_ErrorCode = EVUTIL_SOCKET_ERROR();
Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
return false;
}
@ -854,8 +875,8 @@ bool cServerHandleImpl::Listen(UInt16 a_Port)
name.sin_port = ntohs(a_Port);
if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
{
err = EVUTIL_SOCKET_ERROR();
LOGWARNING("Cannot bind to IPv4 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
m_ErrorCode = EVUTIL_SOCKET_ERROR();
Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
evutil_closesocket(MainSock);
return false;
}
@ -884,23 +905,23 @@ bool cServerHandleImpl::Listen(UInt16 a_Port)
name.sin6_port = ntohs(a_Port);
if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0)
{
int err = EVUTIL_SOCKET_ERROR();
LOGWARNING("Cannot bind to IPv6 port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
m_ErrorCode = EVUTIL_SOCKET_ERROR();
Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
evutil_closesocket(MainSock);
return false;
}
}
if (evutil_make_socket_nonblocking(MainSock) != 0)
{
err = EVUTIL_SOCKET_ERROR();
LOGWARNING("Cannot make socket for port %d non-blocking: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
m_ErrorCode = EVUTIL_SOCKET_ERROR();
Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
evutil_closesocket(MainSock);
return false;
}
if (listen(MainSock, 0) != 0)
{
err = EVUTIL_SOCKET_ERROR();
LOGWARNING("Cannot listen on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err));
m_ErrorCode = EVUTIL_SOCKET_ERROR();
Printf(m_ErrorMsg, "Cannot listen on port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode));
evutil_closesocket(MainSock);
return false;
}
@ -985,7 +1006,9 @@ bool cNetwork::Connect(
cTCPLink::cCallbacksPtr a_LinkCallbacks
)
{
return cNetworkSingleton::Get().Connect(a_Host, a_Port, a_ConnectCallbacks, a_LinkCallbacks);
// Add a connection request to the queue:
cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks);
return (Conn != nullptr);
}
@ -998,7 +1021,7 @@ cServerHandlePtr cNetwork::Listen(
cTCPLink::cCallbacksPtr a_LinkCallbacks
)
{
return cNetworkSingleton::Get().Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks);
return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks);
}
@ -1098,42 +1121,6 @@ cNetworkSingleton & cNetworkSingleton::Get(void)
bool cNetworkSingleton::Connect(
const AString & a_Host,
const UInt16 a_Port,
cNetwork::cConnectCallbacksPtr a_ConnectCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
)
{
// Add a connection request to the queue:
cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks);
return (Conn != nullptr);
}
cServerHandlePtr cNetworkSingleton::Listen(
const UInt16 a_Port,
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
cTCPLink::cCallbacksPtr a_LinkCallbacks
)
{
cServerHandleImplPtr res = std::make_shared<cServerHandleImpl>(a_ListenCallbacks, a_LinkCallbacks);
if (!res->Listen(a_Port))
{
return res;
}
cCSLock Lock(m_CS);
m_Servers.push_back(res);
return res;
}
bool cNetworkSingleton::HostnameToIP(
const AString & a_Hostname,
cNetwork::cResolveNameCallbacksPtr a_Callbacks
@ -1263,3 +1250,30 @@ void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link)
void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server)
{
cCSLock Lock(m_CS);
m_Servers.push_back(a_Server);
}
void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server)
{
cCSLock Lock(m_CS);
for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr)
{
if (itr->get() == a_Server)
{
m_Servers.erase(itr);
return;
}
} // for itr - m_Servers[]
}