socket issues
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <imgui/backends/imgui_impl_opengl3.h>
|
#include <imgui/backends/imgui_impl_opengl3.h>
|
||||||
@@ -23,6 +24,12 @@ namespace anm2ed
|
|||||||
constexpr auto SOCKET_ADDRESS = "127.0.0.1";
|
constexpr auto SOCKET_ADDRESS = "127.0.0.1";
|
||||||
constexpr auto SOCKET_PORT = 11414;
|
constexpr auto SOCKET_PORT = 11414;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
constexpr int SOCKET_ERROR_ADDRESS_IN_USE = WSAEADDRINUSE;
|
||||||
|
#else
|
||||||
|
constexpr int SOCKET_ERROR_ADDRESS_IN_USE = EADDRINUSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
bool socket_paths_send(Socket& socket, const std::vector<std::string>& paths)
|
bool socket_paths_send(Socket& socket, const std::vector<std::string>& paths)
|
||||||
@@ -75,8 +82,6 @@ namespace anm2ed
|
|||||||
if (!testSocket.open(SERVER))
|
if (!testSocket.open(SERVER))
|
||||||
logger.warning(std::format("Failed to open socket; single instancing will not work."));
|
logger.warning(std::format("Failed to open socket; single instancing will not work."));
|
||||||
|
|
||||||
bool isPrimaryInstance = false;
|
|
||||||
|
|
||||||
if (testSocket.bind({SOCKET_ADDRESS, SOCKET_PORT}))
|
if (testSocket.bind({SOCKET_ADDRESS, SOCKET_PORT}))
|
||||||
{
|
{
|
||||||
socket = std::move(testSocket);
|
socket = std::move(testSocket);
|
||||||
@@ -85,13 +90,19 @@ namespace anm2ed
|
|||||||
logger.warning("Could not listen on socket; single instancing disabled.");
|
logger.warning("Could not listen on socket; single instancing disabled.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isPrimaryInstance = true;
|
|
||||||
isSocketThread = true;
|
isSocketThread = true;
|
||||||
logger.info(std::format("Opened socket at {}:{}", SOCKET_ADDRESS, SOCKET_PORT));
|
logger.info(std::format("Opened socket at {}:{}", SOCKET_ADDRESS, SOCKET_PORT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (testSocket.last_error() != SOCKET_ERROR_ADDRESS_IN_USE)
|
||||||
|
{
|
||||||
|
logger.fatal(std::format("Failed to bind single-instance socket (error {}).", testSocket.last_error()));
|
||||||
|
isError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(std::format("Existing instance of program exists; passing arguments..."));
|
logger.info(std::format("Existing instance of program exists; passing arguments..."));
|
||||||
Socket clientSocket;
|
Socket clientSocket;
|
||||||
if (!clientSocket.open(CLIENT))
|
if (!clientSocket.open(CLIENT))
|
||||||
@@ -103,11 +114,8 @@ namespace anm2ed
|
|||||||
else
|
else
|
||||||
logger.info("Sent arguments to existing instance. Exiting.");
|
logger.info("Sent arguments to existing instance. Exiting.");
|
||||||
|
|
||||||
if (!isPrimaryInstance)
|
isError = true;
|
||||||
{
|
return;
|
||||||
isError = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings = Settings(settings_path());
|
settings = Settings(settings_path());
|
||||||
|
|||||||
138
src/socket.cpp
138
src/socket.cpp
@@ -1,10 +1,12 @@
|
|||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
namespace anm2ed
|
namespace anm2ed
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
struct WSAInitializer
|
struct WSAInitializer
|
||||||
{
|
{
|
||||||
WSAInitializer()
|
WSAInitializer()
|
||||||
@@ -16,12 +18,25 @@ namespace anm2ed
|
|||||||
};
|
};
|
||||||
|
|
||||||
WSAInitializer initializer{};
|
WSAInitializer initializer{};
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Socket::Socket() : handle(SOCKET_INVALID), role(CLIENT) {}
|
int socket_last_error()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return WSAGetLastError();
|
||||||
|
#else
|
||||||
|
return errno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Socket::Socket(Socket&& other) noexcept : handle(other.handle), role(other.role) { other.handle = SOCKET_INVALID; }
|
Socket::Socket() : handle(SOCKET_INVALID), role(CLIENT), lastError(0) {}
|
||||||
|
|
||||||
|
Socket::Socket(Socket&& other) noexcept : handle(other.handle), role(other.role), lastError(other.lastError)
|
||||||
|
{
|
||||||
|
other.handle = SOCKET_INVALID;
|
||||||
|
other.lastError = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Socket& Socket::operator=(Socket&& other) noexcept
|
Socket& Socket::operator=(Socket&& other) noexcept
|
||||||
{
|
{
|
||||||
@@ -31,6 +46,8 @@ namespace anm2ed
|
|||||||
handle = other.handle;
|
handle = other.handle;
|
||||||
role = other.role;
|
role = other.role;
|
||||||
other.handle = SOCKET_INVALID;
|
other.handle = SOCKET_INVALID;
|
||||||
|
lastError = other.lastError;
|
||||||
|
other.lastError = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@@ -45,24 +62,44 @@ namespace anm2ed
|
|||||||
|
|
||||||
handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
|
||||||
if (!is_valid()) return false;
|
if (!is_valid())
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (role == SERVER)
|
if (role == SERVER)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
BOOL opt = TRUE;
|
BOOL opt = TRUE;
|
||||||
|
if (::setsockopt(handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&opt), sizeof(opt)) != 0)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
|
if (::setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&opt), sizeof(opt)) != 0)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
::setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&opt), sizeof(opt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastError = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::bind(const SocketAddress& address)
|
bool Socket::bind(const SocketAddress& address)
|
||||||
{
|
{
|
||||||
if (!is_valid()) return false;
|
if (!is_valid())
|
||||||
|
{
|
||||||
|
lastError = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sockaddr_in addr{};
|
sockaddr_in addr{};
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
@@ -72,16 +109,38 @@ namespace anm2ed
|
|||||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0) return false;
|
if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ::bind(handle, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == 0;
|
if (::bind(handle, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == 0)
|
||||||
|
{
|
||||||
|
lastError = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::listen()
|
bool Socket::listen()
|
||||||
{
|
{
|
||||||
if (!is_valid()) return false;
|
if (!is_valid())
|
||||||
return ::listen(handle, SOMAXCONN) == 0;
|
{
|
||||||
|
lastError = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (::listen(handle, SOMAXCONN) == 0)
|
||||||
|
{
|
||||||
|
lastError = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket Socket::accept()
|
Socket Socket::accept()
|
||||||
@@ -90,30 +149,54 @@ namespace anm2ed
|
|||||||
if (!is_valid()) return client;
|
if (!is_valid()) return client;
|
||||||
|
|
||||||
auto accepted = ::accept(handle, nullptr, nullptr);
|
auto accepted = ::accept(handle, nullptr, nullptr);
|
||||||
if (accepted == SOCKET_INVALID) return client;
|
if (accepted == SOCKET_INVALID)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
client.close();
|
client.close();
|
||||||
client.handle = accepted;
|
client.handle = accepted;
|
||||||
client.role = CLIENT;
|
client.role = CLIENT;
|
||||||
|
lastError = 0;
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::connect(const SocketAddress& address)
|
bool Socket::connect(const SocketAddress& address)
|
||||||
{
|
{
|
||||||
if (!is_valid()) return false;
|
if (!is_valid())
|
||||||
|
{
|
||||||
|
lastError = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sockaddr_in addr{};
|
sockaddr_in addr{};
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(address.port);
|
addr.sin_port = htons(address.port);
|
||||||
|
|
||||||
if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0) return false;
|
if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return ::connect(handle, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == 0;
|
if (::connect(handle, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == 0)
|
||||||
|
{
|
||||||
|
lastError = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::send(const void* data, size_t size)
|
bool Socket::send(const void* data, size_t size)
|
||||||
{
|
{
|
||||||
if (!is_valid() || !data || size == 0) return false;
|
if (!is_valid() || !data || size == 0)
|
||||||
|
{
|
||||||
|
lastError = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto bytes = reinterpret_cast<const char*>(data);
|
auto bytes = reinterpret_cast<const char*>(data);
|
||||||
size_t totalSent = 0;
|
size_t totalSent = 0;
|
||||||
@@ -121,16 +204,25 @@ namespace anm2ed
|
|||||||
while (totalSent < size)
|
while (totalSent < size)
|
||||||
{
|
{
|
||||||
auto sent = ::send(handle, bytes + totalSent, static_cast<int>(size - totalSent), 0);
|
auto sent = ::send(handle, bytes + totalSent, static_cast<int>(size - totalSent), 0);
|
||||||
if (sent <= 0) return false;
|
if (sent <= 0)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
totalSent += static_cast<size_t>(sent);
|
totalSent += static_cast<size_t>(sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastError = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::receive(void* buffer, size_t size)
|
bool Socket::receive(void* buffer, size_t size)
|
||||||
{
|
{
|
||||||
if (!is_valid() || !buffer || size == 0) return false;
|
if (!is_valid() || !buffer || size == 0)
|
||||||
|
{
|
||||||
|
lastError = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto* bytes = reinterpret_cast<char*>(buffer);
|
auto* bytes = reinterpret_cast<char*>(buffer);
|
||||||
size_t totalReceived = 0;
|
size_t totalReceived = 0;
|
||||||
@@ -138,10 +230,15 @@ namespace anm2ed
|
|||||||
while (totalReceived < size)
|
while (totalReceived < size)
|
||||||
{
|
{
|
||||||
auto received = ::recv(handle, bytes + totalReceived, static_cast<int>(size - totalReceived), 0);
|
auto received = ::recv(handle, bytes + totalReceived, static_cast<int>(size - totalReceived), 0);
|
||||||
if (received <= 0) return false;
|
if (received <= 0)
|
||||||
|
{
|
||||||
|
lastError = socket_last_error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
totalReceived += static_cast<size_t>(received);
|
totalReceived += static_cast<size_t>(received);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastError = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +252,10 @@ namespace anm2ed
|
|||||||
::close(handle);
|
::close(handle);
|
||||||
#endif
|
#endif
|
||||||
handle = SOCKET_INVALID;
|
handle = SOCKET_INVALID;
|
||||||
|
lastError = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::is_valid() const { return handle != SOCKET_INVALID; }
|
bool Socket::is_valid() const { return handle != SOCKET_INVALID; }
|
||||||
|
|
||||||
|
int Socket::last_error() const { return lastError; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace anm2ed
|
|||||||
private:
|
private:
|
||||||
socket_handle handle;
|
socket_handle handle;
|
||||||
SocketRole role{};
|
SocketRole role{};
|
||||||
|
int lastError{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Socket();
|
Socket();
|
||||||
@@ -57,5 +58,6 @@ namespace anm2ed
|
|||||||
|
|
||||||
void close();
|
void close();
|
||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
|
int last_error() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user