PoC: C++ ASIO

This commit is contained in:
Tiger Wang 2020-05-20 19:59:46 +01:00
parent 99f8c44342
commit 072fdf3488
15 changed files with 89 additions and 107 deletions

3
.gitmodules vendored
View File

@ -66,3 +66,6 @@
[submodule "Tools/BlockTypePaletteGenerator/lib/lunajson"]
path = Tools/BlockTypePaletteGenerator/lib/lunajson
url = https://github.com/grafi-tt/lunajson.git
[submodule "lib/asio"]
path = lib/asio
url = https://github.com/chriskohlhoff/asio

View File

@ -49,6 +49,7 @@ function(link_dependencies TARGET)
# Add required includes:
target_include_directories(
${TARGET} SYSTEM PRIVATE
lib/asio/asio/include
lib/mbedtls/include
lib/TCLAP/include
lib # TODO fix files including zlib/x instead of x

1
lib/asio Submodule

@ -0,0 +1 @@
Subproject commit efff0de89920eb66afead00dfd8bb8cf588ccee4

View File

@ -169,7 +169,7 @@ static int tolua_cNetwork_HostnameToIP(lua_State * L)
ASSERT(callbacks != nullptr); // Invalid callbacks would have resulted in GetStackValues() returning false
// Try to look up:
bool res = cNetwork::HostnameToIP(host, std::make_shared<cLuaNameLookup>(host, std::move(callbacks)));
bool res = cNetwork::HostnameToIP(host, std::make_unique<cLuaNameLookup>(host, std::move(callbacks)));
S.Push(res);
return 1;
}
@ -205,7 +205,7 @@ static int tolua_cNetwork_IPToHostname(lua_State * L)
ASSERT(callbacks != nullptr); // Invalid callbacks would have resulted in GetStackValues() returning false
// Try to look up:
bool res = cNetwork::IPToHostName(ip, std::make_shared<cLuaNameLookup>(ip, std::move(callbacks)));
bool res = cNetwork::IPToHostName(ip, std::make_unique<cLuaNameLookup>(ip, std::move(callbacks)));
S.Push(res);
return 1;
}

View File

@ -57,7 +57,7 @@
#include <stdlib.h>
#include <crtdbg.h>
#define DEBUG_CLIENTBLOCK new(_CLIENT_BLOCK, __FILE__, __LINE__)
#define new DEBUG_CLIENTBLOCK
// #define new DEBUG_CLIENTBLOCK
// For some reason this works magically - each "new X" gets replaced as "new(_CLIENT_BLOCK, "file", line) X"
// The CRT has a definition for this operator new that stores the debugging info for leak-finding later.
#endif

View File

@ -15,104 +15,85 @@
////////////////////////////////////////////////////////////////////////////////
// cHostnameLookup:
cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks):
m_Callbacks(std::move(a_Callbacks)),
m_Hostname(a_Hostname)
void cHostnameLookup::Lookup(
const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks)
{
// Note the Lookup object is owned solely by this lambda which is destroyed
// after it runs
cNetworkSingleton::Get().GetLookupThread().async_resolve(
a_Hostname, "",
[Callbacks = std::move(a_Callbacks)](const auto & a_Error, const auto & a_Results)
{
// If an error has occurred, notify the error callback:
if (a_Error)
{
Callbacks->OnError(a_Error.value(), a_Error.message());
return;
}
Callback(*Callbacks.get(), a_Results);
}
);
}
void cHostnameLookup::Lookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks)
void cHostnameLookup::Callback(cNetwork::cResolveNameCallbacks & a_Callbacks, const asio::ip::tcp::resolver::results_type & a_Addr)
{
// Cannot use std::make_shared here, constructor is not accessible
cHostnameLookupPtr Lookup{ new cHostnameLookup(a_Hostname, std::move(a_Callbacks)) };
// Note the Lookup object is owned solely by this lambda which is destroyed after it runs
cNetworkSingleton::Get().GetLookupThread().ScheduleLookup([=]()
{
// Start the lookup:
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_CANONNAME;
addrinfo * Result;
int ErrCode = getaddrinfo(Lookup->m_Hostname.c_str(), nullptr, &hints, &Result);
Lookup->Callback(ErrCode, Result);
});
}
void cHostnameLookup::Callback(int a_ErrCode, addrinfo * a_Addr)
{
// If an error has occurred, notify the error callback:
if (a_ErrCode != 0)
{
m_Callbacks->OnError(a_ErrCode, ErrorString(a_ErrCode));
return;
}
// Call the success handler for each entry received:
bool HasResolved = false;
addrinfo * OrigAddr = a_Addr;
for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next)
for (const auto & Addr : a_Addr)
{
char IP[128];
switch (a_Addr->ai_family)
const auto & Endpoint = Addr.endpoint();
const auto & Address = Endpoint.address();
const auto & Hostname = Addr.host_name();
if (Address.is_v4())
{
case AF_INET: // IPv4
const auto sin =
reinterpret_cast<const sockaddr_in *>(Endpoint.data());
if (!a_Callbacks.OnNameResolvedV4(Hostname, sin))
{
sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr->ai_addr);
if (!m_Callbacks->OnNameResolvedV4(m_Hostname, sin))
{
// Callback indicated that the IP shouldn't be serialized to a string, just continue with the next address:
HasResolved = true;
continue;
}
evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP));
break;
}
case AF_INET6: // IPv6
{
sockaddr_in6 * sin = reinterpret_cast<sockaddr_in6 *>(a_Addr->ai_addr);
if (!m_Callbacks->OnNameResolvedV6(m_Hostname, sin))
{
// Callback indicated that the IP shouldn't be serialized to a string, just continue with the next address:
HasResolved = true;
continue;
}
evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP));
break;
}
default:
{
// Unknown address family, handle as if this entry wasn't received
continue; // for (a_Addr)
// Callback indicated that the IP shouldn't be serialized to
// a string, just continue with the next address:
HasResolved = true;
continue;
}
}
m_Callbacks->OnNameResolved(m_Hostname, IP);
else if (Address.is_v6())
{
const auto sin =
reinterpret_cast<const sockaddr_in6 *>(Endpoint.data());
if (!a_Callbacks.OnNameResolvedV6(Hostname, sin))
{
// Callback indicated that the IP shouldn't be serialized to
// a string, just continue with the next address:
HasResolved = true;
continue;
}
}
else
{
// Unknown address family, handle as if this entry wasn't
// received
continue; // for (a_Addr)
}
a_Callbacks.OnNameResolved(Hostname, Address.to_string());
HasResolved = true;
} // for (a_Addr)
// If only unsupported families were reported, call the Error handler:
if (!HasResolved)
{
m_Callbacks->OnError(EAI_NONAME, ErrorString(EAI_NONAME));
a_Callbacks.OnError(EAI_NONAME, ErrorString(EAI_NONAME));
}
else
{
m_Callbacks->OnFinished();
a_Callbacks.OnFinished();
}
freeaddrinfo(OrigAddr);
}

View File

@ -12,6 +12,7 @@
#pragma once
#include "Network.h"
#include <asio/ip/tcp.hpp>
@ -24,23 +25,7 @@ public:
/** Creates a lookup object and schedules the lookup. */
static void Lookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks);
protected:
private:
/** Creates the lookup object. Doesn't start the lookup yet. */
cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks);
/** The callbacks to call for resolved names / errors. */
cNetwork::cResolveNameCallbacksPtr m_Callbacks;
/** The hostname that was queried (needed for the callbacks). */
AString m_Hostname;
void Callback(int a_ErrCode, struct addrinfo * a_Addr);
static void Callback(cNetwork::cResolveNameCallbacks & a_Callbacks, const asio::ip::tcp::resolver::results_type & a_Addr);
};
typedef std::shared_ptr<cHostnameLookup> cHostnameLookupPtr;
typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;

View File

@ -32,7 +32,7 @@ void cIPLookup::Lookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr
cIPLookupPtr Lookup{ new cIPLookup(a_IP, std::move(a_Callbacks)) }; // Cannot use std::make_shared here, constructor is not accessible
// Note the Lookup object is owned solely by this lambda which is destroyed after it runs
cNetworkSingleton::Get().GetLookupThread().ScheduleLookup([=]()
/* cNetworkSingleton::Get().GetLookupThread().ScheduleLookup( */[=]()
{
sockaddr_storage sa;
int salen = sizeof(sa);
@ -58,7 +58,7 @@ void cIPLookup::Lookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr
0
);
Lookup->Callback(ErrCode, Hostname);
});
}();
}

View File

@ -297,7 +297,7 @@ public:
Only called if there was no error reported. */
virtual void OnFinished(void) = 0;
};
typedef std::shared_ptr<cResolveNameCallbacks> cResolveNameCallbacksPtr;
typedef std::unique_ptr<cResolveNameCallbacks> cResolveNameCallbacksPtr;
/** Queues a TCP connection to be made to the specified host.

View File

@ -16,7 +16,8 @@
cNetworkSingleton::cNetworkSingleton() :
m_HasTerminated(true)
m_HasTerminated(true),
m_Resolver(m_Context)
{
}
@ -47,7 +48,9 @@ cNetworkSingleton & cNetworkSingleton::Get(void)
void cNetworkSingleton::Initialise(void)
{
// Start the lookup thread
m_LookupThread.Start();
m_Context.restart();
m_Context.get_executor().on_work_started();
m_LookupThread = std::thread([this] { m_Context.run(); });
// Windows: initialize networking:
#ifdef _WIN32
@ -100,7 +103,8 @@ void cNetworkSingleton::Terminate(void)
ASSERT(!m_HasTerminated);
// Wait for the lookup thread to stop
m_LookupThread.Stop();
m_Context.get_executor().on_work_finished();
m_LookupThread.join();
// Wait for the LibEvent event loop to terminate:
event_base_loopbreak(m_EventBase);

View File

@ -14,6 +14,8 @@
#pragma once
#include <event2/event.h>
#include <asio/ip/tcp.hpp>
#include <asio/io_context.hpp>
#include "NetworkLookup.h"
#include "CriticalSection.h"
#include "Event.h"
@ -57,7 +59,7 @@ public:
event_base * GetEventBase(void) { return m_EventBase; }
/** Returns the thread used to perform hostname and IP lookups */
cNetworkLookup & GetLookupThread() { return m_LookupThread; }
asio::ip::tcp::resolver & GetLookupThread() { return m_Resolver; }
/** Adds the specified link to m_Connections.
Used by the underlying link implementation when a new link is created. */
@ -100,7 +102,11 @@ protected:
cEvent m_StartupEvent;
/** The thread on which hostname and ip address lookup is performed. */
cNetworkLookup m_LookupThread;
std::thread m_LookupThread;
asio::io_context m_Context;
asio::ip::tcp::resolver m_Resolver;
/** Converts LibEvent-generated log events into log messages in MCS log. */

View File

@ -138,7 +138,7 @@ cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTC
};
// Schedule the host query
cNetwork::HostnameToIP(a_Host, std::make_shared<cHostnameCallback>(res, a_Port));
cNetwork::HostnameToIP(a_Host, std::make_unique<cHostnameCallback>(res, a_Port));
return res;
}

View File

@ -270,8 +270,7 @@ bool cUDPEndpointImpl::Send(const AString & a_Payload, const AString & a_Host, U
if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0)
{
// a_Host is a hostname, we need to do a lookup first:
auto queue = std::make_shared<cUDPSendAfterLookup>(a_Payload, a_Port, m_MainSock, m_SecondarySock, m_IsMainSockIPv6);
return cNetwork::HostnameToIP(a_Host, queue);
return cNetwork::HostnameToIP(a_Host, std::make_unique<cUDPSendAfterLookup>(a_Payload, a_Port, m_MainSock, m_SecondarySock, m_IsMainSockIPv6));
}
// a_Host is an IP address and has been parsed into "sa"

View File

@ -55,6 +55,8 @@ add_library(Network
${Network_HDRS}
)
target_include_directories(Network SYSTEM PUBLIC ${CMAKE_SOURCE_DIR}/lib/asio/asio/include)
target_link_libraries(Network event_core event_extra fmt::fmt mbedtls)
if(NOT WIN32)
target_link_libraries(Network event_pthreads Threads::Threads)

View File

@ -52,7 +52,7 @@ static void DoTest(void)
// Look up google.com (has multiple IP addresses):
LOGD("Network test: Looking up google.com");
if (!cNetwork::HostnameToIP("google.com", std::make_shared<cFinishLookupCallbacks>(evtFinish)))
if (!cNetwork::HostnameToIP("google.com", std::make_unique<cFinishLookupCallbacks>(evtFinish)))
{
LOGWARNING("Cannot resolve google.com to IP");
abort();
@ -63,7 +63,7 @@ static void DoTest(void)
// Look up 8.8.8.8 (Google free DNS):
LOGD("Network test: Looking up IP 8.8.8.8");
if (!cNetwork::IPToHostName("8.8.8.8", std::make_shared<cFinishLookupCallbacks>(evtFinish)))
if (!cNetwork::IPToHostName("8.8.8.8", std::make_unique<cFinishLookupCallbacks>(evtFinish)))
{
LOGWARNING("Cannot resolve 8.8.8.8 to name");
abort();