Implemented LibEvent-based client connections.
This commit is contained in:
parent
a8a7488b54
commit
6f5e267d58
@ -5,6 +5,7 @@ project (MCServer)
|
||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/")
|
||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include")
|
||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include")
|
||||
include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include")
|
||||
|
||||
set(FOLDERS
|
||||
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
|
||||
|
@ -12,6 +12,7 @@ SET (SRCS
|
||||
GZipFile.cpp
|
||||
IsThread.cpp
|
||||
ListenThread.cpp
|
||||
Network.cpp
|
||||
Semaphore.cpp
|
||||
Socket.cpp
|
||||
SocketThreads.cpp
|
||||
@ -26,6 +27,7 @@ SET (HDRS
|
||||
GZipFile.h
|
||||
IsThread.h
|
||||
ListenThread.h
|
||||
Network.h
|
||||
Queue.h
|
||||
Semaphore.h
|
||||
Socket.h
|
||||
|
837
src/OSSupport/Network.cpp
Normal file
837
src/OSSupport/Network.cpp
Normal file
@ -0,0 +1,837 @@
|
||||
|
||||
// Network.cpp
|
||||
|
||||
// Implements the classes used for the Network API
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Network.h"
|
||||
#include <event2/event.h>
|
||||
#include <event2/thread.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include <event2/dns.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Self-test:
|
||||
|
||||
class cNetworkTest
|
||||
{
|
||||
public:
|
||||
/** cTCPLink callbacks that dump everything it received to the log. */
|
||||
class cDumpCallbacks:
|
||||
public cTCPLink::cCallbacks
|
||||
{
|
||||
cEvent & m_Event;
|
||||
|
||||
virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Size) override
|
||||
{
|
||||
// Log the incoming data size:
|
||||
AString Hex;
|
||||
CreateHexDump(Hex, a_Data, a_Size, 16);
|
||||
LOGD("Incoming data: %u bytes:\n%s", static_cast<unsigned>(a_Size), Hex.c_str());
|
||||
}
|
||||
|
||||
virtual void OnRemoteClosed(cTCPLink & a_Link) override
|
||||
{
|
||||
LOGD("Remote has closed the connection.");
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) override
|
||||
{
|
||||
LOGD("Error in the cDumpCallbacks.");
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
public:
|
||||
cDumpCallbacks(cEvent & a_Event):
|
||||
m_Event(a_Event)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** cTCPLink callbacks that echo everything they receive back to the remote peer. */
|
||||
class cEchoCallbacks:
|
||||
public cTCPLink::cCallbacks
|
||||
{
|
||||
cEvent & m_Event;
|
||||
|
||||
virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Size) override
|
||||
{
|
||||
// Echo the incoming data back to outgoing data:
|
||||
LOGD("Data received (%u bytes), echoing back.", static_cast<unsigned>(a_Size));
|
||||
a_Link.Send(a_Data, a_Size);
|
||||
LOGD("Echo queued");
|
||||
}
|
||||
|
||||
virtual void OnRemoteClosed(cTCPLink & a_Link) override
|
||||
{
|
||||
LOGD("Remote has closed the connection.");
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) override
|
||||
{
|
||||
LOGD("Error in the cEchoCallbacks.");
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
public:
|
||||
cEchoCallbacks(cEvent & a_Event):
|
||||
m_Event(a_Event)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Connect callbacks that send a simple test message when connected. */
|
||||
class cConnectCallbacks:
|
||||
public cNetwork::cConnectCallbacks
|
||||
{
|
||||
cEvent & m_Event;
|
||||
|
||||
virtual void OnSuccess(cTCPLink & a_Link) override
|
||||
{
|
||||
LOGD("Connected, sending test message");
|
||||
a_Link.Send("test message");
|
||||
LOGD("Message queued.");
|
||||
}
|
||||
|
||||
virtual void OnError(int a_ErrorCode) override
|
||||
{
|
||||
LOGD("Error while connecting: %d", a_ErrorCode);
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
public:
|
||||
cConnectCallbacks(cEvent & a_Event):
|
||||
m_Event(a_Event)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Connect callbacks that send a HTTP GET request for google.com when connected. */
|
||||
class cHTTPConnectCallbacks:
|
||||
public cNetwork::cConnectCallbacks
|
||||
{
|
||||
cEvent & m_Event;
|
||||
virtual void OnSuccess(cTCPLink & a_Link) override
|
||||
{
|
||||
LOGD("Connected, sending HTTP GET");
|
||||
if (!a_Link.Send("GET / HTTP/1.0\r\nHost:google.com\r\n\r\n"))
|
||||
{
|
||||
LOGWARNING("Sending HTTP GET failed");
|
||||
}
|
||||
LOGD("HTTP GET queued.");
|
||||
}
|
||||
|
||||
virtual void OnError(int a_ErrorCode) override
|
||||
{
|
||||
LOGD("Error while connecting HTTP: %d", a_ErrorCode);
|
||||
m_Event.Set();
|
||||
}
|
||||
|
||||
public:
|
||||
cHTTPConnectCallbacks(cEvent & a_Event):
|
||||
m_Event(a_Event)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Listen callbacks that send a simple welcome message to all connecting peers. */
|
||||
class cListenCallbacks:
|
||||
public cNetwork::cListenCallbacks
|
||||
{
|
||||
virtual void OnAccepted(cTCPLink & a_Link) override
|
||||
{
|
||||
// Send some trivial data:
|
||||
a_Link.Send("Welcome to echo server\n");
|
||||
}
|
||||
};
|
||||
|
||||
cNetworkTest(void)
|
||||
{
|
||||
cEvent evtFinish;
|
||||
|
||||
LOGD("Network test: Connecting to google.com:80, reading front page via HTTP.");
|
||||
if (!cNetwork::Connect("google.com", 80, std::make_shared<cHTTPConnectCallbacks>(evtFinish), std::make_shared<cDumpCallbacks>(evtFinish)))
|
||||
{
|
||||
LOGWARNING("Cannot queue connection to google.com");
|
||||
abort();
|
||||
}
|
||||
LOGD("Connect request has been queued.");
|
||||
|
||||
/*
|
||||
LOGD("Creating a server on port 33033");
|
||||
auto Server = cNetwork::Listen(33033, std::make_shared<cListenCallbacks>(), std::make_shared<cEchoCallbacks>());
|
||||
LOGD("Test server created.");
|
||||
|
||||
LOGD("Connecting to test server");
|
||||
cNetwork::Connect("localhost", 33033, std::make_shared<cConnectCallbacks>(), std::make_shared<cDumpCallbacks>());
|
||||
|
||||
LOGD("Waiting for network operations to finish.");
|
||||
evtFinish.Wait();
|
||||
|
||||
LOGD("Terminating test server.");
|
||||
Server->Close();
|
||||
*/
|
||||
|
||||
evtFinish.Wait();
|
||||
LOGD("Network test finished");
|
||||
}
|
||||
} g_NetworkTest;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Class definitions:
|
||||
|
||||
/** Implements the cTCPLink details so that it can represent the single connection between two endpoints. */
|
||||
class cTCPLinkImpl:
|
||||
public cTCPLink
|
||||
{
|
||||
typedef cTCPLink super;
|
||||
public:
|
||||
/** Creates a new link to be queued to connect to a specified host:port.
|
||||
Used for outgoing connections created using cNetwork::Connect().
|
||||
Call Connect() first, before using the link. */
|
||||
cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks);
|
||||
|
||||
/** Creates a new link based on the given socket.
|
||||
Used for connections accepted in a server using cNetwork::Listen(). */
|
||||
cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks);
|
||||
|
||||
/** Queues a connection request to the specified host.
|
||||
a_ConnectCallbacks must be valid.
|
||||
The object must have been constructed by the right constructor (without the Socket param). */
|
||||
bool Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks);
|
||||
|
||||
// cTCPLink overrides:
|
||||
virtual bool Send(const void * a_Data, size_t a_Length) override;
|
||||
virtual AString GetLocalIP(void) const override;
|
||||
virtual UInt16 GetLocalPort(void) const override;
|
||||
virtual AString GetRemoteIP(void) const override;
|
||||
virtual UInt16 GetRemotePort(void) const override;
|
||||
virtual void Close(void) override;
|
||||
virtual void Drop(void) override;
|
||||
|
||||
protected:
|
||||
|
||||
/** Callbacks to call when the connection is established.
|
||||
May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */
|
||||
cNetwork::cConnectCallbacksPtr m_ConnectCallbacks;
|
||||
|
||||
/** The LibEvent handle representing this connection. */
|
||||
bufferevent * m_BufferEvent;
|
||||
|
||||
|
||||
/** Callback that LibEvent calls when there's data available from the remote peer. */
|
||||
static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self);
|
||||
|
||||
/** Callback that LibEvent calls when there's a non-data-related event on the socket. */
|
||||
static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self);
|
||||
};
|
||||
typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
|
||||
typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Implements the cServerHandle details so that it can represent a real server socket, with a list of clients. */
|
||||
class cServerHandleImpl:
|
||||
public cServerHandle
|
||||
{
|
||||
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
|
||||
);
|
||||
|
||||
/** Starts listening on the specified port.
|
||||
Both IPv4 and IPv6 interfaces are used, if possible. */
|
||||
bool Listen(UInt16 a_Port);
|
||||
|
||||
// cServerHandle overrides:
|
||||
virtual void Close(void) override;
|
||||
virtual bool IsListening(void) const override { return m_IsListening; }
|
||||
|
||||
protected:
|
||||
/** The callbacks used to notify about incoming connections. */
|
||||
cNetwork::cListenCallbacksPtr m_ListenCallbacks;
|
||||
|
||||
/** The callbacks used to create new cTCPLink instances for incoming connections. */
|
||||
cTCPLink::cCallbacksPtr m_LinkCallbacks;
|
||||
|
||||
/** Set to true when the server is initialized successfully and is listening for incoming connections. */
|
||||
bool m_IsListening;
|
||||
};
|
||||
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
|
||||
typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cNetworkSingleton
|
||||
{
|
||||
friend class cTCPLinkImpl;
|
||||
|
||||
public:
|
||||
/** Returns the singleton instance of this class */
|
||||
static cNetworkSingleton & Get(void);
|
||||
|
||||
|
||||
// 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.
|
||||
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */
|
||||
bool HostnameToIP(
|
||||
const AString & a_Hostname,
|
||||
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||
);
|
||||
|
||||
|
||||
/** Queues a DNS query to resolve the specified IP address to a hostname.
|
||||
Calls one of the callbacks when the resolving succeeds, or when it fails.
|
||||
Returns true if queueing was successful, false if not.
|
||||
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */
|
||||
bool IPToHostName(
|
||||
const AString & a_IP,
|
||||
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||
);
|
||||
|
||||
protected:
|
||||
|
||||
/** The main LibEvent container for driving the event loop. */
|
||||
event_base * m_EventBase;
|
||||
|
||||
/** The LibEvent handle for doing DNS lookups. */
|
||||
evdns_base * m_DNSBase;
|
||||
|
||||
/** Container for all client connections, including ones with pending-connect. */
|
||||
cTCPLinkImplPtrs m_Connections;
|
||||
|
||||
/** Container for all servers that are currently active. */
|
||||
cServerHandleImplPtrs m_Servers;
|
||||
|
||||
|
||||
/** Initializes the LibEvent internals. */
|
||||
cNetworkSingleton(void);
|
||||
|
||||
/** Converts LibEvent-generated log events into log messages in MCS log. */
|
||||
static void LogCallback(int a_Severity, const char * a_Msg);
|
||||
|
||||
/** Runs the thread that LibEvent uses to dispatch event. */
|
||||
static void EventLoopThread(cNetworkSingleton * a_Self);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cTCPLinkImpl:
|
||||
|
||||
cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks):
|
||||
super(a_LinkCallbacks),
|
||||
m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, -1, BEV_OPT_CLOSE_ON_FREE))
|
||||
{
|
||||
bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this);
|
||||
bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks):
|
||||
super(a_LinkCallbacks),
|
||||
m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().m_EventBase, a_Socket, BEV_OPT_CLOSE_ON_FREE))
|
||||
{
|
||||
bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this);
|
||||
bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Schedules the actual connection request.
|
||||
Returns true on success, false on failure. */
|
||||
bool cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks)
|
||||
{
|
||||
ASSERT(bufferevent_getfd(m_BufferEvent) == -1); // Did you create this object using the right constructor (the one without the Socket param)?
|
||||
ASSERT(a_ConnectCallbacks != nullptr);
|
||||
|
||||
m_ConnectCallbacks = a_ConnectCallbacks;
|
||||
|
||||
// If a_Host is an IP address, schedule a connection immediately:
|
||||
sockaddr_storage sa;
|
||||
int salen = static_cast<int>(sizeof(sa));
|
||||
if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) == 0)
|
||||
{
|
||||
// Insert the correct port:
|
||||
if (sa.ss_family == AF_INET6)
|
||||
{
|
||||
reinterpret_cast<sockaddr_in6 *>(&sa)->sin6_port = htons(a_Port);
|
||||
}
|
||||
else
|
||||
{
|
||||
reinterpret_cast<sockaddr_in *>(&sa)->sin_port = htons(a_Port);
|
||||
}
|
||||
|
||||
if (bufferevent_socket_connect(m_BufferEvent, reinterpret_cast<sockaddr *>(&sa), salen) == 0)
|
||||
{
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
// Failure
|
||||
return false;
|
||||
}
|
||||
|
||||
// a_Host is a hostname, connect after a lookup:
|
||||
if (bufferevent_socket_connect_hostname(m_BufferEvent, cNetworkSingleton::Get().m_DNSBase, AF_UNSPEC, a_Host.c_str(), a_Port) == 0)
|
||||
{
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
// Failure
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length)
|
||||
{
|
||||
return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cTCPLinkImpl::GetLocalIP(void) const
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"cTCPLinkImpl::GetLocalIP: Not implemented yet");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UInt16 cTCPLinkImpl::GetLocalPort(void) const
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"cTCPLinkImpl::GetLocalPort(): Not implemented yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cTCPLinkImpl::GetRemoteIP(void) const
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"cTCPLinkImpl::GetRemoteIP(): Not implemented yet");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UInt16 cTCPLinkImpl::GetRemotePort(void) const
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"cTCPLinkImpl::GetRemotePort(): Not implemented yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cTCPLinkImpl::Close(void)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"cTCPLinkImpl::Close(): Not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cTCPLinkImpl::Drop(void)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"cTCPLinkImpl::Drop(): Not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self)
|
||||
{
|
||||
ASSERT(a_Self != nullptr);
|
||||
cTCPLinkImpl * Self = static_cast<cTCPLinkImpl *>(a_Self);
|
||||
ASSERT(Self->m_Callbacks != nullptr);
|
||||
|
||||
// Read all the incoming data, in 1024-byte chunks:
|
||||
char data[1024];
|
||||
size_t length;
|
||||
while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0)
|
||||
{
|
||||
Self->m_Callbacks->OnReceivedData(*Self, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self)
|
||||
{
|
||||
ASSERT(a_Self != nullptr);
|
||||
cTCPLinkImpl * Self = static_cast<cTCPLinkImpl *>(a_Self);
|
||||
|
||||
// If an error is reported, call the error callback:
|
||||
if (a_What & BEV_EVENT_ERROR)
|
||||
{
|
||||
// Choose the proper callback to call based on whether we were waiting for connection or not:
|
||||
if (Self->m_ConnectCallbacks != nullptr)
|
||||
{
|
||||
Self->m_ConnectCallbacks->OnError(EVUTIL_SOCKET_ERROR());
|
||||
}
|
||||
else
|
||||
{
|
||||
Self->m_Callbacks->OnError(*Self, EVUTIL_SOCKET_ERROR());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Pending connection succeeded, call the connection callback:
|
||||
if (a_What & BEV_EVENT_CONNECTED)
|
||||
{
|
||||
if (Self->m_ConnectCallbacks != nullptr)
|
||||
{
|
||||
Self->m_ConnectCallbacks->OnSuccess(*Self);
|
||||
// Reset the connect callbacks so that later errors get reported through the link callbacks:
|
||||
Self->m_ConnectCallbacks.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the connectino has been closed, call the link callback:
|
||||
if (a_What & BEV_EVENT_EOF)
|
||||
{
|
||||
Self->m_Callbacks->OnRemoteClosed(*Self);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unknown event, report it:
|
||||
LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What);
|
||||
ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cServerHandleImpl:
|
||||
|
||||
cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks, cTCPLink::cCallbacksPtr a_LinkCallbacks):
|
||||
m_ListenCallbacks(a_ListenCallbacks),
|
||||
m_LinkCallbacks(a_LinkCallbacks)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"Not implemented yet!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cServerHandleImpl::Close(void)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"Not implemented yet!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cServerHandleImpl::Listen(UInt16 a_Port)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"Not implemented yet!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cNetwork:
|
||||
|
||||
bool cNetwork::Connect(
|
||||
const AString & a_Host,
|
||||
const UInt16 a_Port,
|
||||
cNetwork::cConnectCallbacksPtr a_ConnectCallbacks,
|
||||
cTCPLink::cCallbacksPtr a_LinkCallbacks
|
||||
)
|
||||
{
|
||||
return cNetworkSingleton::Get().Connect(a_Host, a_Port, a_ConnectCallbacks, a_LinkCallbacks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cServerHandlePtr cNetwork::Listen(
|
||||
const UInt16 a_Port,
|
||||
cNetwork::cListenCallbacksPtr a_ListenCallbacks,
|
||||
cTCPLink::cCallbacksPtr a_LinkCallbacks
|
||||
)
|
||||
{
|
||||
return cNetworkSingleton::Get().Listen(a_Port, a_ListenCallbacks, a_LinkCallbacks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cNetwork::HostnameToIP(
|
||||
const AString & a_Hostname,
|
||||
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||
)
|
||||
{
|
||||
return cNetworkSingleton::Get().HostnameToIP(a_Hostname, a_Callbacks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cNetwork::IPToHostName(
|
||||
const AString & a_IP,
|
||||
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||
)
|
||||
{
|
||||
return cNetworkSingleton::Get().IPToHostName(a_IP, a_Callbacks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cTCPLink:
|
||||
|
||||
cTCPLink::cTCPLink(cCallbacksPtr a_Callbacks):
|
||||
m_Callbacks(a_Callbacks)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cNetworkSingleton:
|
||||
|
||||
cNetworkSingleton::cNetworkSingleton(void)
|
||||
{
|
||||
// Windows: initialize networking:
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
memset(&wsaData, 0, sizeof(wsaData));
|
||||
WSAStartup (MAKEWORD(2, 2), &wsaData);
|
||||
#endif // _WIN32
|
||||
|
||||
// Initialize LibEvent logging:
|
||||
event_set_log_callback(LogCallback);
|
||||
|
||||
// Initialize threading:
|
||||
#if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
|
||||
evthread_use_windows_threads();
|
||||
#elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
|
||||
evthread_use_pthreads();
|
||||
#else
|
||||
#error No threading implemented for EVTHREAD
|
||||
#endif
|
||||
|
||||
// Create the main event_base:
|
||||
m_EventBase = event_base_new();
|
||||
if (m_EventBase == nullptr)
|
||||
{
|
||||
LOGERROR("Failed to initialize LibEvent. The server will now terminate.");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Create the DNS lookup helper:
|
||||
m_DNSBase = evdns_base_new(m_EventBase, 1);
|
||||
if (m_DNSBase == nullptr)
|
||||
{
|
||||
LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate.");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Create the event loop thread:
|
||||
std::thread::thread(EventLoopThread, this).detach();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cNetworkSingleton & cNetworkSingleton::Get(void)
|
||||
{
|
||||
static cNetworkSingleton Instance;
|
||||
return Instance;
|
||||
}
|
||||
|
||||
|
||||
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 ConnRequest = std::make_shared<cTCPLinkImpl>(a_LinkCallbacks);
|
||||
m_Connections.push_back(ConnRequest);
|
||||
|
||||
// Queue the connection:
|
||||
if (!ConnRequest->Connect(a_Host, a_Port, a_ConnectCallbacks))
|
||||
{
|
||||
// TODO: m_Connections.remove(ConnRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Everything successful, return success:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
m_Servers.push_back(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cNetworkSingleton::HostnameToIP(
|
||||
const AString & a_Hostname,
|
||||
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||
)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"Not implemented yet!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool cNetworkSingleton::IPToHostName(
|
||||
const AString & a_IP,
|
||||
cNetwork::cResolveNameCallbacksPtr a_Callbacks
|
||||
)
|
||||
{
|
||||
// TODO
|
||||
ASSERT(!"Not implemented yet!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg)
|
||||
{
|
||||
switch (a_Severity)
|
||||
{
|
||||
case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break;
|
||||
case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break;
|
||||
case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break;
|
||||
case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break;
|
||||
default:
|
||||
{
|
||||
printf("LibEvent: %s", a_Msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetworkSingleton::EventLoopThread(cNetworkSingleton * a_Self)
|
||||
{
|
||||
event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
188
src/OSSupport/Network.h
Normal file
188
src/OSSupport/Network.h
Normal file
@ -0,0 +1,188 @@
|
||||
|
||||
// Network.h
|
||||
|
||||
// Declares the classes used for the Network API
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface that provides the methods available on a single TCP connection. */
|
||||
class cTCPLink
|
||||
{
|
||||
friend class cNetwork;
|
||||
|
||||
public:
|
||||
class cCallbacks
|
||||
{
|
||||
public:
|
||||
/** Called when there's data incoming from the remote peer. */
|
||||
virtual void OnReceivedData(cTCPLink & a_Link, const char * a_Data, size_t a_Length) = 0;
|
||||
|
||||
/** Called when the remote end closes the connection.
|
||||
The link is still available for connection information query (IP / port).
|
||||
Sending data on the link is not an error, but the data won't be delivered. */
|
||||
virtual void OnRemoteClosed(cTCPLink & a_Link) = 0;
|
||||
|
||||
/** Called when an error is detected on the connection. */
|
||||
virtual void OnError(cTCPLink & a_Link, int a_ErrorCode) = 0;
|
||||
};
|
||||
typedef SharedPtr<cCallbacks> cCallbacksPtr;
|
||||
|
||||
|
||||
/** Queues the specified data for sending to the remote peer.
|
||||
Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */
|
||||
virtual bool Send(const void * a_Data, size_t a_Length) = 0;
|
||||
|
||||
/** Queues the specified data for sending to the remote peer.
|
||||
Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */
|
||||
bool Send(const AString & a_Data)
|
||||
{
|
||||
return Send(a_Data.data(), a_Data.size());
|
||||
}
|
||||
|
||||
/** Returns the IP address of the local endpoint of the connection. */
|
||||
virtual AString GetLocalIP(void) const = 0;
|
||||
|
||||
/** Returns the port used by the local endpoint of the connection. */
|
||||
virtual UInt16 GetLocalPort(void) const = 0;
|
||||
|
||||
/** Returns the IP address of the remote endpoint of the connection. */
|
||||
virtual AString GetRemoteIP(void) const = 0;
|
||||
|
||||
/** Returns the port used by the remote endpoint of the connection. */
|
||||
virtual UInt16 GetRemotePort(void) const = 0;
|
||||
|
||||
/** Closes the link gracefully.
|
||||
The link will keep trying to send the queued data, then it will send the FIN packet. */
|
||||
virtual void Close(void) = 0;
|
||||
|
||||
/** Drops the connection without any more processing.
|
||||
Sends the RST packet, queued outgoing and incoming data is lost. */
|
||||
virtual void Drop(void) = 0;
|
||||
|
||||
protected:
|
||||
/** Callbacks to be used for the various situations. */
|
||||
cCallbacksPtr m_Callbacks;
|
||||
|
||||
|
||||
/** Creates a new link, with the specified callbacks. */
|
||||
cTCPLink(cCallbacksPtr a_Callbacks);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Interface that provides the methods available on a listening server socket. */
|
||||
class cServerHandle
|
||||
{
|
||||
friend class cNetwork;
|
||||
public:
|
||||
|
||||
/** Stops the server, no more incoming connections will be accepted. */
|
||||
virtual void Close(void) = 0;
|
||||
|
||||
/** Returns true if the server has been started correctly and is currently listening for incoming connections. */
|
||||
virtual bool IsListening(void) const = 0;
|
||||
};
|
||||
typedef SharedPtr<cServerHandle> cServerHandlePtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cNetwork
|
||||
{
|
||||
public:
|
||||
/** Callbacks used for connecting to other servers as a client. */
|
||||
class cConnectCallbacks
|
||||
{
|
||||
public:
|
||||
/** Called when the Connect call succeeds.
|
||||
Provides the newly created link that can be used for communication. */
|
||||
virtual void OnSuccess(cTCPLink & a_Link) = 0;
|
||||
|
||||
/** Called when the Connect call fails. */
|
||||
virtual void OnError(int a_ErrorCode) = 0;
|
||||
};
|
||||
typedef SharedPtr<cConnectCallbacks> cConnectCallbacksPtr;
|
||||
|
||||
|
||||
/** Callbacks used when listening for incoming connections as a server. */
|
||||
class cListenCallbacks
|
||||
{
|
||||
public:
|
||||
/** Called when the TCP server created with Listen() accepts an incoming connection.
|
||||
Provides the newly created Link that can be used for communication. */
|
||||
virtual void OnAccepted(cTCPLink & a_Link) = 0;
|
||||
};
|
||||
typedef SharedPtr<cListenCallbacks> cListenCallbacksPtr;
|
||||
|
||||
|
||||
/** Callbacks used when resolving names to IPs. */
|
||||
class cResolveNameCallbacks
|
||||
{
|
||||
public:
|
||||
/** Called when the hostname is successfully resolved into an IP address. */
|
||||
virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0;
|
||||
|
||||
/** Called when an error is encountered while resolving. */
|
||||
virtual void OnError(int a_ErrorCode) = 0;
|
||||
};
|
||||
typedef SharedPtr<cResolveNameCallbacks> cResolveNameCallbacksPtr;
|
||||
|
||||
|
||||
/** 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. */
|
||||
static bool Connect(
|
||||
const AString & a_Host,
|
||||
const UInt16 a_Port,
|
||||
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. */
|
||||
static cServerHandlePtr Listen(
|
||||
const UInt16 a_Port,
|
||||
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.
|
||||
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */
|
||||
static bool HostnameToIP(
|
||||
const AString & a_Hostname,
|
||||
cResolveNameCallbacksPtr a_Callbacks
|
||||
);
|
||||
|
||||
|
||||
/** Queues a DNS query to resolve the specified IP address to a hostname.
|
||||
Calls one of the callbacks when the resolving succeeds, or when it fails.
|
||||
Returns true if queueing was successful, false if not.
|
||||
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. */
|
||||
static bool IPToHostName(
|
||||
const AString & a_IP,
|
||||
cResolveNameCallbacksPtr a_Callbacks
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user