blob: c9894e42591005671e1ef9aae0e2a8d6b2fa40ea [file] [log] [blame]
jwendelil421d169f2020-03-23 18:03:22 +01001/*
2 * TCPSelectServer.cpp
3 *
4 * Author: wendel
5 */
6
Thomas Psota92ce6482020-07-10 12:30:43 +02007#include <BaSyx/server/TCPSelectServer.h>
8#include <BaSyx/vab/backend//connector/native/frame/Frame.h>
jwendelil421d169f2020-03-23 18:03:22 +01009#include <BaSyx/vab/provider/native/frame/BaSyxNativeFrameProcessor.h>
Constantin Ziesche02817f12020-08-04 21:40:43 +020010#include <BaSyx/abstraction/net/Buffer.h>
jwendelil421d169f2020-03-23 18:03:22 +010011
12#include <errno.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17
18#include <chrono>
19
20namespace basyx {
21namespace vab {
22namespace provider {
23namespace native {
24
Thomas Psota92ce6482020-07-10 12:30:43 +020025TCPSelectServer::TCPSelectServer(core::IModelProvider* backend, int port, int timeout_ms, int listen_backlog)
jwendelil421d169f2020-03-23 18:03:22 +010026 : port(port)
Thomas Psota92ce6482020-07-10 12:30:43 +020027 , initialized(false)
28 , backend(backend)
29 , log("TCPSelectServer")
30 , listen_backlog(listen_backlog)
jwendelil421d169f2020-03-23 18:03:22 +010031{
Thomas Psota92ce6482020-07-10 12:30:43 +020032 frame_processor = std::unique_ptr<frame::BaSyxNativeFrameProcessor>(new frame::BaSyxNativeFrameProcessor(backend));
33 timeout.tv_sec = timeout_ms / 1000;
34 timeout.tv_usec = (timeout_ms % 1000) * 1000;
jwendelil421d169f2020-03-23 18:03:22 +010035}
36
37TCPSelectServer::~TCPSelectServer()
38{
Thomas Psota92ce6482020-07-10 12:30:43 +020039 this->clean_up();
jwendelil421d169f2020-03-23 18:03:22 +010040}
41
42void TCPSelectServer::Init()
43{
Thomas Psota92ce6482020-07-10 12:30:43 +020044 int rc, on = 1;
45 // create socket to accept incoming connections
46 listen_sd = socket(AF_INET, SOCK_STREAM, 0);
47 if (listen_sd < 0) {
48 log.error("socket() failed");
49 exit(-1);
50 }
jwendelil421d169f2020-03-23 18:03:22 +010051
Thomas Psota92ce6482020-07-10 12:30:43 +020052 // set socket reusable
53 int setsocketopt_state = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
54 if (setsocketopt_state < 0) {
55 log.error("setsockopt() failed");
56 close(listen_sd);
57 exit(-1);
58 }
jwendelil421d169f2020-03-23 18:03:22 +010059
Thomas Psota92ce6482020-07-10 12:30:43 +020060 // Set socket to nonblocking state (FIONBIO -> non-blocking io)
61 int ioctl_state = ioctl(listen_sd, FIONBIO, (char*)&on);
62 if (ioctl_state < 0) {
63 log.error("ioctl() failed");
64 close(listen_sd);
65 exit(-1);
66 }
jwendelil421d169f2020-03-23 18:03:22 +010067
Thomas Psota92ce6482020-07-10 12:30:43 +020068 // bind socket
69 memset(&addr, 0, sizeof(addr));
70 addr.sin_family = AF_INET;
71 memset(&addr.sin_addr, INADDR_ANY, sizeof(INADDR_ANY));
72 //memcpy(&addr.sin_addr, INADDR_ANY, sizeof(INADDR_ANY));
73 addr.sin_port = htons(port);
jwendelil421d169f2020-03-23 18:03:22 +010074
Thomas Psota92ce6482020-07-10 12:30:43 +020075 int bind_state = bind(listen_sd, (struct sockaddr*)&addr, sizeof(addr));
76 if (bind_state < 0) {
77 log.error("bind() failed");
78 close(listen_sd);
79 exit(-1);
80 }
jwendelil421d169f2020-03-23 18:03:22 +010081
Thomas Psota92ce6482020-07-10 12:30:43 +020082 int listen_state = listen(listen_sd, listen_backlog);
83 if (listen_state < 0) {
84 log.error("listen() failed");
85 close(listen_sd);
86 exit(-1);
87 }
jwendelil421d169f2020-03-23 18:03:22 +010088
Thomas Psota92ce6482020-07-10 12:30:43 +020089 //init master filedescriptor
90 FD_ZERO(&master_set);
91 max_socket = listen_sd;
92 FD_SET(listen_sd, &master_set);
jwendelil421d169f2020-03-23 18:03:22 +010093
Thomas Psota92ce6482020-07-10 12:30:43 +020094 log.info("Select server initialized. Listen socket-descriptor({})", listen_sd);
95 this->initialized = true;
jwendelil421d169f2020-03-23 18:03:22 +010096}
97
98int TCPSelectServer::Update()
99{
Thomas Psota92ce6482020-07-10 12:30:43 +0200100 if (not initialized)
101 log.warn("Select server not initialized");
jwendelil421d169f2020-03-23 18:03:22 +0100102
Thomas Psota92ce6482020-07-10 12:30:43 +0200103 int rc;
104 fd_set working_set;
jwendelil421d169f2020-03-23 18:03:22 +0100105
Thomas Psota92ce6482020-07-10 12:30:43 +0200106 // copy filedescriptor
107 memcpy(&working_set, &master_set, sizeof(master_set));
jwendelil421d169f2020-03-23 18:03:22 +0100108
Thomas Psota92ce6482020-07-10 12:30:43 +0200109 log.info("Waiting on select()...");
110 rc = select(max_socket + 1, &working_set, nullptr, nullptr, &timeout);
jwendelil421d169f2020-03-23 18:03:22 +0100111
Thomas Psota92ce6482020-07-10 12:30:43 +0200112 // check select state
113 if (rc < 0) {
114 log.error("select() failed");
115 return -1;
jwendelil421d169f2020-03-23 18:03:22 +0100116 }
Thomas Psota92ce6482020-07-10 12:30:43 +0200117 if (rc == 0) {
118 log.info("select() timed out. End program.");
119 return -2;
120 }
121
122 desc_ready = rc;
123 // check which descriptors are readable
124 for (int fd = 0; fd <= max_socket && desc_ready > 0; ++fd) {
125 // Check readiness of descriptors
126 if (FD_ISSET(fd, &working_set)) {
127 // decrease number of readable descriptors, if all found stop looking for them
128 desc_ready -= 1;
129
130 if (fd == listen_sd)
131 this->accept_incoming_connections();
132 else //if not listen socket, socket should be readable
133 {
134 log.info("Descriptor {} is readable", fd);
135 close_connection = false;
136 this->receive_incoming_data(fd);
137
138 // if connection is closed, clean up
139 if (close_connection) {
140 close(fd);
141 log.info("Connection {} closed", fd);
142 FD_CLR(fd, &master_set);
143 if (fd == max_socket) {
144 while (FD_ISSET(max_socket, &master_set) == false) {
145 max_socket -= 1;
146 }
147 }
148 }
149 }
150 }
151 }
152 return 0;
jwendelil421d169f2020-03-23 18:03:22 +0100153}
154
155void TCPSelectServer::Close()
156{
Thomas Psota92ce6482020-07-10 12:30:43 +0200157 this->clean_up();
jwendelil421d169f2020-03-23 18:03:22 +0100158}
159
160bool TCPSelectServer::isRunning()
161{
Thomas Psota92ce6482020-07-10 12:30:43 +0200162 log.warn("Not implemented!");
163 return false;
jwendelil421d169f2020-03-23 18:03:22 +0100164}
165
166void TCPSelectServer::clean_up()
167{
Thomas Psota92ce6482020-07-10 12:30:43 +0200168 for (int i = 0; i <= max_socket; ++i) {
169 if (FD_ISSET(i, &master_set)) {
170 close(i);
171 }
jwendelil421d169f2020-03-23 18:03:22 +0100172 }
jwendelil421d169f2020-03-23 18:03:22 +0100173}
174
175void TCPSelectServer::accept_incoming_connections()
176{
Thomas Psota92ce6482020-07-10 12:30:43 +0200177 log.info("Listening socket is readable");
178 int new_sd;
179 do {
180 // accept incoming connections
181 new_sd = accept(listen_sd, nullptr, nullptr);
182 if (new_sd < 0) {
183 // if not EWOULDBLOCK -> all incomminng connections are accepted
184 if (errno != EWOULDBLOCK) {
185 log.error("accept() failed");
186 end_server = true;
187 }
188 break;
189 }
jwendelil421d169f2020-03-23 18:03:22 +0100190
Thomas Psota92ce6482020-07-10 12:30:43 +0200191 log.info("New incoming connection - {}", new_sd);
jwendelil421d169f2020-03-23 18:03:22 +0100192
Thomas Psota92ce6482020-07-10 12:30:43 +0200193 // add incoming connections to master read set
194 FD_SET(new_sd, &master_set);
195 if (new_sd > max_socket) {
196 max_socket = new_sd;
197 }
198 } while (new_sd != -1);
jwendelil421d169f2020-03-23 18:03:22 +0100199}
200
201void TCPSelectServer::receive_incoming_data(int fd)
202{
Thomas Psota92ce6482020-07-10 12:30:43 +0200203 do {
204 // receive data
205 int receive_state = recv(fd, recv_buffer.data(), recv_buffer.size(), 0);
206 if (receive_state < 0) {
207 log.debug("receive state {}", receive_state);
208 if (errno != EWOULDBLOCK) {
209 log.error("recv() failed {}", errno);
210 close_connection = true;
211 }
212 break;
213 }
jwendelil421d169f2020-03-23 18:03:22 +0100214
Thomas Psota92ce6482020-07-10 12:30:43 +0200215 // if 0, client closed connection
216 if (receive_state == 0) {
217 log.info("Connection closed");
218 close_connection = true;
219 break;
220 }
jwendelil421d169f2020-03-23 18:03:22 +0100221
Thomas Psota92ce6482020-07-10 12:30:43 +0200222 int len = receive_state;
223 log.info("{} bytes received", len);
jwendelil421d169f2020-03-23 18:03:22 +0100224
Constantin Ziesche02817f12020-08-04 21:40:43 +0200225 // Determine input frame size and create frame
226 auto input_size = *reinterpret_cast<uint32_t*>(recv_buffer.data());
227 auto input_frame = connector::native::Frame::read_from_buffer(basyx::net::make_buffer(this->recv_buffer.data() + BASYX_FRAMESIZE_SIZE, input_size));
jwendelil421d169f2020-03-23 18:03:22 +0100228
Constantin Ziesche02817f12020-08-04 21:40:43 +0200229 // Process frame to obtain output frame
230 auto output_frame = frame_processor->processInputFrame(input_frame);
jwendelil421d169f2020-03-23 18:03:22 +0100231
Constantin Ziesche02817f12020-08-04 21:40:43 +0200232 // Write the output frame to an output buffer
233 net::Buffer out_buffer = basyx::net::make_buffer(send_buffer.data() + BASYX_FRAMESIZE_SIZE, default_buffer_size - BASYX_FRAMESIZE_SIZE);
234 connector::native::Frame::write_to_buffer(out_buffer, output_frame);
Thomas Psota92ce6482020-07-10 12:30:43 +0200235
Constantin Ziesche02817f12020-08-04 21:40:43 +0200236 // Add size of the containing frame to buffer
237 auto size_field = reinterpret_cast<uint32_t*>(&send_buffer[0]);
238 *size_field = output_frame.size();
Thomas Psota92ce6482020-07-10 12:30:43 +0200239
240 // answer client
Constantin Ziesche02817f12020-08-04 21:40:43 +0200241 int send_state = send(fd, send_buffer.data(), output_frame.size() + BASYX_FRAMESIZE_SIZE, 0);
242 if (send_state < 0)
243 {
244 log.error("send() failed");
245 close_connection = true;
246 break;
Thomas Psota92ce6482020-07-10 12:30:43 +0200247 }
248
249 } while (true);
jwendelil421d169f2020-03-23 18:03:22 +0100250}
251
252}
253}
254}
255}