This commit is contained in:
2025-12-29 18:02:42 +01:00
parent 17cc314c26
commit 68a825b7df
7 changed files with 53 additions and 40 deletions

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ CMakeUserPresets.json
build/ build/
.vscode/ .vscode/
.DS_Store

View File

@@ -11,10 +11,6 @@
#include <columnlynx/common/utils.hpp> #include <columnlynx/common/utils.hpp>
#include <array> #include <array>
#include <vector> #include <vector>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#include <memory> #include <memory>
#include <cstring> #include <cstring>

View File

@@ -96,7 +96,7 @@ int main(int argc, char** argv) {
}); });
//ioThread.join(); //ioThread.join();
log("Client connected to " + host + ":" + port); log("Attempting connection to " + host + ":" + port);
debug("Client connection flag: " + std::to_string(client->isConnected())); debug("Client connection flag: " + std::to_string(client->isConnected()));
debug("Client handshake flag: " + std::to_string(client->isHandshakeComplete())); debug("Client handshake flag: " + std::to_string(client->isHandshakeComplete()));
debug("isDone flag: " + std::to_string(done)); debug("isDone flag: " + std::to_string(done));

View File

@@ -13,13 +13,22 @@ namespace ColumnLynx::Net::TCP {
if (!ec) { if (!ec) {
asio::async_connect(mSocket, endpoints, asio::async_connect(mSocket, endpoints,
[this, self](asio::error_code ec, const tcp::endpoint&) { [this, self](asio::error_code ec, const tcp::endpoint&) {
if (!NetHelper::isExpectedDisconnect(ec)) { if (!ec) {
mConnected = true; mConnected = true;
Utils::log("Client connected."); Utils::log("Client connected.");
mHandler = std::make_shared<MessageHandler>(std::move(mSocket)); mHandler = std::make_shared<MessageHandler>(std::move(mSocket));
mHandler->onMessage([this](AnyMessageType type, const std::string& data) { mHandler->onMessage([this](AnyMessageType type, const std::string& data) {
mHandleMessage(static_cast<ServerMessageType>(MessageHandler::toUint8(type)), data); mHandleMessage(static_cast<ServerMessageType>(MessageHandler::toUint8(type)), data);
}); });
// Close only after peer FIN to avoid RSTs
mHandler->onDisconnect([this](const asio::error_code& ec) {
asio::error_code ec2;
if (mHandler) {
mHandler->socket().close(ec2);
}
mConnected = false;
Utils::log(std::string("Server disconnected: ") + ec.message());
});
mHandler->start(); mHandler->start();
// Init connection handshake // Init connection handshake
@@ -50,8 +59,10 @@ namespace ColumnLynx::Net::TCP {
mStartHeartbeat(); mStartHeartbeat();
} else { } else {
if (!NetHelper::isExpectedDisconnect(ec)) {
Utils::error("Client connect failed: " + ec.message()); Utils::error("Client connect failed: " + ec.message());
} }
}
}); });
} else { } else {
Utils::error("Client resolve failed: " + ec.message()); Utils::error("Client resolve failed: " + ec.message());
@@ -81,18 +92,14 @@ namespace ColumnLynx::Net::TCP {
asio::error_code ec; asio::error_code ec;
mHeartbeatTimer.cancel(); mHeartbeatTimer.cancel();
mHandler->socket().shutdown(tcp::socket::shutdown_both, ec); // Half-close: stop sending, keep reading until peer FIN
mHandler->socket().shutdown(tcp::socket::shutdown_send, ec);
if (ec) { if (ec) {
Utils::error("Error during socket shutdown: " + ec.message()); Utils::error("Error during socket shutdown: " + ec.message());
} }
mHandler->socket().close(ec); // Do not close immediately; rely on onDisconnect to finalize
if (ec) { Utils::log("Client initiated graceful disconnect (half-close).");
Utils::error("Error during socket close: " + ec.message());
}
mConnected = false;
Utils::log("Client disconnected.");
} }
} }

View File

@@ -43,14 +43,20 @@ namespace ColumnLynx::Net::TCP {
auto self = shared_from_this(); auto self = shared_from_this();
asio::async_read(mSocket, asio::buffer(mHeader), asio::async_read(mSocket, asio::buffer(mHeader),
[this, self](asio::error_code ec, std::size_t) { [this, self](asio::error_code ec, std::size_t) {
if (!NetHelper::isExpectedDisconnect(ec)) { if (!ec) {
mCurrentType = decodeMessageType(mHeader[0]); mCurrentType = decodeMessageType(mHeader[0]);
uint16_t len = (mHeader[1] << 8) | mHeader[2]; uint16_t len = (mHeader[1] << 8) | mHeader[2];
mReadBody(len); mReadBody(len);
} else { } else {
if (!NetHelper::isExpectedDisconnect(ec)) {
Utils::error("Header read failed: " + ec.message()); Utils::error("Header read failed: " + ec.message());
} }
// Connection closed, trigger disconnect handler
if (mOnDisconnect) {
mOnDisconnect(ec);
}
}
} }
); );
} }
@@ -61,7 +67,7 @@ namespace ColumnLynx::Net::TCP {
asio::async_read(mSocket, asio::buffer(mBody), asio::async_read(mSocket, asio::buffer(mBody),
[this, self](asio::error_code ec, std::size_t) { [this, self](asio::error_code ec, std::size_t) {
if (!NetHelper::isExpectedDisconnect(ec)) { if (!ec) {
std::string payload(mBody.begin(), mBody.end()); std::string payload(mBody.begin(), mBody.end());
// Dispatch based on message type // Dispatch based on message type
@@ -71,8 +77,10 @@ namespace ColumnLynx::Net::TCP {
mReadHeader(); // Keep listening mReadHeader(); // Keep listening
} else { } else {
if (!NetHelper::isExpectedDisconnect(ec)) {
Utils::error("Body read failed: " + ec.message()); Utils::error("Body read failed: " + ec.message());
}
// Connection closed, trigger disconnect handler
if (mOnDisconnect) { if (mOnDisconnect) {
mOnDisconnect(ec); mOnDisconnect(ec);
} }

View File

@@ -11,8 +11,20 @@ namespace ColumnLynx::Net::TCP {
}); });
mHandler->onDisconnect([this](const asio::error_code& ec) { mHandler->onDisconnect([this](const asio::error_code& ec) {
Utils::log("Client disconnected: " + mHandler->socket().remote_endpoint().address().to_string() + " - " + ec.message()); // Peer has closed; finalize locally without sending RST
disconnect(); std::string ip = mHandler->socket().remote_endpoint().address().to_string();
Utils::log("Client disconnected: " + ip + " - " + ec.message());
asio::error_code ec2;
mHandler->socket().close(ec2);
SessionRegistry::getInstance().erase(mConnectionSessionID);
SessionRegistry::getInstance().deallocIP(mConnectionSessionID);
Utils::log("Closed connection to " + ip);
if (mOnDisconnect) {
mOnDisconnect(shared_from_this());
}
}); });
mHandler->start(); mHandler->start();
@@ -40,24 +52,13 @@ namespace ColumnLynx::Net::TCP {
} }
mHeartbeatTimer.cancel(); mHeartbeatTimer.cancel();
asio::error_code ec; asio::error_code ec;
mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec); // Half-close: stop sending, keep reading until peer FIN
mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_send, ec);
if (ec) { if (ec) {
Utils::error("Error during socket shutdown: " + ec.message()); Utils::error("Error during socket shutdown: " + ec.message());
} }
// Do not close immediately; final cleanup happens in onDisconnect
mHandler->socket().close(ec); Utils::log("Initiated graceful disconnect (half-close) to " + ip);
if (ec) {
Utils::error("Error during socket close: " + ec.message());
}
SessionRegistry::getInstance().erase(mConnectionSessionID);
SessionRegistry::getInstance().deallocIP(mConnectionSessionID);
Utils::log("Closed connection to " + ip);
if (mOnDisconnect) {
mOnDisconnect(shared_from_this());
}
} }
uint64_t TCPConnection::getSessionID() const { uint64_t TCPConnection::getSessionID() const {