From da484bd8532db9c398d6ef750aa367ecb161a4ef Mon Sep 17 00:00:00 2001 From: Blitblank Date: Sat, 25 Jul 2026 23:51:39 -0500 Subject: [PATCH] sync --- client/src/TcpClient.cpp | 251 ++++++++++++++++++----------- client/src/TcpClient.hpp | 22 ++- client/src/config/ClientConfig.hpp | 2 +- common/TcpBase.hpp | 6 +- 4 files changed, 176 insertions(+), 105 deletions(-) diff --git a/client/src/TcpClient.cpp b/client/src/TcpClient.cpp index a507951..916b37b 100644 --- a/client/src/TcpClient.cpp +++ b/client/src/TcpClient.cpp @@ -3,18 +3,21 @@ #include -TcpClient::TcpClient(ConfigService* config, LoggerService* logger) : TcpBase(logger) { +TcpClient::TcpClient(ConfigService* config, LoggerService* logger, QObject* parent) : QObject(parent), logger_(logger) { + // load config if(!(config->getConfig("TcpClient", "main", &configuration_))) { logger_->log("TcpClient", LogFlag::Error, "Failed to get configuration"); return; } - if(init() == ErrorCode::Success) { - logger_->log("TcpClient", LogFlag::Info, "TcpClient initialized."); - } else { - logger_->log("TcpClient", LogFlag::Error, "TcpClient failed to initialize."); - } + // connect sockets + connect(&socket_, &QTcpSocket::connected, this, &TcpClient::onConnected()); + connect(&socket_, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected()); + connect(&socket_, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead()); + connect(&socket_, &QTcpSocket::errorOccured, this, [this](QAbstractSocket::SocketError) { emit errorOccured(socket_.errorString()); }); // lazy + + logger_->log("TcpClient", LogFlag::Info, "TcpClient initialized."); } @@ -22,102 +25,156 @@ TcpClient::~TcpClient() { // close port } -ErrorCode TcpClient::init() { - logger_->log("TcpClient", LogFlag::Debug, "Initializing TcpClient..."); - - // keeping everything in here for now - int receiveBytes; - char buffer[1024]; - - struct hostent* host; // houses host network info - struct sockaddr_in serverAddress; // ip address of the server socket - - // parse the hostname string into uint8s - host = gethostbyname(configuration_.hostname.c_str()); - if(host == NULL) { - logger_->log("TcpClient", LogFlag::Error, "Unable to parse hostname."); - close(socketFd_); // TODO: probably on a destructor - return ErrorCode::Error; - } - - // create tcp socket - socketFd_ = socket(AF_INET, SOCK_STREAM, 0); - if(socketFd_ < 0) { - logger_->log("TcpClient", LogFlag::Error, "Unable to open client socket."); - close(socketFd_); - return ErrorCode::Error; - } - - // describe the server address from the hostname and port - memset(&serverAddress.sin_zero, 0, sizeof(serverAddress.sin_zero)); - serverAddress.sin_family = AF_INET; - serverAddress.sin_port = htons(configuration_.port); - serverAddress.sin_addr = *((struct in_addr*)host->h_addr); - - char ip[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, host->h_addr_list[0], ip, sizeof(ip)); - logger_->log("TcpClient", LogFlag::Error, "Connecting to {}:{}", ip, configuration_.port); - - // connect to server - if(connect(socketFd_, (struct sockaddr*)&serverAddress, sizeof(struct sockaddr)) < 0) { - logger_->log("TcpClient", LogFlag::Error, "Unable to connect to server."); - close(socketFd_); - return ErrorCode::Error; - } - - // start sending thread (only needed for cli because std::cin is blocking. a regular message box doesn't need to block for input) - pthread_t messengerThread; - if(pthread_create(&messengerThread, NULL, TcpClient::sendMessage, static_cast(this)) != 0) { - logger_->log("TcpClient", LogFlag::Debug, "Unable to spawn messenger thread."); - close(socketFd_); - return ErrorCode::Error; - } - - while(true) { - - // listen for receive from the server - memset(buffer, 0x00, sizeof(buffer)); - receiveBytes = tcpRead(socketFd_, buffer, sizeof(buffer)); // TODO: this currently blocks forever, add timeout - // TODO: separate thread - - // we expect a response based on the server design, something went wrong otherwise - if(receiveBytes < 0) { - logger_->log("TcpClient", LogFlag::Error, "Unable to receive from server."); - return ErrorCode::Error; - } else if(receiveBytes == 0) { - logger_->log("TcpClient", LogFlag::Error, "Server closed the connection during receive."); - close(socketFd_); - return ErrorCode::Error; - } - - std::string receivedMessage(buffer, receiveBytes); - logger_->log("TcpClient", LogFlag::Debug, "Received {} bytes back from the server: {}", receiveBytes, receivedMessage); - } - - close(socketFd_); - pthread_join(messengerThread, NULL); - - return ErrorCode::Success; +ErrorCode TcpClient::connect() { + socket_.connectToHost(configuration_.hostname, configuration_.port); } -void* TcpClient::sendMessage(void* arg) { +ssize_t send(const char* data, size_t numBytes) { + socket_.write(data, numBytes); +} - int sendBytes; - std::string message = "placeholder"; - TcpClient* obj = static_cast(arg); +void TcpClient::onConnected() { + logger_->log("TcpClient", LogFlag::Info, "TcpClient connected to server."); +} + +void TcpClient::onDisconnected() { + logger_->log("TcpClient", LogFlag::Info, "TcpClient disconnected from server."); +} + +void TcpClient::onReadyRead() { + receiveBuffer_.append(socket_.readAll()); + + handleMessage(); +} + +void TcpClient::handleMessage() { while(true) { - obj->logger_->log("TcpClient", LogFlag::Debug, "Enter message..."); - std::getline(std::cin, message); - - // send data buffer to server - sendBytes = obj->tcpSend(obj->socketFd_, message.c_str(), message.size()); - - if(sendBytes < 0) { - obj->logger_->log("TcpClient", LogFlag::Error, "Unable to send to server."); - close(obj->socketFd_); - return nullptr; + if(receiveBuffer_.size() < kHeaderSize) { + logger_->log("TcpClient", LogFlag::Error, "Received message size less than header size."); + return; } - obj->logger_->log("TcpClient", LogFlag::Debug, "Sent {} bytes to the server.", sendBytes); + + // TODO: do this with a struct + uint32_t msgSize; + uint32_t msgType; + memcpy(&msgSize, receiveBuffer_.constData(), sizeof(msgSize)); + memcpy(&msgType, receiveBuffer_.constData() + sizeof(msgSize), sizeof(msgType)); + + // don't know if there's a way to generalize this part + msgSize = ntohl(msgSize); + msgType = ntohl(msgType); + + if(receiveBuffer_.size() != kHeaderSize + msgSize) { + logger_->log("TcpClient", LogFlag::Error, "Received message size ({}), does not match specified ({}).", msgSize, receiveBuffer_.size() - kHeaderSize); + return; + } + + QByteArray message = receiveBuffer_.mid(kHeaderSize, msgSize); + receiveBuffer_.remove(0, msgSize + kHeaderSize); + + emit messageRecieved(message); + } + } + +// ErrorCode TcpClient::init() { +// logger_->log("TcpClient", LogFlag::Debug, "Initializing TcpClient..."); + +// // keeping everything in here for now +// int receiveBytes; +// char buffer[1024]; + +// struct hostent* host; // houses host network info +// struct sockaddr_in serverAddress; // ip address of the server socket + +// // parse the hostname string into uint8s +// host = gethostbyname(configuration_.hostname.c_str()); +// if(host == NULL) { +// logger_->log("TcpClient", LogFlag::Error, "Unable to parse hostname."); +// close(socketFd_); // TODO: probably on a destructor +// return ErrorCode::Error; +// } + +// // create tcp socket +// socketFd_ = socket(AF_INET, SOCK_STREAM, 0); +// if(socketFd_ < 0) { +// logger_->log("TcpClient", LogFlag::Error, "Unable to open client socket."); +// close(socketFd_); +// return ErrorCode::Error; +// } + +// // describe the server address from the hostname and port +// memset(&serverAddress.sin_zero, 0, sizeof(serverAddress.sin_zero)); +// serverAddress.sin_family = AF_INET; +// serverAddress.sin_port = htons(configuration_.port); +// serverAddress.sin_addr = *((struct in_addr*)host->h_addr); + +// char ip[INET_ADDRSTRLEN]; +// inet_ntop(AF_INET, host->h_addr_list[0], ip, sizeof(ip)); +// logger_->log("TcpClient", LogFlag::Error, "Connecting to {}:{}", ip, configuration_.port); + +// // connect to server +// if(connect(socketFd_, (struct sockaddr*)&serverAddress, sizeof(struct sockaddr)) < 0) { +// logger_->log("TcpClient", LogFlag::Error, "Unable to connect to server."); +// close(socketFd_); +// return ErrorCode::Error; +// } + +// // start sending thread (only needed for cli because std::cin is blocking. a regular message box doesn't need to block for input) +// pthread_t messengerThread; +// if(pthread_create(&messengerThread, NULL, TcpClient::sendMessage, static_cast(this)) != 0) { +// logger_->log("TcpClient", LogFlag::Debug, "Unable to spawn messenger thread."); +// close(socketFd_); +// return ErrorCode::Error; +// } + +// while(true) { + +// // listen for receive from the server +// memset(buffer, 0x00, sizeof(buffer)); +// receiveBytes = tcpRead(socketFd_, buffer, sizeof(buffer)); // TODO: this currently blocks forever, add timeout +// // TODO: separate thread + +// // we expect a response based on the server design, something went wrong otherwise +// if(receiveBytes < 0) { +// logger_->log("TcpClient", LogFlag::Error, "Unable to receive from server."); +// return ErrorCode::Error; +// } else if(receiveBytes == 0) { +// logger_->log("TcpClient", LogFlag::Error, "Server closed the connection during receive."); +// close(socketFd_); +// return ErrorCode::Error; +// } + +// std::string receivedMessage(buffer, receiveBytes); +// logger_->log("TcpClient", LogFlag::Debug, "Received {} bytes back from the server: {}", receiveBytes, receivedMessage); +// } + +// close(socketFd_); +// pthread_join(messengerThread, NULL); + +// return ErrorCode::Success; +// } + +// void* TcpClient::sendMessage(void* arg) { + +// int sendBytes; +// std::string message = "placeholder"; +// TcpClient* obj = static_cast(arg); + +// while(true) { +// obj->logger_->log("TcpClient", LogFlag::Debug, "Enter message..."); +// std::getline(std::cin, message); + +// // send data buffer to server +// sendBytes = obj->tcpSend(obj->socketFd_, message.c_str(), message.size()); + +// if(sendBytes < 0) { +// obj->logger_->log("TcpClient", LogFlag::Error, "Unable to send to server."); +// close(obj->socketFd_); +// return nullptr; +// } +// obj->logger_->log("TcpClient", LogFlag::Debug, "Sent {} bytes to the server.", sendBytes); +// } +// } diff --git a/client/src/TcpClient.hpp b/client/src/TcpClient.hpp index 262f944..d09e9b8 100644 --- a/client/src/TcpClient.hpp +++ b/client/src/TcpClient.hpp @@ -22,14 +22,26 @@ public: TcpClient(ConfigService* config, LoggerService* logger); ~TcpClient(); + void connect(); // asynchronous so no errorcode return + ssize_t send(const char* data, size_t numBytes); + +signals: + void connected(); + void disconnected(); + void messageReceived(QByteArray message); + void error(QString error); + +private slots: + void onConnected(); + void onDisconnected(); + void onReadyRead(); + private: - ErrorCode init(); - - // handler for sending messages since std::cin is blocking - static void* sendMessage(void* arg); + void handleMessage(); ClientParams configuration_; - int socketFd_; // file descriptor for the client socket (socket proper is a function) + QTcpSocket socket_; + QByteArray receiveBuffer_[1024]; }; diff --git a/client/src/config/ClientConfig.hpp b/client/src/config/ClientConfig.hpp index 0d191ee..5367bf2 100644 --- a/client/src/config/ClientConfig.hpp +++ b/client/src/config/ClientConfig.hpp @@ -7,7 +7,7 @@ struct ClientParams { std::string id; std::string hostname; - uint32_t port; + uint16_t port; }; class ClientConfig : public IConfig { diff --git a/common/TcpBase.hpp b/common/TcpBase.hpp index 5a61666..edfdf65 100644 --- a/common/TcpBase.hpp +++ b/common/TcpBase.hpp @@ -6,11 +6,13 @@ // TcpBase contains common tcp I/O operations for the tcp client and tcp server // Abstracts away the networking methods into an accordion common protocol (ACP) -class TcpBase { +class TcpBase : public QObject{ + + Q_OBJECT public: - TcpBase(LoggerService* logger); + TcpBase(LoggerService* logger, QObject* parent = nullptr); ~TcpBase(); // read from a tcp socket, blocking