STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
NetworkSocket.cpp
Go to the documentation of this file.
2
3#include <arpa/inet.h>
4#include <fcntl.h>
5#include <netinet/in.h>
6#include <sys/socket.h>
7#include <unistd.h>
8
9#include <cerrno>
10#include <cstring>
11#include <iostream>
12
13namespace daq_comms {
14namespace transport {
15
16// UDP Socket Implementation
17UDPSocket::UDPSocket(const std::string& bind_address, uint16_t bind_port)
18 : socket_fd_(-1), is_bound_(true), remote_port_(0) {
19 socket_fd_ = socket(AF_INET, SOCK_DGRAM, 0);
20 if (socket_fd_ < 0) {
21 last_error_ = "Failed to create UDP socket: " + std::string(strerror(errno));
22 return;
23 }
24
25 // Set SO_REUSEADDR to allow multiple processes to bind to same port (like DiabloAvionics GUI)
26 int reuse = 1;
27 if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
28 // Non-fatal, but log it
29 std::cerr << "[UDPSocket] Warning: Failed to set SO_REUSEADDR: " << strerror(errno)
30 << std::endl;
31 }
32
33 struct sockaddr_in addr = {};
34 addr.sin_family = AF_INET;
35 addr.sin_port = htons(bind_port);
36
37 if (bind_address == "0.0.0.0" || bind_address.empty()) {
38 addr.sin_addr.s_addr = INADDR_ANY;
39 } else {
40 if (inet_aton(bind_address.c_str(), &addr.sin_addr) == 0) {
41 last_error_ = "Invalid bind address: " + bind_address;
42 close(socket_fd_);
43 socket_fd_ = -1;
44 return;
45 }
46 }
47
48 if (bind(socket_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
49 last_error_ = "Failed to bind UDP socket: " + std::string(strerror(errno));
50 close(socket_fd_);
51 socket_fd_ = -1;
52 return;
53 }
54
55 // Set socket to non-blocking for better control
56 int flags = fcntl(socket_fd_, F_GETFL, 0);
57 fcntl(socket_fd_, F_SETFL, flags | O_NONBLOCK);
58}
59
60UDPSocket::UDPSocket(const std::string& remote_address, uint16_t remote_port, bool is_sender,
61 const std::string& bind_address)
62 : socket_fd_(-1), is_bound_(false), remote_address_(remote_address), remote_port_(remote_port) {
63 socket_fd_ = socket(AF_INET, SOCK_DGRAM, 0);
64 if (socket_fd_ < 0) {
65 last_error_ = "Failed to create UDP socket: " + std::string(strerror(errno));
66 return;
67 }
68
69 // Pin the source address, and with it the egress interface. Bound on an ephemeral port: we
70 // are choosing a NIC, not a port. Non-fatal — an unpinned send still reaches a single-homed
71 // host — but the failure must not be silent.
72 if (!bind_address.empty() && bind_address != "0.0.0.0") {
73 struct sockaddr_in local;
74 memset(&local, 0, sizeof(local));
75 local.sin_family = AF_INET;
76 local.sin_port = 0;
77 if (inet_aton(bind_address.c_str(), &local.sin_addr) == 0) {
78 std::cerr << "[UDPSocket] Warning: bad bind address '" << bind_address << "'"
79 << std::endl;
80 } else if (bind(socket_fd_, reinterpret_cast<struct sockaddr*>(&local), sizeof(local)) <
81 0) {
82 std::cerr << "[UDPSocket] Warning: failed to bind sender to " << bind_address << ": "
83 << strerror(errno) << std::endl;
84 }
85 }
86
87 // Set socket to non-blocking
88 int flags = fcntl(socket_fd_, F_GETFL, 0);
89 fcntl(socket_fd_, F_SETFL, flags | O_NONBLOCK);
90}
91
93 if (socket_fd_ >= 0) {
94 close(socket_fd_);
95 }
96}
97
98ssize_t UDPSocket::receive(uint8_t* buffer, size_t max_size) {
99 if (socket_fd_ < 0) {
100 last_error_ = "Socket not initialized";
101 return -1;
102 }
103
104 ssize_t received = recvfrom(socket_fd_, buffer, max_size, 0, nullptr, nullptr);
105 if (received < 0) {
106 if (errno == EAGAIN || errno == EWOULDBLOCK) {
107 return 0; // No data available (non-blocking)
108 }
109 last_error_ = "Receive error: " + std::string(strerror(errno));
110 return -1;
111 }
112
113 return received;
114}
115
116ssize_t UDPSocket::receive_from(uint8_t* buffer, size_t max_size, std::string& source_address,
117 uint16_t& source_port) {
118 if (socket_fd_ < 0) {
119 last_error_ = "Socket not initialized";
120 return -1;
121 }
122
123 struct sockaddr_in addr = {};
124 socklen_t addr_len = sizeof(addr);
125
126 ssize_t received = recvfrom(socket_fd_, buffer, max_size, 0,
127 reinterpret_cast<struct sockaddr*>(&addr), &addr_len);
128 if (received < 0) {
129 if (errno == EAGAIN || errno == EWOULDBLOCK) {
130 return 0;
131 }
132 last_error_ = "Receive error: " + std::string(strerror(errno));
133 return -1;
134 }
135
136 source_address = inet_ntoa(addr.sin_addr);
137 source_port = ntohs(addr.sin_port);
138 return received;
139}
140
142 if (socket_fd_ < 0)
143 return false;
144 int opt = enable ? 1 : 0;
145 if (setsockopt(socket_fd_, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0) {
146 last_error_ = "Failed to set SO_BROADCAST: " + std::string(strerror(errno));
147 return false;
148 }
149 return true;
150}
151
152ssize_t UDPSocket::send_to(const std::string& dest_address, uint16_t dest_port, const uint8_t* data,
153 size_t size) {
154 if (socket_fd_ < 0) {
155 last_error_ = "Socket not initialized";
156 return -1;
157 }
158 struct sockaddr_in addr = {};
159 addr.sin_family = AF_INET;
160 addr.sin_port = htons(dest_port);
161 if (inet_aton(dest_address.c_str(), &addr.sin_addr) == 0) {
162 last_error_ = "Invalid destination: " + dest_address;
163 return -1;
164 }
165 ssize_t sent = ::sendto(socket_fd_, data, size, 0, reinterpret_cast<struct sockaddr*>(&addr),
166 sizeof(addr));
167 if (sent < 0) {
168 if (errno == EAGAIN || errno == EWOULDBLOCK)
169 return 0;
170 last_error_ = "sendto error: " + std::string(strerror(errno));
171 return -1;
172 }
173 return sent;
174}
175
176ssize_t UDPSocket::send(const uint8_t* data, size_t size) {
177 if (socket_fd_ < 0) {
178 last_error_ = "Socket not initialized";
179 return -1;
180 }
181
182 if (!is_bound_ && !remote_address_.empty()) {
183 // Send to specific remote address
184 struct sockaddr_in addr = {};
185 addr.sin_family = AF_INET;
186 addr.sin_port = htons(remote_port_);
187 if (inet_aton(remote_address_.c_str(), &addr.sin_addr) == 0) {
188 last_error_ = "Invalid remote address: " + remote_address_;
189 return -1;
190 }
191
192 ssize_t sent = sendto(socket_fd_, data, size, 0, reinterpret_cast<struct sockaddr*>(&addr),
193 sizeof(addr));
194 if (sent < 0) {
195 if (errno == EAGAIN || errno == EWOULDBLOCK) {
196 return 0;
197 }
198 last_error_ = "Send error: " + std::string(strerror(errno));
199 return -1;
200 }
201 return sent;
202 } else {
203 // Socket is bound, use send (shouldn't happen for UDP sender)
204 last_error_ = "Cannot send on bound UDP socket without destination";
205 return -1;
206 }
207}
208
210 return socket_fd_ >= 0;
211}
212
213std::string UDPSocket::last_error() const {
214 return last_error_;
215}
216
217// TCP Socket Implementation
218TCPSocket::TCPSocket(const std::string& remote_address, uint16_t remote_port) : socket_fd_(-1) {
219 setup_socket(remote_address, remote_port);
220}
221
223 if (socket_fd_ >= 0) {
224 close(socket_fd_);
225 }
226}
227
228void TCPSocket::setup_socket(const std::string& remote_address, uint16_t remote_port) {
229 socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
230 if (socket_fd_ < 0) {
231 last_error_ = "Failed to create TCP socket: " + std::string(strerror(errno));
232 return;
233 }
234
235 struct sockaddr_in addr = {};
236 addr.sin_family = AF_INET;
237 addr.sin_port = htons(remote_port);
238
239 if (inet_aton(remote_address.c_str(), &addr.sin_addr) == 0) {
240 last_error_ = "Invalid remote address: " + remote_address;
241 close(socket_fd_);
242 socket_fd_ = -1;
243 return;
244 }
245
246 if (connect(socket_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
247 last_error_ = "Failed to connect TCP socket: " + std::string(strerror(errno));
248 close(socket_fd_);
249 socket_fd_ = -1;
250 return;
251 }
252
253 // Enable TCP keepalive
254 int keepalive = 1;
255 setsockopt(socket_fd_, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
256}
257
258ssize_t TCPSocket::receive(uint8_t* buffer, size_t max_size) {
259 if (socket_fd_ < 0) {
260 last_error_ = "Socket not initialized";
261 return -1;
262 }
263
264 ssize_t received = recv(socket_fd_, buffer, max_size, 0);
265 if (received < 0) {
266 if (errno == EAGAIN || errno == EWOULDBLOCK) {
267 return 0;
268 }
269 last_error_ = "Receive error: " + std::string(strerror(errno));
270 return -1;
271 } else if (received == 0) {
272 last_error_ = "Connection closed by peer";
273 return -1;
274 }
275 return received;
276}
277
278ssize_t TCPSocket::send(const uint8_t* data, size_t size) {
279 if (socket_fd_ < 0) {
280 last_error_ = "Socket not initialized";
281 return -1;
282 }
283
284 ssize_t sent = ::send(socket_fd_, data, size, 0);
285 if (sent < 0) {
286 if (errno == EAGAIN || errno == EWOULDBLOCK) {
287 return 0;
288 }
289 last_error_ = "Send error: " + std::string(strerror(errno));
290 return -1;
291 }
292 return sent;
293}
294
296 return socket_fd_ >= 0;
297}
298
299std::string TCPSocket::last_error() const {
300 return last_error_;
301}
302
303} // namespace transport
304} // namespace daq_comms
static void enable(board_bringup_t *b, uint16_t device_id)
void setup_socket(const std::string &remote_address, uint16_t remote_port)
TCPSocket(const std::string &remote_address, uint16_t remote_port)
Create TCP socket connected to remote address.
ssize_t send(const uint8_t *data, size_t size) override
Send data through the socket.
std::string last_error() const override
Get the last error message.
ssize_t receive(uint8_t *buffer, size_t max_size) override
Receive data from the socket.
bool is_valid() const override
Check if socket is valid and ready.
std::string last_error() const override
Get the last error message.
ssize_t send_to(const std::string &dest_address, uint16_t dest_port, const uint8_t *data, size_t size)
Send UDP packet to specific destination (works on bound sockets).
UDPSocket(const std::string &bind_address, uint16_t bind_port)
Create UDP socket bound to local address.
bool is_valid() const override
Check if socket is valid and ready.
bool set_broadcast(bool enable)
Enable broadcast (required before send_to to 255.255.255.255).
ssize_t receive(uint8_t *buffer, size_t max_size) override
Receive data from the socket.
ssize_t receive_from(uint8_t *buffer, size_t max_size, std::string &source_address, uint16_t &source_port)
Receive with source address information.
ssize_t send(const uint8_t *data, size_t size) override
Send data through the socket.