TEC
A lightweight C++ library enabling safe, efficient execution in multithreaded and concurrent systems.
Loading...
Searching...
No Matches
tec_socket_client.hpp
Go to the documentation of this file.
1// Time-stamp: <Last changed 2026-02-07 14:13:52 by magnolia>
2/*----------------------------------------------------------------------
3------------------------------------------------------------------------
4Copyright (c) 2020-2026 The Emacs Cat (https://github.com/olddeuteronomy/tec).
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17------------------------------------------------------------------------
18----------------------------------------------------------------------*/
44#pragma once
45
46#include <cstddef>
47#ifndef _POSIX_C_SOURCE
48#define _POSIX_C_SOURCE 200809L // This line fixes the "storage size of ‘hints’ isn’t known" issue.
49#endif
50
51#include <cerrno>
52#include <cstdlib>
53#include <vector>
54
55#include <sys/types.h>
56#include <sys/socket.h>
57#include <netdb.h>
58
59#include "tec/tec_def.hpp" // IWYU pragma: keep
60#include "tec/tec_trace.hpp"
61#include "tec/tec_status.hpp"
62#include "tec/tec_message.hpp"
63#include "tec/tec_actor.hpp"
64#include "tec/tec_memfile.hpp"
66
67
68namespace tec {
69
91template <typename TParams>
92class SocketClient: public Actor {
93private:
96 std::vector<char> buffer_;
97
98public:
101 using Params = TParams;
102
103protected:
107
111
116 constexpr char* get_buffer() { return buffer_.data(); }
117
122 constexpr size_t get_buffer_size() { return params_.buffer_size; }
123
124public:
133 explicit SocketClient(const Params& params)
134 : Actor()
135 , params_{params}
136 , sockfd_{EOF}
137 {
138 static_assert(
139 std::is_base_of<SocketClientParams, Params>::value,
140 "Not derived from tec::SocketClientParams class");
141 }
142
148 virtual ~SocketClient() {
149 if (sockfd_ != EOF) {
150 terminate();
151 }
152 }
153
163 void start(Signal* sig_started, Status* status) override {
164 TEC_ENTER("SocketClient::start");
165 Signal::OnExit on_exit(sig_started);
166
167 // Resolve the server address.
168 TEC_TRACE("Resolving address {}:{}...", params_.addr, params_.port);
169 addrinfo hints;
170 ::memset(&hints, 0, sizeof(hints));
171 hints.ai_family = params_.family;
172 hints.ai_socktype = params_.socktype;
173 hints.ai_protocol = params_.protocol;
174
175 // getaddrinfo() returns a list of address structures.
176 // Try each address until we successfully connect().
177 addrinfo* servinfo{NULL};
178 char port_str[16];
179 ::snprintf(port_str, 15, "%d", params_.port);
180 int ecode = ::getaddrinfo(params_.addr.c_str(), port_str,
181 &hints, &servinfo);
182 if( ecode != 0 ) {
183 std::string emsg = ::gai_strerror(ecode);
184 TEC_TRACE("Error resolving address: {}", emsg);
185 *status = {ecode, emsg, Error::Kind::NetErr};
186 return;
187 }
188 TEC_TRACE("Address resolved OK.");
189
190 // If socket() (or connect()) fails, we close the socket
191 // and try the next address.
192 addrinfo* p{NULL};
193 int fd{-1};
194 TEC_TRACE("Connecting...");
195 for( p = servinfo; p != NULL; p = p->ai_next ) {
196 fd = ::socket(p->ai_family, p->ai_socktype, p->ai_protocol);
197 if( fd == -1 ) {
198 // Try next socket.
199 continue;
200 }
201
202 if( ::connect(fd, p->ai_addr, p->ai_addrlen) != -1 ) {
203 // Success.
204 break;
205 }
206
207 // Try next socket.
208 close(fd);
209 }
210
211 // No longer needed.
212 ::freeaddrinfo(servinfo);
213
214 if( p == NULL ) {
215 auto emsg = format("Failed to connect to {}:{}", params_.addr, params_.port);
216 *status = {ECONNREFUSED, emsg, Error::Kind::NetErr};
217 TEC_TRACE(emsg);
218 return;
219 }
220 //
221 // Success. Allocate buffer for send/recv operations.
222 //
223 sockfd_ = fd;
224 TEC_TRACE("Connected OK.");
225 buffer_.resize(get_buffer_size());
226 TEC_TRACE("Buffer size is {} bytes.", get_buffer_size());
227 }
228
237 void shutdown(Signal* sig_stopped) override {
238 TEC_ENTER("SocketClient::shutdown");
239 Signal::OnExit on_exit(sig_stopped);
240 ::shutdown(sockfd_, SHUT_RDWR);
241 close(sockfd_);
242 sockfd_ = EOF;
243 }
244
256 Status process_request(Request request, Reply reply) override {
257 TEC_ENTER("SocketClient::process_request");
258 if( request.type() == typeid(const SocketCharStreamIn*) ) {
259 // Process raw character stream.
260 const SocketCharStreamIn* req = std::any_cast<const SocketCharStreamIn*>(request);
261 SocketCharStreamOut* rep = nullptr;
262 if( reply.has_value() ) {
263 rep = std::any_cast<SocketCharStreamOut*>(reply);
264 }
265 return send_recv_string(req, rep);
266 }
267
268 // Process future NetData request. See `tec_socket_client_nd.hpp`
269 // that implements NetData processing.
271 }
272
284 Status request_str(const std::string* str_in, std::string* str_out) {
285 TEC_ENTER("SocketClient::request_str");
286 SocketCharStreamIn request{str_in};
287 SocketCharStreamOut reply{str_out};
288 auto status = send_recv_string(&request, &reply);
289 if (status && (str_out != nullptr)) {
290 *str_out = *reply.str;
291 }
292 return status;
293 }
294
295protected:
296
307 virtual Status set_socket_options(int sockfd) {
308 return {};
309 }
310
320 virtual Status send_string(const SocketCharStreamIn* request) {
321 TEC_ENTER("SocketClient::send_string");
322 if (request && request->str) {
323 // `request` must be valid.
324 Bytes data(request->str->data(), request->str->size() + 1);
325 Socket sock{sockfd_, params_.addr.c_str(), params_.port,
327 auto status = Socket::send(data, &sock);
328 return status;
329 }
330 return {EFAULT, Error::Kind::Invalid};
331 }
332
344 TEC_ENTER("SocketClient::recv_string");
345 if (reply == nullptr) {
346 // Notification message -- no reply required.
347 return {};
348 }
349 if (reply->str == nullptr) {
350 return {EFAULT, Error::Kind::Invalid};
351 }
352 Bytes data;
353 Socket sock{sockfd_, params_.addr.c_str(), params_.port,
355 auto status = Socket::recv(data, &sock, 0);
356 if (status) {
357 *reply->str = static_cast<const char*>(data.data());
358 }
359 return status;
360 }
361
373 TEC_ENTER("SocketClient::send_recv_string");
374 auto status = send_string(request);
375 if (status) {
376 status = recv_string(reply);
377 }
378 else {
379 terminate();
380 }
381 return status;
382 }
383
384}; // class SocketClient
385
386} // namespace tec
Abstract base class defining the actor lifecycle and request handling interface.
Definition tec_actor.hpp:63
virtual Status terminate()
Mimics Daemon's behavior.
Definition tec_actor.hpp:183
A byte buffer class with stream-like read/write semantics.
Definition tec_memfile.hpp:50
const void * data() const
Returns a const pointer to the buffer's data.
Definition tec_memfile.hpp:247
A thread-safe signal mechanism for inter-thread synchronization.
Definition tec_signal.hpp:44
Templated client socket actor for establishing and managing connections.
Definition tec_socket_client.hpp:92
virtual Status send_recv_string(const SocketCharStreamIn *request, SocketCharStreamOut *reply)
Sends a request and receives a reply in one operation.
Definition tec_socket_client.hpp:372
TParams Params
Type alias for the template parameter TParams. This allows easy reference to the params type within t...
Definition tec_socket_client.hpp:101
virtual Status recv_string(SocketCharStreamOut *reply)
Receives a string from the socket.
Definition tec_socket_client.hpp:343
Status request_str(const std::string *str_in, std::string *str_out)
Convenience method to send a string request and receive a response.
Definition tec_socket_client.hpp:284
constexpr size_t get_buffer_size()
Returns the size of the internal buffer.
Definition tec_socket_client.hpp:122
constexpr char * get_buffer()
Returns a pointer to the internal buffer.
Definition tec_socket_client.hpp:116
virtual Status send_string(const SocketCharStreamIn *request)
Sends a string over the socket.
Definition tec_socket_client.hpp:320
void start(Signal *sig_started, Status *status) override
Starts the client by resolving the address and establishing a connection.
Definition tec_socket_client.hpp:163
void shutdown(Signal *sig_stopped) override
Shuts down the client connection.
Definition tec_socket_client.hpp:237
Params params_
Instance of the parameters used for configuration. This holds settings like address,...
Definition tec_socket_client.hpp:106
int sockfd_
Socket file descriptor for the established connection. Initialized to EOF (-1) and set upon successfu...
Definition tec_socket_client.hpp:110
Status process_request(Request request, Reply reply) override
Processes incoming requests, handling SocketCharStreamIn types.
Definition tec_socket_client.hpp:256
virtual Status set_socket_options(int sockfd)
Virtual hook to set custom socket options after connection.
Definition tec_socket_client.hpp:307
virtual ~SocketClient()
Destructor that ensures the socket is terminated if still open.
Definition tec_socket_client.hpp:148
SocketClient(const Params &params)
Constructs a SocketClient with the given parameters.
Definition tec_socket_client.hpp:133
#define TEC_ENTER(name)
Logs an entry message for a named context (e.g., function).
Definition tec_trace.hpp:211
#define TEC_TRACE(...)
Logs a formatted trace message.
Definition tec_trace.hpp:222
@ Invalid
Invalid data or state.
@ NetErr
Network-related error.
@ NotImplemented
Not implemented.
Helper struct to signal termination on exit.
Definition tec_signal.hpp:110
Input descriptor for character stream mode.
Definition tec_socket.hpp:242
const std::string * str
Pointer to the received null-terminated string.
Definition tec_socket.hpp:244
Output descriptor for character stream mode.
Definition tec_socket.hpp:254
std::string * str
Pointer to the string that will receive the response.
Definition tec_socket.hpp:256
Lightweight wrapper around a connected socket file descriptor.
Definition tec_socket.hpp:272
static Status recv(Bytes &data, const Socket *sock, size_t length)
Receive data from a socket into a MemFile (Bytes).
Definition tec_socket.hpp:329
static Status send(const Bytes &data, const Socket *sock)
Send the entire contents of a MemFile (Bytes) through a socket.
Definition tec_socket.hpp:395
char addr[INET6_ADDRSTRLEN]
Peer address as a null-terminated string (IPv4 or IPv6).
Definition tec_socket.hpp:274
Core interface for TEC actors with lifecycle management and request processing.
Common definitions and utilities for the tec namespace.
A byte buffer class with stream-like read/write semantics.
Defines a flexible message type and helper functions for the tec namespace.
std::any Reply
Type alias for a reply object that can hold any object.
Definition tec_message.hpp:55
std::any Request
Type alias for a request object that can hold any object.
Definition tec_message.hpp:49
std::string format(const T &arg)
Formats a single argument into a string.
Definition tec_print.hpp:171
Generic BSD socket parameters and helpers.
Defines error handling types and utilities for the tec namespace.
Provides a thread-safe tracing utility for debugging in the tec namespace.