-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
com-sockets.cpp
272 lines (232 loc) · 7.41 KB
/
com-sockets.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include <atomic>
#include <errno.h>
#include <cstring>
#ifdef ESP32
#include <Arduino.h>
#elif defined(__MINGW32__)
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <poll.h>
#endif
#include <unistd.h>
#if !defined(__MINGW32__)
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#endif
#include "com-sockets.h"
#include "log.h"
#include "utils.h"
com_sockets::com_sockets(const std::string & listen_ip, const int listen_port, std::atomic_bool *const stop):
com(stop),
listen_ip(listen_ip),
listen_port(listen_port)
{
}
bool com_sockets::begin()
{
// setup listening socket for viewers
listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#if defined(__MINGW32__)
if (listen_fd == INVALID_SOCKET) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to create socket: %d", WSAGetLastError());
return false;
}
#else
if (listen_fd == -1) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to create socket: %s", strerror(errno));
return false;
}
#endif
int reuse_addr = 1;
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&reuse_addr), sizeof reuse_addr) == -1) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to set socket to reuse address: %s", strerror(errno));
return false;
}
#if !defined(ARDUINO) && !defined(__MINGW32__)
int q_size = SOMAXCONN;
#ifdef linux
if (setsockopt(listen_fd, SOL_TCP, TCP_FASTOPEN, &q_size, sizeof q_size)) {
#else
if (setsockopt(listen_fd, IPPROTO_TCP, TCP_FASTOPEN, &q_size, sizeof q_size)) {
#endif
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to set \"TCP fast open\": %s", strerror(errno));
return false;
}
#endif
sockaddr_in server_addr { };
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(listen_port);
#if !defined(__MINGW32__)
if (inet_aton(listen_ip.c_str(), &reinterpret_cast<sockaddr_in *>(&server_addr)->sin_addr) == 0) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to translate listen address (%s): %s", listen_ip.c_str(), strerror(errno));
return false;
}
#else
if (inet_pton(AF_INET, listen_ip.c_str(), &reinterpret_cast<sockaddr_in *>(&server_addr)->sin_addr) == 0) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to translate listen address (%s): %s", listen_ip.c_str(), strerror(errno));
return false;
}
#endif
if (bind(listen_fd, reinterpret_cast<sockaddr *>(&server_addr), sizeof server_addr) == -1) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to bind socket to %s:%d: %s", listen_ip.c_str(), listen_port, strerror(errno));
return false;
}
if (listen(listen_fd, 4) == -1) {
DOLOG(logging::ll_error, "com_sockets::begin", get_local_address(), "failed to setup listen queue: %s", strerror(errno));
return false;
}
return true;
}
com_sockets::~com_sockets()
{
close(listen_fd);
}
com_client *com_sockets::accept()
{
struct pollfd fds[] { { listen_fd, POLLIN, 0 } };
#if !defined(__MINGW32__)
for(;;) {
int rc = poll(fds, 1, 100);
if (rc == -1) {
DOLOG(logging::ll_error, "com_sockets::accept", get_local_address(), "poll failed with error %s", strerror(errno));
return nullptr;
}
if (rc >= 1)
break;
if (*stop) {
DOLOG(logging::ll_info, "com_sockets::accept", get_local_address(), "stop flag set");
return nullptr;
}
}
#endif
int fd = ::accept(listen_fd, nullptr, nullptr);
if (fd == -1) {
DOLOG(logging::ll_error, "com_sockets::accept", get_local_address(), "accept failed: %s", strerror(errno));
return nullptr;
}
return new com_client_sockets(fd, stop);
}
com_client_sockets::com_client_sockets(const int fd, std::atomic_bool *const stop): com_client(stop), fd(fd)
{
socket_set_nodelay(fd);
}
com_client_sockets::~com_client_sockets()
{
close(fd);
}
bool com_client_sockets::send(const uint8_t *const from, const size_t n)
{
#if defined(__MINGW32__)
size_t offset = 0;
size_t todo = n;
while(todo) {
int n_written = ::send(fd, reinterpret_cast<const char *>(&from[offset]), todo, 0);
if (n_written == -1) {
DOLOG(logging::ll_error, "com_client_sockets::send", get_endpoint_name(), "send failed with error %s", strerror(errno));
break;
}
if (n_written == 0) {
DOLOG(logging::ll_info, "com_client_sockets::send", get_endpoint_name(), "socket closed");
break;
}
offset += n_written;
todo -= n_written;
}
return todo == 0;
#else
auto rc = WRITE(fd, from, n);
if (rc == -1)
DOLOG(logging::ll_error, "com_client_sockets::send", "-", "write failed with error %s", strerror(errno));
return rc == ssize_t(n);
#endif
}
bool com_client_sockets::recv(uint8_t *const to, const size_t n)
{
#if !defined(__MINGW32__)
pollfd fds[] { { fd, POLLIN, 0 } };
#endif
size_t offset = 0;
size_t todo = n;
while(todo > 0) {
#if defined(__MINGW32__)
int rc = 1; // uggly hack
#else
int rc = poll(fds, 1, 100);
if (rc == -1) {
DOLOG(logging::ll_error, "com_client_sockets::recv", get_endpoint_name(), "poll failed with error %s", strerror(errno));
return false;
}
if (*stop == true) {
DOLOG(logging::ll_info, "com_client_sockets::recv", get_endpoint_name(), "abort due external stop");
return false;
}
#endif
if (rc >= 1) {
#if defined(__MINGW32__)
int n_read = ::recv(fd, reinterpret_cast<char *>(&to[offset]), todo, 0);
#else
int n_read = read(fd, &to[offset], todo);
#endif
if (n_read == -1) {
DOLOG(logging::ll_error, "com_client_sockets::recv", get_endpoint_name(), "read failed with error %s", strerror(errno));
return false;
}
if (n_read == 0) {
DOLOG(logging::ll_info, "com_client_sockets::recv", get_endpoint_name(), "socket closed");
return false;
}
offset += n_read;
todo -= n_read;
}
}
return todo == 0;
}
std::string convert_sockaddr_to_name(const sockaddr *const addr, const socklen_t addr_len)
{
char host[256];
#if defined(ESP32)
auto sa_addr = reinterpret_cast<const sockaddr_in *>(addr);
inet_ntop(addr->sa_family, &sa_addr->sin_addr.s_addr, host, sizeof host);
return host + myformat(":%d", sa_addr->sin_port);
#else
char serv[16];
getnameinfo(addr, addr_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
return myformat("[%s]:%s", host, serv);
#endif
}
std::string com_client_sockets::get_endpoint_name() const
{
#if defined(ESP32)
sockaddr_in addr { };
#else
sockaddr_in6 addr { };
#endif
socklen_t addr_len = sizeof addr;
if (getpeername(fd, reinterpret_cast<sockaddr *>(&addr), &addr_len) == -1) {
DOLOG(logging::ll_debug, "get_endpoint_name", "-", "failed to find name of fd %d", fd);
return "?:?";
}
return convert_sockaddr_to_name(reinterpret_cast<sockaddr *>(&addr), addr_len);
}
std::string com_client_sockets::get_local_address() const
{
#if defined(ESP32)
sockaddr_in addr { };
#else
sockaddr_in6 addr { };
#endif
socklen_t addr_len = sizeof addr;
if (getsockname(fd, reinterpret_cast<sockaddr *>(&addr), &addr_len) == -1) {
DOLOG(logging::ll_error, "get_local_address", "-", "failed to find local name of fd %d", fd);
return "?:?";
}
#if defined(ESP32)
addr.sin_port = 3260; // ESP32 SDK2 returns peer port?!
#endif
return convert_sockaddr_to_name(reinterpret_cast<sockaddr *>(&addr), addr_len);
}