scc 2026.07
SystemC components library
server.h
1/*******************************************************************************
2 * Copyright 2026 MINRES Technologies GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16
17#ifndef TLM_SCC_TCP4TLM_SERVER
18#define TLM_SCC_TCP4TLM_SERVER
19
20#include "i_server.h"
21#include <boost/lexical_cast.hpp>
22#include <boost/thread.hpp>
23#include <util/logging.h>
24
25namespace scc {
26namespace tcp4tlm {
27
28template <typename REQ, typename RESP> class server : public i_server<REQ, RESP> {
29 typedef typename std::shared_ptr<connection<RESP, REQ>> con_ptr;
30 typedef typename connection<RESP, REQ>::async_listener con_listener;
31
32 struct forward_session : public con_listener {
33 forward_session(i_server<REQ, RESP>* server_)
34 : server_instance(server_)
35 , conn_shptr(new connection<RESP, REQ>(server_instance->get_io_service())) {}
36
37 virtual ~forward_session() {}
38
39 con_ptr& get_connection() { return conn_shptr; }
40
41 bool start() {
42 std::shared_ptr<con_listener> ptr = std::enable_shared_from_this<con_listener>::shared_from_this();
43 conn_shptr->add_listener(ptr);
44 typename connection<RESP, REQ>::endpoint_t endpoint = conn_shptr->socket().remote_endpoint();
45 CPPLOG(TRACE, "tcp4tlm::server") << "forward_session::start(), got connected";
46 server_instance->server_send_completed(conn_shptr, true);
47 return server_instance->is_shutdown_requested();
48 }
49
50 void receive_completed(const boost::system::error_code& e, const REQ* const data) {
51 if(e.value() == 2) {
52 CPPLOG(WARN, "tcp4tlm::server") << "Client closed connection (" << e.message() << ")";
53 return;
54 } else if(e) {
55 CPPLOG(ERR, "tcp4tlm::server") << "Communication error (" << e.message() << ")";
56 return;
57 }
58 server_instance->server_receive_completed(conn_shptr, data);
59 }
60
61 void send_completed(const boost::system::error_code& e) {
62 if(!e) {
63 server_instance->server_send_completed(conn_shptr);
64 } else {
65 CPPLOG(ERR, "tcp4tlm::server") << e.message() << "(" << e << ")";
66 }
67 }
68
69 private:
70 i_server<REQ, RESP>* server_instance;
71 con_ptr conn_shptr;
72 };
73
74 friend struct forward_session;
75
76public:
77 server(std::size_t thread_pool_size = 4)
78 : thread_pool_size(thread_pool_size)
79 , io_service()
80 , io_service_work()
81 , acceptor()
82 , server_running(false)
83 , shutdown_requested(false) {}
84
85 virtual ~server() {}
86
87#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
88 long set_acceptor_endpoint(std::shared_ptr<boost::asio::local::stream_protocol::acceptor>& a, unsigned short port);
89#endif
90 long set_acceptor_endpoint(std::shared_ptr<boost::asio::ip::tcp::acceptor>& a, unsigned short port);
91
92 void start_server(unsigned short port, char* name = NULL);
93
94 void request_shutdown() {
95 if(!shutdown_requested) {
96 shutdown_requested = true;
97 io_service.stop();
98 }
99 }
100
101 bool is_shutdown_requested() { return shutdown_requested; }
102
103 void shutdown_server();
104
105 virtual boost::asio::io_context& get_io_service() { return io_service; }
106
107 bool is_server_running() { return server_running; }
108
109 void create_new_session() {
110 std::shared_ptr<forward_session> forward_session_ptr(new forward_session(this));
111 acceptor->async_accept(
112 forward_session_ptr->get_connection()->socket(),
113 [this, forward_session_ptr](const boost::system::error_code& error) { handle_accept(error, forward_session_ptr); });
114 }
115
116protected:
117 const typename connection<RESP, REQ>::acceptor_t& get_acceptor() { return *acceptor; };
118
119private:
120 void handle_accept(const boost::system::error_code& e, std::shared_ptr<forward_session> session) {
121 if(!e) {
122 if(!session->start())
123 create_new_session();
124 } else {
125 CPPLOG(ERR, "tcp4tlm::server") << e.message();
126 }
127 }
128
129 std::size_t thread_pool_size;
130 boost::asio::io_context io_service;
131 std::shared_ptr<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> io_service_work;
132 std::shared_ptr<typename connection<RESP, REQ>::acceptor_t> acceptor;
133 boost::thread_group threads;
134 bool server_running;
135 bool shutdown_requested;
136};
137
138#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
139template <typename REQ, typename RESP>
140long server<REQ, RESP>::set_acceptor_endpoint(std::shared_ptr<boost::asio::local::stream_protocol::acceptor>& a, unsigned short port) {
141 std::string s("/tmp/server");
142 s += boost::lexical_cast<std::string>(port ? port : getpid());
143 boost::asio::local::stream_protocol::endpoint endpoint(s);
144 std::remove(endpoint.path().c_str());
145 acceptor.reset(new typename connection<RESP, REQ>::acceptor_t(io_service));
146 acceptor->open(endpoint.protocol());
147 acceptor->bind(endpoint);
148 return port;
149}
150#endif
151
152template <typename REQ, typename RESP>
153long server<REQ, RESP>::set_acceptor_endpoint(std::shared_ptr<boost::asio::ip::tcp::acceptor>& a, unsigned short port) {
154 unsigned short retry_count = 128;
155 bool connected = false;
156 do {
157 try {
158 boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
159 acceptor.reset(new typename connection<RESP, REQ>::acceptor_t(io_service));
160 acceptor->open(endpoint.protocol());
161 acceptor->set_option(boost::asio::ip::tcp::no_delay(true));
162 acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
163 acceptor->bind(endpoint);
164 connected = true;
165 } catch(std::exception& ex) {
166 port++;
167 retry_count--;
168 CPPLOG(DEBUG, "tcp4tlm::server") << "Got '" << ex.what() << "', retrying with port " << port;
169 }
170 } while(!connected && retry_count > 0 && port <= 0xffff);
171 return connected ? port : -1;
172}
173
174template <typename REQ, typename RESP> void server<REQ, RESP>::start_server(unsigned short port, char* name) {
175 if(server_running)
176 return;
177 CPPLOG(TRACE, "tcp4tlm::server") << "starting tcp server";
178 io_service_work.reset(new boost::asio::executor_work_guard<boost::asio::io_context::executor_type>(io_service.get_executor()));
179 for(std::size_t i = 0; i < thread_pool_size; ++i) {
180 threads.create_thread([this]() { io_service.run(); });
181 }
182 long actual_port = set_acceptor_endpoint(acceptor, port);
183 if(actual_port < 0)
184 throw new std::runtime_error(std::string("Could not open socket!"));
185 if(actual_port != (long)port) {
186 CPPLOG(INFO, "tcp4tlm::server") << "started the listener on " << boost::asio::ip::host_name() << ":" << actual_port
187 << " (instead of port " << port << ")";
188 } else {
189 CPPLOG(INFO, "tcp4tlm::server") << "started the listener on " << boost::asio::ip::host_name() << ":" << port;
190 }
191 acceptor->listen();
192 create_new_session();
193 server_running = true;
194}
195
196template <typename REQ, typename RESP> void server<REQ, RESP>::shutdown_server() {
197 CPPLOG(TRACE, "tcp4tlm::server") << "shutting down tcp server";
198 io_service.stop();
199 io_service_work.reset();
200 threads.interrupt_all();
201 threads.join_all();
202 server_running = false;
203}
204
205} // namespace tcp4tlm
206} // namespace scc
207
208#endif // TLM_SCC_TCP4TLM_SERVER
The connection class provides FlatBuffers framing primitives on top of a socket.
SCC TLM utilities.