refactor NetworkAddress

This commit is contained in:
Ted John 2016-05-28 16:45:09 +01:00
parent 2334e701e4
commit 46ecd53a99
6 changed files with 248 additions and 104 deletions

View File

@ -83,6 +83,7 @@
<ClCompile Include="src\management\research.c" />
<ClCompile Include="src\network\http.cpp" />
<ClCompile Include="src\network\network.cpp" />
<ClCompile Include="src\network\NetworkAddress.cpp" />
<ClCompile Include="src\network\NetworkKey.cpp" />
<ClCompile Include="src\network\NetworkUser.cpp" />
<ClCompile Include="src\network\twitch.cpp" />
@ -358,6 +359,8 @@
<ClInclude Include="src\management\news_item.h" />
<ClInclude Include="src\management\research.h" />
<ClInclude Include="src\network\http.h" />
<ClInclude Include="src\network\NetworkAddress.h" />
<ClInclude Include="src\network\NetworkTypes.h" />
<ClInclude Include="src\network\NetworkUser.h" />
<ClInclude Include="src\network\twitch.h" />
<ClInclude Include="src\network\network.h" />
@ -480,6 +483,7 @@
<TreatSpecificWarningsAsErrors>4013</TreatSpecificWarningsAsErrors>
<MinimalRebuild>false</MinimalRebuild>
<TreatWarningAsError>true</TreatWarningAsError>
<BrowseInformation>true</BrowseInformation>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -487,6 +491,9 @@
<LinkTimeCodeGeneration>UseFastLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<AdditionalOptions>/OPT:NOLBR /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
<Bscmake>
<PreserveSbr>true</PreserveSbr>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>

View File

@ -0,0 +1,123 @@
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include <string>
#include <sdl/SDL_thread.h>
#include "NetworkAddress.h"
NetworkAddress::NetworkAddress()
{
_result = std::make_shared<ResolveResult>();
_result->status = RESOLVE_NONE;
_resolveMutex = SDL_CreateMutex();
}
NetworkAddress::~NetworkAddress()
{
SDL_DestroyMutex(_resolveMutex);
}
void NetworkAddress::Resolve(const char * host, uint16 port)
{
SDL_LockMutex(_resolveMutex);
{
// Create a new result store
_result = std::make_shared<ResolveResult>();
_result->status = RESOLVE_INPROGRESS;
// Create a new request
auto req = new ResolveRequest();
req->Host = std::string(host == nullptr ? "" : host);
req->Port = port;;
req->Result = _result;
// Resolve synchronously
ResolveWorker(req);
}
SDL_UnlockMutex(_resolveMutex);
}
void NetworkAddress::ResolveAsync(const char * host, uint16 port)
{
SDL_LockMutex(_resolveMutex);
{
// Create a new result store
_result = std::make_shared<ResolveResult>();
_result->status = RESOLVE_INPROGRESS;
// Create a new request
auto req = new ResolveRequest();
req->Host = std::string(host);
req->Port = port;
req->Result = _result;
// Spin off a worker thread for resolving the address
SDL_CreateThread([](void * pointer) -> int
{
ResolveWorker((ResolveRequest *)pointer);
return 0;
}, 0, req);
}
SDL_UnlockMutex(_resolveMutex);
}
NetworkAddress::RESOLVE_STATUS NetworkAddress::GetResult(sockaddr_storage * ss, int * ss_len)
{
SDL_LockMutex(_resolveMutex);
{
const ResolveResult * result = _result.get();
if (result->status == RESOLVE_OK)
{
*ss = result->ss;
*ss_len = result->ss_len;
}
return result->status;
}
SDL_UnlockMutex(_resolveMutex);
}
void NetworkAddress::ResolveWorker(ResolveRequest * req)
{
// Resolve the address
const char * nodeName = req->Host.c_str();
std::string serviceName = std::to_string(req->Port);
addrinfo hints = { 0 };
hints.ai_family = AF_UNSPEC;
if (req->Host.empty())
{
hints.ai_flags = AI_PASSIVE;
nodeName = nullptr;
}
addrinfo * result;
getaddrinfo(nodeName, serviceName.c_str(), &hints, &result);
// Store the result
ResolveResult * resolveResult = req->Result.get();
if (result != nullptr)
{
resolveResult->status = RESOLVE_OK;
memcpy(&resolveResult->ss, result->ai_addr, result->ai_addrlen);
resolveResult->ss_len = result->ai_addrlen;
freeaddrinfo(result);
}
else
{
resolveResult->status = RESOLVE_FAILED;
}
delete req;
}

View File

@ -0,0 +1,71 @@
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#pragma once
#include <memory>
#include <sdl/SDL_mutex.h>
#include "NetworkTypes.h"
#include "../common.h"
class NetworkAddress final
{
public:
enum RESOLVE_STATUS
{
RESOLVE_NONE,
RESOLVE_INPROGRESS,
RESOLVE_OK,
RESOLVE_FAILED
};
NetworkAddress();
~NetworkAddress();
void Resolve(const char * host, uint16 port);
void ResolveAsync(const char * host, uint16 port);
RESOLVE_STATUS GetResult(sockaddr_storage * ss, int * ss_len);
private:
struct ResolveResult
{
RESOLVE_STATUS status;
sockaddr_storage ss;
int ss_len;
};
struct ResolveRequest
{
std::string Host;
uint16 Port;
std::shared_ptr<ResolveResult> Result;
};
/**
* Store for the async result. A new store is created for every request.
* Old requests simply write to an old store that will then be
* automatically deleted by std::shared_ptr.
*/
std::shared_ptr<ResolveResult> _result;
/**
* Mutex so synchronoise the requests.
*/
SDL_mutex * _resolveMutex;
static void ResolveWorker(ResolveRequest * req);
};

View File

@ -0,0 +1,27 @@
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#pragma once
#include <sdl/SDL_platform.h>
#ifdef __WINDOWS__
// winsock2 must be included before windows.h
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#endif // __WINDOWS__

View File

@ -537,74 +537,6 @@ void NetworkConnection::setLastDisconnectReason(const rct_string_id string_id, v
setLastDisconnectReason(buffer);
}
NetworkAddress::NetworkAddress()
{
ss = std::make_shared<sockaddr_storage>();
ss_len = std::make_shared<int>();
status = std::make_shared<int>();
*status = RESOLVE_NONE;
}
void NetworkAddress::Resolve(const char* host, unsigned short port, bool nonblocking)
{
// A non-blocking hostname resolver
*status = RESOLVE_INPROGRESS;
mutex = SDL_CreateMutex();
cond = SDL_CreateCond();
NetworkAddress::host = host;
NetworkAddress::port = port;
SDL_LockMutex(mutex);
SDL_Thread* thread = SDL_CreateThread(ResolveFunc, 0, this);
// The mutex/cond is to make sure ResolveFunc doesn't ever get a dangling pointer
SDL_CondWait(cond, mutex);
SDL_UnlockMutex(mutex);
SDL_DestroyCond(cond);
SDL_DestroyMutex(mutex);
if (!nonblocking) {
int status;
SDL_WaitThread(thread, &status);
}
}
int NetworkAddress::GetResolveStatus(void)
{
return *status;
}
int NetworkAddress::ResolveFunc(void* pointer)
{
// Copy data for thread safety
NetworkAddress * networkaddress = (NetworkAddress*)pointer;
SDL_LockMutex(networkaddress->mutex);
std::string host;
if (networkaddress->host) host = networkaddress->host;
std::string port = std::to_string(networkaddress->port);
std::shared_ptr<sockaddr_storage> ss = networkaddress->ss;
std::shared_ptr<int> ss_len = networkaddress->ss_len;
std::shared_ptr<int> status = networkaddress->status;
SDL_CondSignal(networkaddress->cond);
SDL_UnlockMutex(networkaddress->mutex);
// Perform the resolve
addrinfo hints;
addrinfo* res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
if (host.length() == 0) {
hints.ai_flags = AI_PASSIVE;
}
getaddrinfo(host.length() == 0 ? NULL : host.c_str(), port.c_str(), &hints, &res);
if (res) {
memcpy(&(*ss), res->ai_addr, res->ai_addrlen);
*ss_len = res->ai_addrlen;
*status = RESOLVE_OK;
freeaddrinfo(res);
} else {
*status = RESOLVE_FAILED;
}
return 0;
}
Network::Network()
{
wsa_initialized = false;
@ -706,7 +638,7 @@ bool Network::BeginClient(const char* host, unsigned short port)
if (!Init())
return false;
server_address.Resolve(host, port);
server_address.ResolveAsync(host, port);
status = NETWORK_STATUS_RESOLVING;
char str_resolving[256];
@ -776,11 +708,16 @@ bool Network::BeginServer(unsigned short port, const char* address)
return false;
_userManager.Load();
NetworkAddress networkaddress;
networkaddress.Resolve(address, port, false);
networkaddress.Resolve(address, port);
sockaddr_storage ss;
int ss_len;
networkaddress.GetResult(&ss, &ss_len);
log_verbose("Begin listening for clients");
listening_socket = socket(networkaddress.ss->ss_family, SOCK_STREAM, IPPROTO_TCP);
listening_socket = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (listening_socket == INVALID_SOCKET) {
log_error("Unable to create socket.");
return false;
@ -792,7 +729,7 @@ bool Network::BeginServer(unsigned short port, const char* address)
log_error("IPV6_V6ONLY failed. %d", LAST_SOCKET_ERROR());
}
if (bind(listening_socket, (sockaddr*)&(*networkaddress.ss), (*networkaddress.ss_len)) != 0) {
if (bind(listening_socket, (sockaddr *)&ss, ss_len) != 0) {
closesocket(listening_socket);
log_error("Unable to bind to socket.");
return false;
@ -934,8 +871,12 @@ void Network::UpdateClient()
bool connectfailed = false;
switch(status){
case NETWORK_STATUS_RESOLVING:{
if (server_address.GetResolveStatus() == NetworkAddress::RESOLVE_OK) {
server_connection.socket = socket(server_address.ss->ss_family, SOCK_STREAM, IPPROTO_TCP);
sockaddr_storage ss;
int ss_len;
NetworkAddress::RESOLVE_STATUS result = server_address.GetResult(&ss, &ss_len);
if (result == NetworkAddress::RESOLVE_OK) {
server_connection.socket = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (server_connection.socket == INVALID_SOCKET) {
log_error("Unable to create socket.");
connectfailed = true;
@ -949,8 +890,9 @@ void Network::UpdateClient()
break;
}
if (connect(server_connection.socket, (sockaddr *)&(*server_address.ss),
(*server_address.ss_len)) == SOCKET_ERROR && (LAST_SOCKET_ERROR() == EINPROGRESS || LAST_SOCKET_ERROR() == EWOULDBLOCK)){
if (connect(server_connection.socket, (sockaddr *)&ss, ss_len) == SOCKET_ERROR &&
(LAST_SOCKET_ERROR() == EINPROGRESS || LAST_SOCKET_ERROR() == EWOULDBLOCK)
) {
char str_connecting[256];
format_string(str_connecting, STR_MULTIPLAYER_CONNECTING, NULL);
window_network_status_open(str_connecting, []() -> void {
@ -963,7 +905,7 @@ void Network::UpdateClient()
connectfailed = true;
break;
}
} else if (server_address.GetResolveStatus() == NetworkAddress::RESOLVE_INPROGRESS) {
} else if (result == NetworkAddress::RESOLVE_INPROGRESS) {
break;
} else {
log_error("Could not resolve address.");

View File

@ -123,6 +123,7 @@ extern "C" {
#include <SDL.h>
#include "../core/Json.hpp"
#include "../core/Nullable.hpp"
#include "NetworkAddress.h"
#include "NetworkKey.h"
#include "NetworkUser.h"
@ -315,33 +316,6 @@ private:
uint32 last_packet_time;
};
class NetworkAddress
{
public:
NetworkAddress();
void Resolve(const char* host, unsigned short port, bool nonblocking = true);
int GetResolveStatus(void);
std::shared_ptr<sockaddr_storage> ss;
std::shared_ptr<int> ss_len;
enum {
RESOLVE_NONE,
RESOLVE_INPROGRESS,
RESOLVE_OK,
RESOLVE_FAILED
};
private:
static int ResolveFunc(void* pointer);
const char* host = nullptr;
unsigned short port = 0;
SDL_mutex* mutex = nullptr;
SDL_cond* cond = nullptr;
std::shared_ptr<int> status;
};
class Network
{
public: