#include "socketactions.h" #include "Logger.h" #include #include #include #include #include #include #include namespace sockAct { struct SocketContext { std::unique_ptr socket; QMutex sendMutex; QMutex connectMutex; bool isInitialized = false; std::string lastError; QHostAddress lastConnectedHost; quint16 lastConnectedPort = 0; qint64 lastSendTime = 0; qint64 lastReceiveTime = 0; int sendCount = 0; int receiveCount = 0; }; static SocketContext& context() { // Static context static SocketContext ctx; return ctx; } namespace { bool ensureInitialized(); bool internalConnectToHost(const std::string& host, quint16 port, int timeoutMs); std::string waitForResponse(int timeoutMs); void logNetworkEvent(const std::string& message, LogLevel level); void handleSocketError(QAbstractSocket::SocketError error); void resetSocket(); QString toQString(const std::string& str); std::string fromQString(const QString& str); // Add UTF-8 encoding helper functions QByteArray stringToUtf8Bytes(const std::string& str); std::string bytesToUtf8String(const QByteArray& bytes); } bool sockInit() { QMutexLocker locker(&context().connectMutex); if (context().isInitialized) { logNetworkEvent("[Socket] Initialization completed.", INFO); return true; } if (!QNetworkInterface::allInterfaces().isEmpty()) { // Check Qt Network Module logNetworkEvent("[Socket] Qt Networking module initializing completed.", INFO); } else { logNetworkEvent("[Socket] No avaliable network interfaces", WARNING); } context().isInitialized = true; logNetworkEvent("[Socket] Initialization completed.", INFO); return true; } bool connectToHost(const std::string& host, quint16 port, int timeoutMs) { if (!ensureInitialized()) { return false; } QMutexLocker locker(&context().connectMutex); QString qhost = toQString(host); if (context().socket && context().lastConnectedHost.toString() == qhost && context().lastConnectedPort == port) { if (context().socket->state() == QAbstractSocket::ConnectedState) { logNetworkEvent(std::string("[Socket] Connected to server: ") + host + ":" + std::to_string(port), INFO); return true; } } return internalConnectToHost(host, port, timeoutMs); } std::string tcpSend(const std::string& data, int timeoutMs) { if (!ensureInitialized()) { return std::string(); } QMutexLocker locker(&context().sendMutex); if (!context().socket || context().socket->state() != QAbstractSocket::ConnectedState) { logNetworkEvent("[Socket] No connection avaliable, cannot send any data.", ERROR); return std::string(); } // Convert string to UTF-8 bytes for transmission QByteArray sendData = stringToUtf8Bytes(data); qint64 bytesWritten = context().socket->write(sendData); // Send data context().lastSendTime = QDateTime::currentMSecsSinceEpoch(); context().sendCount++; if (bytesWritten == -1) { QString errorStr = context().socket->errorString(); logNetworkEvent("[Socket] Failed to send data: " + fromQString(errorStr), ERROR); return std::string(); } if (bytesWritten != sendData.size()) { logNetworkEvent("[Socket] Data send not complete, expected to send " + std::to_string(sendData.size()) + " bytes, " + std::to_string(bytesWritten) + " bytes sent", WARNING); } logNetworkEvent("[Socket] " + std::to_string(bytesWritten) + " bytes has been sent to sevrer", DEBUG); std::string response = waitForResponse(timeoutMs); return response; } void clearSocket() { QMutexLocker locker1(&context().connectMutex); QMutexLocker locker2(&context().sendMutex); if (context().socket) { std::string serverInfo; if (!context().lastConnectedHost.isNull()) { serverInfo = fromQString(context().lastConnectedHost.toString()) + ":" + std::to_string(context().lastConnectedPort); } logNetworkEvent("[Socket] Disconnecting from server " + serverInfo + "...", INFO); if (context().socket->state() != QAbstractSocket::UnconnectedState) { context().socket->abort(); } context().socket->disconnect(); // Reset connection info context().lastConnectedHost.clear(); context().lastConnectedPort = 0; context().lastSendTime = 0; context().lastReceiveTime = 0; context().sendCount = 0; context().receiveCount = 0; logNetworkEvent("[Socket] Disconnected.", INFO); } } bool isConnected() { if (!context().socket) { return false; } return context().socket->state() == QAbstractSocket::ConnectedState; } std::string getLastError() { return context().lastError; } // ==================== Internal methods ==================== namespace { QString toQString(const std::string& str) { return QString::fromStdString(str); } std::string fromQString(const QString& str) { return str.toStdString(); } // Convert string to UTF-8 bytes QByteArray stringToUtf8Bytes(const std::string& str) { // Use QString to convert to UTF-8 QString qstr = QString::fromStdString(str); return qstr.toUtf8(); } // Convert UTF-8 bytes to string std::string bytesToUtf8String(const QByteArray& bytes) { // Convert from UTF-8 bytes to QString, then to std::string QString qstr = QString::fromUtf8(bytes); return qstr.toStdString(); } bool ensureInitialized() { if (!context().isInitialized) { logNetworkEvent("[Socket] Not initialized.", ERROR); return false; } return true; } bool internalConnectToHost(const std::string& host, quint16 port, int timeoutMs) { if (context().socket) { context().socket->abort(); context().socket.reset(); } context().socket.reset(new QTcpSocket()); QString qhost = toQString(host); QObject::connect(context().socket.get(), &QTcpSocket::connected, [host, port]() { logNetworkEvent("[Socket] Connected to server " + host + ":" + std::to_string(port), INFO); }); QObject::connect(context().socket.get(), QOverload::of(&QTcpSocket::errorOccurred), [](QAbstractSocket::SocketError error) { handleSocketError(error); }); logNetworkEvent("[Socket] Connecting to server " + host + ":" + std::to_string(port) + " (Timeout: " + std::to_string(timeoutMs) + "ms)...", INFO); context().socket->connectToHost(qhost, port); QElapsedTimer timer; timer.start(); while (context().socket->state() == QAbstractSocket::ConnectingState) { if (timer.elapsed() > timeoutMs) { logNetworkEvent("[Socket] Connection to server " + host + ":" + std::to_string(port) + " timeout", WARNING); context().socket->abort(); return false; } QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QThread::msleep(10); } if (context().socket->state() != QAbstractSocket::ConnectedState) { QString errorStr = context().socket->errorString(); logNetworkEvent("[Sockct] Failed to connect to server " + host + ":" + std::to_string(port) + " Reason: " + fromQString(errorStr), ERROR); return false; } context().lastConnectedHost = context().socket->peerAddress(); context().lastConnectedPort = context().socket->peerPort(); return true; } std::string waitForResponse(int timeoutMs) { if (!context().socket) { return std::string(); } QElapsedTimer timer; timer.start(); while (context().socket->bytesAvailable() == 0) { if (timer.elapsed() > timeoutMs) { logNetworkEvent("[Socket] Timeout when waiting for reply (" + std::to_string(timeoutMs) + "ms)", WARNING); return std::string(); } if (!context().socket->waitForReadyRead(100)) { if (context().socket->state() != QAbstractSocket::ConnectedState) { logNetworkEvent("[Socket] Connection lost.", ERROR); return std::string(); } } } QByteArray responseData = context().socket->readAll(); context().lastReceiveTime = QDateTime::currentMSecsSinceEpoch(); context().receiveCount++; // Convert UTF-8 bytes to string std::string response = bytesToUtf8String(responseData); int waitTime = timer.elapsed(); logNetworkEvent("[Socket] Reply from server " + std::to_string(responseData.size()) + " bytes, Time: " + std::to_string(waitTime) + "ms", DEBUG); return response; } void logNetworkEvent(const std::string& message, LogLevel level) { postLog(message, static_cast(level)); } void handleSocketError(QAbstractSocket::SocketError error) { std::string errorMsg; LogLevel level = ERROR; switch (error) { case QAbstractSocket::ConnectionRefusedError: errorMsg = "[Socket] Connection refused."; break; case QAbstractSocket::RemoteHostClosedError: errorMsg = "[Socket] Remote server closed connection."; level = WARNING; break; case QAbstractSocket::HostNotFoundError: errorMsg = "[Socket] No host found."; break; case QAbstractSocket::SocketAccessError: errorMsg = "[Socket] Access error."; break; case QAbstractSocket::SocketResourceError: errorMsg = "[Socket] Resource error."; level = WARNING; break; case QAbstractSocket::SocketTimeoutError: errorMsg = "[Socket] Timeout."; level = WARNING; break; case QAbstractSocket::NetworkError: errorMsg = "[Socket] Network fatal."; level = FATAL; break; case QAbstractSocket::SslHandshakeFailedError: errorMsg = "[Socket] TLS handshake error."; break; default: errorMsg = "[Socket] Unknown error."; break; } if (context().socket) { QString errorStr = context().socket->errorString(); errorMsg += ": " + fromQString(errorStr); } logNetworkEvent(errorMsg, level); context().lastError = errorMsg; } void resetSocket() { if (context().socket) { context().socket->disconnect(); context().socket.reset(); } context().lastError.clear(); } } } // namespace sockAct