IRSOL
C++ code implementing socket server for interacting with Baumer camera.
session.cpp
Go to the documentation of this file.
2
3#include "irsol/logging.hpp"
8#include "irsol/utils.hpp"
9
10#include <algorithm>
11#include <vector>
12
13namespace irsol {
14namespace server {
15
16ClientSession::ClientSession(const std::string& id, irsol::types::socket_t&& sock, App& app)
17 : m_id(id), m_socket(std::move(sock)), m_app(app)
18{}
19
20void
22{
23 constexpr size_t INITIAL_BUFFER_SIZE = 1024;
24 std::vector<char> buffer(INITIAL_BUFFER_SIZE);
25 std::string messageBuffer;
26
27 IRSOL_NAMED_LOG_DEBUG(m_id, "Client session started running");
28
29 while(true) {
30 // Read data from socket
31 auto readResult = m_socket.read(buffer.data(), buffer.size());
32
33 // Handle read errors or connection closure
34 if(!readResult) {
35 IRSOL_NAMED_LOG_ERROR(m_id, "Socket read error: {}", readResult.error().message());
36 break;
37 }
38
39 size_t bytesRead = readResult.value();
40 if(bytesRead == 0) {
41 IRSOL_NAMED_LOG_INFO(m_id, "Connection closed by client");
42 break;
43 }
44
45 // Append received data to message buffer
46 messageBuffer.append(buffer.data(), bytesRead);
48 m_id,
49 "Read {} bytes: '{}'",
50 bytesRead,
51 messageBuffer.substr(messageBuffer.size() - bytesRead));
52
53 // Process all complete messages in the buffer
54 processInMessageBuffer(messageBuffer);
55
56 // Dynamically resize buffer if it was filled completely
57 if(bytesRead == buffer.size()) {
58 IRSOL_NAMED_LOG_DEBUG(m_id, "Increasing buffer size to {}", buffer.size());
59 buffer.resize(buffer.size() * 2);
60 }
61 }
62
63 m_socket.close();
64 IRSOL_NAMED_LOG_INFO(m_id, "Client session terminated");
65}
66
67void
68ClientSession::handleOutMessages(std::vector<protocol::OutMessage>&& messages)
69{
70 IRSOL_NAMED_LOG_DEBUG(m_id, "Serializing {} messages", messages.size());
71 for(auto& message : messages) {
72 handleOutMessage(std::move(message));
73 }
74}
75
76void
78{
79 IRSOL_NAMED_LOG_DEBUG(m_id, "Serializing message: '{}'", irsol::protocol::toString(message));
80 auto serializedMessage = irsol::protocol::Serializer::serialize(std::move(message));
81 IRSOL_NAMED_LOG_DEBUG(m_id, "Serialized message: '{}'", serializedMessage.toString());
82
83 handleSerializedMessage(serializedMessage);
84}
85
86void
88 const protocol::internal::SerializedMessage& serializedMessage)
89{
90 // Send the serialized message to the client
91 if(serializedMessage.hasHeader()) {
92 send(serializedMessage.header);
93 }
94 if(serializedMessage.hasPayload()) {
95 send(serializedMessage.payload.data(), serializedMessage.payloadSize());
96 }
97}
98
99void
100ClientSession::processInMessageBuffer(std::string& messageBuffer)
101{
102 size_t newlinePos;
103
104 // Process all complete messages (ending with newline)
105 while((newlinePos = messageBuffer.find('\n')) != std::string::npos) {
106 // Extract the complete message
107 std::string rawMessage = messageBuffer.substr(0, newlinePos);
108
109 // Remove the processed message from the buffer
110 messageBuffer.erase(0, newlinePos + 1);
111
112 // Process the extracted message
113 processInRawMessage(rawMessage);
114 }
115}
116
117void
118ClientSession::processInRawMessage(const std::string& rawMessage)
119{
120 IRSOL_NAMED_LOG_DEBUG(m_id, "Processing raw message: '{}'", rawMessage);
121
122 // Strip the prefix 'bypass ' from the message
123 std::string processedMessage = utils::stripString(rawMessage, "bypass ");
124
125 // Extract the parsed message
126 std::optional<irsol::protocol::InMessage> optionalParsedMessage =
127 irsol::protocol::Parser::parse(processedMessage);
128 if(!optionalParsedMessage) {
129 IRSOL_NAMED_LOG_ERROR(m_id, "Failed to parse message: '{}'", rawMessage);
130 // TODO: ability to create error message without identifier for failed parsing
131 // handleOutMessage(irsol::protocol::Error());
132 return;
133 }
134
135 // Extract the InMessage from the optional wrapper and submit it the the message handler
136 auto result = app().messageHandler().handle(m_id, std::move(*optionalParsedMessage));
137 IRSOL_NAMED_LOG_DEBUG(m_id, "Received {} response(s)", result.size());
138
139 // lock the session's mutex to prevent race conditions
140 std::scoped_lock<std::mutex> lock(m_socketMutex);
141 for(auto& message : result) {
142 handleOutMessage(std::move(message));
143 }
144 IRSOL_NAMED_LOG_DEBUG(m_id, "Sent {} response(s) to client", result.size());
145}
146
147void
148ClientSession::send(const std::string& msg)
149{
150 std::string preparedMessage = msg;
151
152 IRSOL_NAMED_LOG_TRACE(m_id, "Sending message of size {}", preparedMessage.size());
153 if(!m_socket) {
155 m_id, "Socket seems to be closed, this is unexpected. Ignoring send request.");
156 return;
157 }
158
159 auto result = m_socket.write(preparedMessage);
160 if(!result) {
161 IRSOL_NAMED_LOG_ERROR(m_id, "Failed to send message: {}", result.error().message());
162 } else if(result.value() != preparedMessage.size()) {
164 m_id, "Incomplete message sent: {} of {} bytes", result.value(), preparedMessage.size());
165 }
166}
167
168void
169ClientSession::send(const irsol::types::byte_t* const data, size_t size)
170{
171 IRSOL_NAMED_LOG_TRACE(m_id, "Sending binary data of size {}", size);
172 if(!m_socket) {
174 m_id, "Socket seems to be closed, this is unexpected. Ignoring send request.");
175 return;
176 }
177
178 auto result = m_socket.write_n(data, size);
179 if(!result) {
180 IRSOL_NAMED_LOG_ERROR(m_id, "Failed to send binary data: {}", result.error().message());
181 } else if(result.value() != size) {
182 IRSOL_NAMED_LOG_WARN(m_id, "Incomplete binary data sent: {} of {} bytes", result.value(), size);
183 }
184}
185} // namespace server
186} // namespace irsol
Main server application managing client connections and camera streaming.
static std::optional< InMessage > parse(const std::string &line)
Attempts to parse a single protocol input line into a structured InMessage.
Definition parser.cpp:11
static internal::SerializedMessage serialize(OutMessage &&msg)
Serialize an irsol::protocol::OutMessage variant into a serialized protocol message.
Main server application that manages client connections and camera streaming.
Definition app.hpp:46
const handlers::MessageHandler & messageHandler() const
Accessor for the message handler.
Definition app.hpp:114
void run()
Starts the client's session logic.
Definition session.cpp:21
ClientSession(const irsol::types::client_id_t &id, irsol::types::socket_t &&sock, App &app)
Constructs a new ClientSession.
Definition session.cpp:16
irsol::types::socket_t m_socket
Socket used for communication with the client.
Definition session.hpp:198
void handleOutMessages(std::vector< protocol::OutMessage > &&messages)
Handles multiple outbound messages to the client.
Definition session.cpp:68
void processInMessageBuffer(std::string &messageBuffer)
Processes accumulated raw data into complete protocol messages.
Definition session.cpp:100
irsol::types::client_id_t m_id
Unique ID identifying this session (maps to client_id_t).
Definition session.hpp:195
void handleOutMessage(protocol::OutMessage &&message)
Handles a single outbound message to the client.
Definition session.cpp:77
std::mutex m_socketMutex
Mutex to synchronize socket access from multiple threads.
Definition session.hpp:201
const App & app() const
Returns a const reference to the owning App instance.
Definition session.hpp:104
void processInRawMessage(const std::string &rawMessage)
Parses and processes a complete incoming raw message.
Definition session.cpp:118
void handleSerializedMessage(const protocol::internal::SerializedMessage &serializedMessage)
Sends an already-serialized message to the client.
Definition session.cpp:87
void send(const std::string &message)
Sends a text message over the socket to the client.
Definition session.cpp:148
handling_function_response_t handle(const irsol::types::client_id_t &clientId, protocol::InMessage &&message) const
Dispatches an incoming message to the correct user-defined handler.
#define IRSOL_NAMED_LOG_INFO(name,...)
Logs an info-level message using a named logger.
Definition logging.hpp:176
#define IRSOL_NAMED_LOG_DEBUG(name,...)
Logs a debug-level message using a named logger.
Definition logging.hpp:174
#define IRSOL_NAMED_LOG_ERROR(name,...)
Logs an error-level message using a named logger.
Definition logging.hpp:180
#define IRSOL_NAMED_LOG_WARN(name,...)
Logs a warning-level message using a named logger.
Definition logging.hpp:178
#define IRSOL_NAMED_LOG_TRACE(name,...)
Logs a trace-level message using a named logger.
Definition logging.hpp:172
std::variant< Success, BinaryDataBuffer, ImageBinaryData, ColorImageBinaryData, Error > OutMessage
Variant type representing any outgoing message.
Definition variants.hpp:149
std::string toString(const InMessage &msg)
Converts an incoming message variant to a human-readable string.
Definition variants.cpp:19
Logging utilities and configuration for the irsol library.
Message routing layer between protocol and application logic.
sockpp::tcp_socket socket_t
Alias for a TCP socket.
Definition types.hpp:87
std::byte byte_t
Alias for a single byte used in serialization or binary data handling.
Definition types.hpp:165
std::string stripString(const std::string &s, const std::string &strippedString)
Removes all occurrences of a specific substring from the start and end of a string.
Definition utils.cpp:89
Parses raw protocol input strings into structured messages.
Declaration of the ClientSession class.
Represents a serialized protocol message with header and payload.
std::vector< irsol::types::byte_t > payload
The binary payload of the serialized message.
std::string header
The textual header of the serialized message.
bool hasHeader() const
Checks whether the serialized message contains a non-empty header.
size_t payloadSize() const
Returns the size of the payload in bytes.
bool hasPayload() const
Checks whether the serialized message contains a non-empty payload.
auto result
auto msg
General utility functions used throughout the irsol library.