abstract common tcp operations to a base class

This commit is contained in:
2026-07-18 22:36:01 -05:00
parent aa007a3130
commit 001bd8e629
8 changed files with 216 additions and 41 deletions

View File

@@ -1,6 +1,7 @@
add_library(accordion-common STATIC
LoggerService.cpp
TcpBase.cpp
config/ConfigService.cpp
)

View File

@@ -101,5 +101,7 @@ void LoggerService::write(std::string component, LogFlag flag, std::string messa
if(configuration_.fileEnabled) {
outfile_ << finalmessage << std::endl;
}
// TODO: if its an error flag we should automatically log the errno
}

121
common/TcpBase.cpp Normal file
View File

@@ -0,0 +1,121 @@
#include "TcpBase.hpp"
#include <sys/socket.h>
#include <string.h>
TcpBase::TcpBase(LoggerService* logger) : logger_(logger) {
}
TcpBase::~TcpBase() {
}
ssize_t TcpBase::tcpRead(int fd, void* buffer, size_t maxBytes) {
// read header first
char header[kHeaderSize];
ssize_t bytesReceived = receiveFull(fd, &header, kHeaderSize);
if(bytesReceived != kHeaderSize) {
logger_->log("TcpNetworker", LogFlag::Error, "Unable to receive header.");
return -1;
}
// parse header: 4 bytes payload length 4 bytes message type
uint32_t payloadLength;
uint32_t messageType;
memcpy(&payloadLength, header, sizeof(payloadLength));
memcpy(&messageType, header + sizeof(payloadLength), sizeof(messageType));
size_t payloadLengthS = static_cast<size_t>(payloadLength);
if(maxBytes < payloadLength) {
logger_->log("TcpNetworker", LogFlag::Warning,
"Payload length is larger than buffer size; payload may be truncated.");
payloadLengthS = maxBytes;
}
// read payload
bytesReceived = receiveFull(fd, buffer, payloadLengthS);
if(bytesReceived != payloadLengthS) {
logger_->log("TcpNetworker", LogFlag::Error, "Did not receive amount of expected data.");
}
return bytesReceived;
}
ssize_t TcpBase::tcpTimedRead(int fd, void* buffer, size_t maxBytes, int timeoutPlaceholder) {
return 0;
}
ssize_t TcpBase::tcpSend(int fd, const void* buffer, size_t numBytes) {
// assemble header
char header[kHeaderSize];
uint32_t payloadLength = static_cast<uint32_t>(numBytes & 0xffffffffffffffff);
uint32_t messageType = 0; // placeholder
memcpy(header, &payloadLength, sizeof(payloadLength));
memcpy(header + sizeof(payloadLength), &messageType, sizeof(messageType));
// send header
ssize_t bytesSent = sendFull(fd, header, kHeaderSize);
if(bytesSent != kHeaderSize) {
logger_->log("TcpNetworker", LogFlag::Error, "Error sending header.");
}
// send payload
bytesSent = sendFull(fd, buffer, numBytes);
if(bytesSent != numBytes) {
logger_->log("TcpNetworker", LogFlag::Error, "Error sending payload.");
}
return bytesSent;
}
ssize_t TcpBase::tcpTimedSend(int fd, const void* backuffer, size_t numBytes, int timeoutPlaceholder) {
return 0;
}
ssize_t TcpBase::receiveHeader(int fd, void* buffer, size_t maxBytes) {
return 0;
}
ssize_t TcpBase::receivePayload(int fd, void* buffer, size_t maxBytes) {
return 0;
}
ssize_t TcpBase::sendHeader(int fd, const void* buffer, size_t maxBytes) {
return 0;
}
ssize_t TcpBase::sendPayload(int fd, const void* buffer, size_t maxBytes) {
return 0;
}
ssize_t TcpBase::receiveFull(int fd, void* buffer, size_t maxBytes) {
ssize_t total = 0;
// attempt to receive maxBytes bytes; continue receiving until we have received maxBytes bytes
while(total < maxBytes) {
ssize_t bytesReceived = recv(fd, static_cast<char*>(buffer) + total, maxBytes - total, 0);
if (bytesReceived <= 0) return bytesReceived; // error or connection closed
total += bytesReceived;
}
return total;
}
ssize_t TcpBase::sendFull(int fd, const void* buffer, size_t numBytes) {
ssize_t total = 0;
// attempt to receive maxBytes bytes; continue receiving until we have received maxBytes bytes
while(total < numBytes) {
ssize_t bytesSent = send(fd, static_cast<const char*>(buffer) + total, numBytes - total, 0);
// last arg is flags, 0=default
// MSG_PEEK: read without consuming the message from the queue
// MSG_WAITALL: block until all of the specified size is received
// MSG_DONTWAIT: non-blocking receive, returns -1 immediately if queue is empty
if (bytesSent <= 0) return bytesSent; // error or connection closed
total += bytesSent;
}
return total;
}

44
common/TcpBase.hpp Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include "ErrorCodes.hpp"
#include "LoggerService.hpp"
// 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 {
public:
TcpBase(LoggerService* logger);
~TcpBase();
// read from a tcp socket, blocking
ssize_t tcpRead(int fd, void* buffer, size_t maxBytes);
// read from a tcp socket, blocking with timeout
ssize_t tcpTimedRead(int fd, void* buffer, size_t maxBytes, int timeoutPlaceholder);
// send over a tcp socket, blocking
ssize_t tcpSend(int fd, const void* buffer, size_t numBytes);
// send over a tcp socket, blocking with timeout
ssize_t tcpTimedSend(int fd, const void* buffer, size_t numBytes, int timeoutPlaceholder);
protected:
LoggerService* logger_;
private:
ssize_t receiveHeader(int fd, void* buffer, size_t maxBytes);
ssize_t receivePayload(int fd, void* buffer, size_t maxBytes);
ssize_t sendHeader(int fd, const void* buffer, size_t maxBytes);
ssize_t sendPayload(int fd, const void* buffer, size_t maxBytes);
ssize_t receiveFull(int fd, void* buffer, size_t maxBytes);
ssize_t sendFull(int fd, const void* buffer, size_t numBytes);
static constexpr size_t kHeaderSize = 8; // 4 bytes for length, 4 bytes for type
};