33 #include <glm/glm.hpp>
38 #ifdef E3D_TARGET_UNIX
39 # define _XOPEN_SOURCE_EXTENDED 1
40 # include <sys/socket.h>
41 # include <netinet/in.h>
42 # ifndef INVALID_SOCKET
43 # define INVALID_SOCKET -1
46 # include <arpa/inet.h>
50 #define NETWORK_FLAG 0
51 #elif defined E3D_TARGET_WINDOWS
52 # define _WINSOCK_DEPRECATED_NO_WARNINGS 1
53 # ifndef WINSOCK2_IMPORTED
54 # define WINSOCK2_IMPORTED
55 # include <winsock2.h>
56 # include <ws2tcpip.h>
59 #define NETWORK_FLAG MSG_DONTROUTE
61 #include <sys/types.h>
63 #include "eng3d/network.hpp"
64 #include "eng3d/log.hpp"
65 #include "eng3d/serializer.hpp"
66 #include "eng3d/utils.hpp"
68 constexpr
static int max_tries = 10;
69 constexpr
static int tries_ms = 100;
75 const auto* c_data =
reinterpret_cast<const char*
>(data);
76 auto tries = max_tries;
77 for(
size_t i = 0; i < size; ) {
80 int r =
::send(
fd, &c_data[i], glm::min<std::size_t>(1024, size - i), NETWORK_FLAG);
85 std::this_thread::sleep_for(std::chrono::milliseconds(tries_ms));
88 i +=
static_cast<std::size_t
>(r);
95 auto* c_data =
reinterpret_cast<char*
>(data);
96 std::memset(c_data, 0, size);
97 auto tries = max_tries;
98 for(
size_t i = 0; i < size; ) {
101 int r = ::recv(fd, &c_data[i], glm::min<std::size_t>(1024, size - i), NETWORK_FLAG);
106 std::this_thread::sleep_for(std::chrono::milliseconds(tries_ms));
109 i +=
static_cast<std::size_t
>(r);
117 #ifdef E3D_TARGET_UNIX
121 setsockopt(this->fd, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&tv),
sizeof tv);
122 #elif defined E3D_TARGET_WINDOWS
123 DWORD timeout = seconds * 1000;
124 setsockopt(this->fd, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout),
sizeof timeout);
130 #ifdef E3D_TARGET_UNIX
134 int has_pending = poll(&pfd, 1, 10);
135 return (pfd.revents & POLLIN) != 0 || has_pending;
136 #elif defined E3D_TARGET_WINDOWS
137 u_long has_pending = 0;
138 int test = ioctlsocket(this->fd, FIONREAD, &has_pending);
144 #ifdef E3D_TARGET_UNIX
145 int flags = fcntl(fd, F_GETFL, 0);
150 flags = blocking ? (flags & (~O_NONBLOCK)) : (flags | O_NONBLOCK);
151 fcntl(fd, F_SETFL, flags);
152 #elif defined E3D_TARGET_WINDOWS
153 u_long mode = blocking ? 0 : 1;
154 ioctlsocket(fd, FIONBIO, &mode);
164 const uint16_t net_code = htons(
static_cast<uint16_t
>(code));
165 if(!stream.send(&net_code,
sizeof(net_code), pred))
168 const uint16_t net_size = htons(n_data);
169 if(!stream.send(&net_size,
sizeof(net_size), pred))
172 if(!stream.send(buffer.data(), n_data, pred))
175 const uint16_t eof_marker = htons(0xFE0F);
176 if(!stream.send(&eof_marker,
sizeof(eof_marker), pred))
183 if(!stream.recv(&net_code,
sizeof(net_code), pred))
185 code =
static_cast<PacketCode>(ntohs(net_code));
188 if(!stream.recv(&net_size,
sizeof(net_size), pred))
190 n_data =
static_cast<size_t>(ntohs(net_size));
194 buffer.resize(n_data);
195 if(!stream.recv(buffer.data(), buffer.size(), pred))
199 if(!stream.recv(&eof_marker,
sizeof(eof_marker), pred))
201 assert(ntohs(eof_marker) == 0xFE0F);
202 if(ntohs(eof_marker) != 0xFE0F)
211 if(this->thread && this->thread->joinable())
212 this->thread->join();
217 socklen_t len =
sizeof(client);
218 conn_fd = accept(fd,
reinterpret_cast<sockaddr*
>(&client), &len);
219 if(conn_fd == INVALID_SOCKET)
242 : n_clients{ static_cast<
std::size_t>(max_conn) }
244 #ifdef E3D_TARGET_WINDOWS
246 WSAStartup(MAKEWORD(2, 2), &data);
250 addr.sin_family = AF_INET;
251 addr.sin_addr.s_addr = htonl(INADDR_ANY);
252 addr.sin_port = htons(port);
254 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
255 if(
fd == INVALID_SOCKET)
257 #ifdef E3D_TARGET_UNIX
259 setsockopt(
fd, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int));
260 setsockopt(
fd, SOL_SOCKET, SO_REUSEPORT, &enable,
sizeof(
int));
262 if(bind(
fd,
reinterpret_cast<sockaddr*
>(&
addr),
sizeof(
addr)) != 0)
264 if(listen(
fd, max_conn) != 0)
266 #ifdef E3D_TARGET_UNIX
268 fcntl(
fd, F_SETFL, fcntl(
fd, F_GETFD, 0) | O_NONBLOCK);
270 signal(SIGPIPE, SIG_IGN);
278 #ifdef E3D_TARGET_UNIX
280 #elif defined E3D_TARGET_WINDOWS
285 for(
size_t i = 0; i < this->n_clients; i++)
286 if(this->clients[i].thread && this->clients[i].thread->joinable())
287 this->clients[i].thread->join();
288 delete[] this->clients;
293 for(
size_t i = 0; i < n_clients; i++)
294 if(clients[i].is_connected ==
true)
295 clients[i].packets.push(packet);
299 auto& cl = clients[id];
302 cl.is_connected =
false;
303 while(!cl.is_connected) {
304 conn_fd = cl.try_connect(fd);
306 const auto delta = std::chrono::seconds{ 5 };
307 const auto start_time = std::chrono::system_clock::now();
308 std::this_thread::sleep_until(start_time + delta);
314 packet.
pred = ([
this]() {
return this->run ==
true;});
318 for(
size_t i = 0; i < n_clients; i++) {
319 if(clients[i].thread ==
nullptr) {
324 on_connect(conn_fd,
id);
326 while(this->run && cl.is_connected ==
true) {
328 if(cl.has_pending()) {
334 handler(packet, ar,
id);
341 while(cl.packets.try_pop(tosend_packet)) {
344 if(!tosend_packet.
send())
360 cl.is_connected =
false;
364 #ifdef E3D_TARGET_WINDOWS
369 #ifdef E3D_TARGET_WINDOWS
370 shutdown(conn_fd, SD_BOTH);
371 #elif defined E3D_TARGET_UNIX && !defined E3D_TARGET_SWITCH
373 shutdown(conn_fd, SHUT_RDWR);
382 #ifdef E3D_TARGET_WINDOWS
384 if(WSAStartup(MAKEWORD(2, 2), &data) != 0) {
391 addr.sin_family = AF_INET;
392 addr.sin_addr.s_addr = inet_addr(host.c_str());
393 addr.sin_port = htons(port);
395 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
396 if(fd == INVALID_SOCKET) {
397 #ifdef E3D_TARGET_WINDOWS
404 if(connect(fd,
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) != 0) {
405 #ifdef E3D_TARGET_UNIX
407 #elif defined E3D_TARGET_WINDOWS
416 #ifdef E3D_TARGET_WINDOWS
444 while(packets.try_pop(tosend_packet)) {
446 tosend_packet.
pred = cond;
448 if(!tosend_packet.
send())
The purpouse of the serializer is to serialize objects onto a byte stream that can be transfered onto...
virtual const char * what() const noexcept
Client(std::string host, const unsigned port)
void do_netloop(std::function< bool()> cond, std::function< void(const Packet &packet, Eng3D::Deser::Archive &ar)> handler)
std::function< bool()> pred
virtual const char * what() const noexcept
void flush_packets()
TODO: flush packets.
void do_netloop(std::function< void(int i)> on_wake_thread, int id)
Server(unsigned port, unsigned max_conn)
void broadcast(const Eng3D::Networking::Packet &packet)
This will broadcast the given packet to all clients currently on the server.
virtual const char * what() const noexcept
bool recv(void *data, size_t size, std::function< bool()> pred=0)
void set_timeout(int seconds)
bool send(const void *data, size_t size, std::function< bool()> pred)
void set_blocking(bool value)
std::string translate(const std::string_view str)
void error(const std::string_view category, const std::string_view msg)
void debug(const std::string_view category, const std::string_view msg)
std::string translate_format(const std::string_view format, Args &&... args)
String formatter, with translation.
Base class that serves as archiver, stores (in memory) the data required for serialization/deserializ...
void set_buffer(const void *buf, size_t size)
#define CXX_THROW(class,...)