mirror of
https://github.com/p2r3/bareiron.git
synced 2025-10-02 07:35:08 +02:00
285 lines
8.4 KiB
C
285 lines
8.4 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef ESP_PLATFORM
|
|
#include "lwip/sockets.h"
|
|
#include "lwip/netdb.h"
|
|
#include "esp_timer.h"
|
|
#else
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#ifndef CLOCK_MONOTONIC
|
|
#define CLOCK_MONOTONIC 1
|
|
#endif
|
|
#endif
|
|
|
|
#include "globals.h"
|
|
#include "varnum.h"
|
|
#include "procedures.h"
|
|
#include "tools.h"
|
|
|
|
#ifndef htonll
|
|
static uint64_t htonll (uint64_t value) {
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
return ((uint64_t)htonl((uint32_t)(value >> 32))) |
|
|
((uint64_t)htonl((uint32_t)(value & 0xFFFFFFFF)) << 32);
|
|
#else
|
|
return value;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// Keep track of the total amount of bytes received with recv_all
|
|
// Helps notice misread packets and clean up after errors
|
|
uint64_t total_bytes_received = 0;
|
|
|
|
ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) {
|
|
char *p = buf;
|
|
size_t total = 0;
|
|
|
|
// Track time of last meaningful network update
|
|
// Used to handle timeout when client is stalling
|
|
int64_t last_update_time = get_program_time();
|
|
|
|
// If requested, exit early when first byte not immediately available
|
|
if (require_first) {
|
|
ssize_t r = recv(client_fd, p, 1, MSG_PEEK);
|
|
if (r <= 0) {
|
|
if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
|
return 0; // no first byte available yet
|
|
}
|
|
return -1; // error or connection closed
|
|
}
|
|
}
|
|
|
|
// Busy-wait (with task yielding) until we get exactly n bytes
|
|
while (total < n) {
|
|
ssize_t r = recv(client_fd, p + total, n - total, 0);
|
|
if (r < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
// handle network timeout
|
|
if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) {
|
|
disconnectClient(&client_fd, -1);
|
|
return -1;
|
|
}
|
|
task_yield();
|
|
continue;
|
|
} else {
|
|
total_bytes_received += total;
|
|
return -1; // real error
|
|
}
|
|
} else if (r == 0) {
|
|
// connection closed before full read
|
|
total_bytes_received += total;
|
|
return total;
|
|
}
|
|
total += r;
|
|
last_update_time = get_program_time();
|
|
}
|
|
|
|
total_bytes_received += total;
|
|
return total; // got exactly n bytes
|
|
}
|
|
|
|
ssize_t send_all (int client_fd, const void *buf, ssize_t len) {
|
|
// Treat any input buffer as *uint8_t for simplicity
|
|
const uint8_t *p = (const uint8_t *)buf;
|
|
ssize_t sent = 0;
|
|
|
|
// Track time of last meaningful network update
|
|
// Used to handle timeout when client is stalling
|
|
int64_t last_update_time = get_program_time();
|
|
|
|
// Busy-wait (with task yielding) until all data has been sent
|
|
while (sent < len) {
|
|
#ifdef _WIN32
|
|
ssize_t n = send(client_fd, p + sent, len - sent, 0);
|
|
#else
|
|
ssize_t n = send(client_fd, p + sent, len - sent, MSG_NOSIGNAL);
|
|
#endif
|
|
if (n > 0) { // some data was sent, log it
|
|
sent += n;
|
|
last_update_time = get_program_time();
|
|
continue;
|
|
}
|
|
if (n == 0) { // connection was closed, treat this as an error
|
|
errno = ECONNRESET;
|
|
return -1;
|
|
}
|
|
// not yet ready to transmit, try again
|
|
#ifdef _WIN32 //handles windows socket timeout
|
|
int err = WSAGetLastError();
|
|
if (err == WSAEWOULDBLOCK || err == WSAEINTR) {
|
|
#else
|
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
#endif
|
|
// handle network timeout
|
|
if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) {
|
|
disconnectClient(&client_fd, -2);
|
|
return -1;
|
|
}
|
|
task_yield();
|
|
continue;
|
|
}
|
|
return -1; // real error
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
ssize_t writeByte (int client_fd, uint8_t byte) {
|
|
return send_all(client_fd, &byte, 1);
|
|
}
|
|
ssize_t writeUint16 (int client_fd, uint16_t num) {
|
|
uint16_t be = htons(num);
|
|
return send_all(client_fd, &be, sizeof(be));
|
|
}
|
|
ssize_t writeUint32 (int client_fd, uint32_t num) {
|
|
uint32_t be = htonl(num);
|
|
return send_all(client_fd, &be, sizeof(be));
|
|
}
|
|
ssize_t writeUint64 (int client_fd, uint64_t num) {
|
|
uint64_t be = htonll(num);
|
|
return send_all(client_fd, &be, sizeof(be));
|
|
}
|
|
ssize_t writeFloat (int client_fd, float num) {
|
|
uint32_t bits;
|
|
memcpy(&bits, &num, sizeof(bits));
|
|
bits = htonl(bits);
|
|
return send_all(client_fd, &bits, sizeof(bits));
|
|
}
|
|
ssize_t writeDouble (int client_fd, double num) {
|
|
uint64_t bits;
|
|
memcpy(&bits, &num, sizeof(bits));
|
|
bits = htonll(bits);
|
|
return send_all(client_fd, &bits, sizeof(bits));
|
|
}
|
|
|
|
uint8_t readByte (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 1, false);
|
|
return recv_buffer[0];
|
|
}
|
|
uint16_t readUint16 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 2, false);
|
|
return ((uint16_t)recv_buffer[0] << 8) | recv_buffer[1];
|
|
}
|
|
int16_t readInt16 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 2, false);
|
|
return ((int16_t)recv_buffer[0] << 8) | (int16_t)recv_buffer[1];
|
|
}
|
|
uint32_t readUint32 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 4, false);
|
|
return ((uint32_t)recv_buffer[0] << 24) |
|
|
((uint32_t)recv_buffer[1] << 16) |
|
|
((uint32_t)recv_buffer[2] << 8) |
|
|
((uint32_t)recv_buffer[3]);
|
|
}
|
|
uint64_t readUint64 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 8, false);
|
|
return ((uint64_t)recv_buffer[0] << 56) |
|
|
((uint64_t)recv_buffer[1] << 48) |
|
|
((uint64_t)recv_buffer[2] << 40) |
|
|
((uint64_t)recv_buffer[3] << 32) |
|
|
((uint64_t)recv_buffer[4] << 24) |
|
|
((uint64_t)recv_buffer[5] << 16) |
|
|
((uint64_t)recv_buffer[6] << 8) |
|
|
((uint64_t)recv_buffer[7]);
|
|
}
|
|
int64_t readInt64 (int client_fd) {
|
|
recv_count = recv_all(client_fd, recv_buffer, 8, false);
|
|
return ((int64_t)recv_buffer[0] << 56) |
|
|
((int64_t)recv_buffer[1] << 48) |
|
|
((int64_t)recv_buffer[2] << 40) |
|
|
((int64_t)recv_buffer[3] << 32) |
|
|
((int64_t)recv_buffer[4] << 24) |
|
|
((int64_t)recv_buffer[5] << 16) |
|
|
((int64_t)recv_buffer[6] << 8) |
|
|
((int64_t)recv_buffer[7]);
|
|
}
|
|
float readFloat (int client_fd) {
|
|
uint32_t bytes = readUint32(client_fd);
|
|
float output;
|
|
memcpy(&output, &bytes, sizeof(output));
|
|
return output;
|
|
}
|
|
double readDouble (int client_fd) {
|
|
uint64_t bytes = readUint64(client_fd);
|
|
double output;
|
|
memcpy(&output, &bytes, sizeof(output));
|
|
return output;
|
|
}
|
|
|
|
// Receive length prefixed data with bounds checking
|
|
ssize_t readLengthPrefixedData (int client_fd) {
|
|
uint32_t length = readVarInt(client_fd);
|
|
if (length >= MAX_RECV_BUF_LEN) {
|
|
printf("ERROR: Received length (%lu) exceeds maximum (%u)\n", length, MAX_RECV_BUF_LEN);
|
|
disconnectClient(&client_fd, -1);
|
|
recv_count = 0;
|
|
return 0;
|
|
}
|
|
return recv_all(client_fd, recv_buffer, length, false);
|
|
}
|
|
|
|
// Reads a networked string into recv_buffer
|
|
void readString (int client_fd) {
|
|
recv_count = readLengthPrefixedData(client_fd);
|
|
recv_buffer[recv_count] = '\0';
|
|
}
|
|
// Reads a networked string of up to N bytes into recv_buffer
|
|
void readStringN (int client_fd, uint32_t max_length) {
|
|
// Forward to readString if max length is invalid
|
|
if (max_length >= MAX_RECV_BUF_LEN) {
|
|
readString(client_fd);
|
|
return;
|
|
}
|
|
// Attempt to read full string within maximum
|
|
uint32_t length = readVarInt(client_fd);
|
|
if (max_length > length) {
|
|
recv_count = recv_all(client_fd, recv_buffer, length, false);
|
|
recv_buffer[recv_count] = '\0';
|
|
return;
|
|
}
|
|
// Read string up to maximum, dump the rest
|
|
recv_count = recv_all(client_fd, recv_buffer, max_length, false);
|
|
recv_buffer[recv_count] = '\0';
|
|
uint8_t dummy;
|
|
for (uint32_t i = max_length; i < length; i ++) {
|
|
recv_all(client_fd, &dummy, 1, false);
|
|
}
|
|
}
|
|
|
|
uint32_t fast_rand () {
|
|
rng_seed ^= rng_seed << 13;
|
|
rng_seed ^= rng_seed >> 17;
|
|
rng_seed ^= rng_seed << 5;
|
|
return rng_seed;
|
|
}
|
|
|
|
uint64_t splitmix64 (uint64_t state) {
|
|
uint64_t z = state + 0x9e3779b97f4a7c15;
|
|
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
|
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
|
return z ^ (z >> 31);
|
|
}
|
|
|
|
#ifndef ESP_PLATFORM
|
|
// Returns system time in microseconds.
|
|
// On ESP-IDF, this is available in "esp_timer.h", and returns time *since
|
|
// the start of the program*, and NOT wall clock time. To ensure
|
|
// compatibility, this should only be used to measure time intervals.
|
|
int64_t get_program_time () {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (int64_t)ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL;
|
|
}
|
|
#endif
|