CARLA
Loading...
Searching...
No Matches
ServerSession.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
9
10#include "carla/Debug.h"
11#include "carla/Logging.h"
12
13#include <boost/asio/read.hpp>
14#include <boost/asio/write.hpp>
15#include <boost/asio/bind_executor.hpp>
16#include <boost/asio/post.hpp>
17
18#include <atomic>
19#include <chrono>
20#include <thread>
21
22namespace carla {
23namespace streaming {
24namespace detail {
25namespace tcp {
26
27 static std::atomic_size_t SESSION_COUNTER{0u};
28
30 boost::asio::io_context &io_context,
31 const time_duration timeout,
32 Server &server)
34 std::string("tcp server session ") + std::to_string(SESSION_COUNTER)),
35 _server(server),
36 _session_id(SESSION_COUNTER++),
37 _socket(io_context),
38 _timeout(timeout),
39 _deadline(io_context, std::chrono::steady_clock::time_point::max()),
40 _strand(io_context) {}
41
43 callback_function_type on_opened,
44 callback_function_type on_closed) {
45 DEBUG_ASSERT(on_opened && on_closed);
46 _on_closed = std::move(on_closed);
47
48 // This forces not using Nagle's algorithm.
49 // Improves the sync mode velocity on Linux by a factor of ~3.
50 const boost::asio::ip::tcp::no_delay option(true);
51 _socket.set_option(option);
52
53 StartTimer();
54 auto self = shared_from_this(); // To keep myself alive.
55 boost::asio::post(_strand, [=]() {
56
57 auto handle_query = [this, self, callback=std::move(on_opened)](
58 const boost::system::error_code &ec,
59 size_t DEBUG_ONLY(bytes_received)) {
60 if (!ec) {
61 DEBUG_ASSERT_EQ(bytes_received, sizeof(_stream_id));
62 log_debug("session", _session_id, "for stream", _stream_id, " started");
63 boost::asio::post(_strand.context(), [=]() { callback(self); });
64 } else {
65 log_error("session", _session_id, ": error retrieving stream id :", ec.message());
66 CloseNow(ec);
67 }
68 };
69
70 // Read the stream id.
71 _deadline.expires_after(_timeout.to_chrono());
72 boost::asio::async_read(
73 _socket,
74 boost::asio::buffer(&_stream_id, sizeof(_stream_id)),
75 boost::asio::bind_executor(_strand, handle_query));
76 });
77 }
78
79 void ServerSession::Write(std::shared_ptr<const Message> message) {
80 DEBUG_ASSERT(message != nullptr);
81 DEBUG_ASSERT(!message->empty());
82 auto self = shared_from_this();
83 if (!_socket.is_open()) {
84 return;
85 }
86 if (_is_writing) {
88 // wait until previous message has been sent
89 while (_is_writing) {
90 std::this_thread::sleep_for(std::chrono::microseconds(100));
91 }
92 } else {
93 // ignore this message
94 log_debug("session", _session_id, ": connection too slow: message discarded");
95 return;
96 }
97 }
98 _is_writing = true;
99
100 auto handle_sent = [this, self, message](const boost::system::error_code &ec, size_t DEBUG_ONLY(bytes)) {
101 _is_writing = false;
102 if (ec) {
103 log_info("session", _session_id, ": error sending data :", ec.message());
104 CloseNow(ec);
105 } else {
106 DEBUG_ONLY(log_debug("session", _session_id, ": successfully sent", bytes, "bytes"));
107 DEBUG_ASSERT_EQ(bytes, sizeof(message_size_type) + message->size());
108 }
109 };
110
111 log_debug("session", _session_id, ": sending message of", message->size(), "bytes");
112
113 _deadline.expires_after(_timeout.to_chrono());
114 boost::asio::async_write(_socket, message->GetBufferSequence(),
115 boost::asio::bind_executor(_strand, handle_sent));
116 }
117
119 boost::asio::post(_strand, [self=shared_from_this()]() { self->CloseNow(); });
120 }
121
123 if (_deadline.expiry() <= std::chrono::steady_clock::now()) {
124 log_debug("session", _session_id, "timed out");
125 Close();
126 } else {
127 _deadline.async_wait([this, self=shared_from_this()](boost::system::error_code ec) {
128 if (!ec) {
129 StartTimer();
130 } else {
131 log_debug("session", _session_id, "timed out error:", ec.message());
132 }
133 });
134 }
135 }
136
137 void ServerSession::CloseNow(boost::system::error_code ec) {
138 if (_is_closed.exchange(true)) return;
139 _deadline.cancel();
140 if (!ec)
141 {
142 if (_socket.is_open()) {
143 boost::system::error_code ec2;
144 _socket.shutdown(boost::asio::socket_base::shutdown_both, ec2);
145 _socket.close();
146 }
147 }
148 _on_closed(shared_from_this());
149 log_debug("session", _session_id, "closed");
150 }
151
152} // namespace tcp
153} // namespace detail
154} // namespace streaming
155} // namespace carla
#define DEBUG_ASSERT(predicate)
Definition Debug.h:66
#define DEBUG_ASSERT_EQ(lhs, rhs)
Definition Debug.h:76
#define DEBUG_ONLY(code)
Definition Debug.h:55
#define LIBCARLA_INITIALIZE_LIFETIME_PROFILER(display_name)
void CloseNow(boost::system::error_code ec=boost::system::error_code())
void Close()
Post a job to close the session.
ServerSession(boost::asio::io_context &io_context, time_duration timeout, Server &server)
void Open(callback_function_type on_opened, callback_function_type on_closed)
Starts the session and calls on_opened after successfully reading the stream id, and on_closed once t...
boost::asio::io_context::strand _strand
std::function< void(std::shared_ptr< ServerSession >)> callback_function_type
void Write(std::shared_ptr< const Message > message)
Writes some data to the socket.
Positive time duration up to milliseconds resolution.
Definition Time.h:20
constexpr auto to_chrono() const
Definition Time.h:54
static std::atomic_size_t SESSION_COUNTER
This file contains definitions of common data structures used in traffic manager.
static void log_error(Args &&... args)
Definition Logging.h:110
static void log_info(Args &&... args)
Definition Logging.h:82
static void log_debug(Args &&... args)
Definition Logging.h:68