56#define MSG_SYNC "SYNC"
58#define MSG_RESP "RESP"
60static void usage(
const char* prog) {
63 " Server: %s -p PORT [-o OFFSET] [-V LOGLEVEL]\n"
64 " Client: %s -s ADDRESS -p PORT [-n COUNT] [-V LOGLEVEL]\n"
67 " -p PORT port to use\n"
68 " -s ADDRESS server address (client mode)\n"
69 " -o OFFSET simulated clock offset in seconds (server only, default: 0.42)\n"
70 " -n COUNT number of sync rounds (client only, default: 20)\n"
71 " -V LEVEL log level: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_ERR\n"
72 " -h show this help\n",
78 clock_gettime(CLOCK_MONOTONIC, &ts);
79 return (
double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
85 n_log(
LOG_NOTICE,
"Server: binding UDP on port %s (simulated offset: %.4f s)",
port, fake_offset);
94 for (
int i = 0; i < nb_rounds; i++) {
96 struct sockaddr_storage client_addr;
97 socklen_t client_len =
sizeof(client_addr);
100 (
struct sockaddr*)&client_addr, &client_len);
105 buf[received] =
'\0';
108 double client_send_time = 0.0;
109 if (sscanf(buf,
MSG_SYNC " %lf", &client_send_time) != 1) {
115 double server_time =
get_time() + fake_offset;
119 snprintf(resp,
sizeof(resp),
MSG_RESP " %.9f %.9f", client_send_time, server_time);
122 (
struct sockaddr*)&client_addr, client_len);
126 n_log(
LOG_DEBUG,
"Server: responded to sync #%d (server_time=%.4f)", i + 1, server_time);
146 n_log(
LOG_ERR,
"Client: failed to create clock sync estimator");
153 n_log(
LOG_NOTICE,
" Round | RTT (ms) | Offset (ms) | Est.Offset (ms) | Est.RTT (ms)");
154 n_log(
LOG_NOTICE,
"-------+-----------+-------------+-----------------+-------------");
157 while (rounds_done < nb_rounds) {
170 snprintf(msg,
sizeof(msg),
MSG_SYNC " %.9f", send_time);
185 buf[received] =
'\0';
190 double resp_client_time = 0.0, resp_server_time = 0.0;
191 if (sscanf(buf,
MSG_RESP " %lf %lf", &resp_client_time, &resp_server_time) != 2) {
202 double raw_rtt = (recv_time - resp_client_time) * 1000.0;
203 double raw_offset = (resp_server_time + (recv_time - resp_client_time) / 2.0 - recv_time) * 1000.0;
213 n_log(
LOG_NOTICE,
"-------+-----------+-------------+-----------------+-------------");
224 n_log(
LOG_NOTICE,
"Difference: %.4f ms", (est_server_now - local_now) * 1000.0);
230int main(
int argc,
char* argv[]) {
235 double fake_offset = 0.42;
246 while ((
getoptret = getopt(argc, argv,
"hs:p:n:o:V:")) != EOF) {
256 port = strdup(optarg);
259 nb_rounds = atoi(optarg);
260 if (nb_rounds < 1) nb_rounds = 1;
263 fake_offset = atof(optarg);
266 if (!strncmp(
"LOG_DEBUG", optarg, 9)) {
268 }
else if (!strncmp(
"LOG_INFO", optarg, 8)) {
270 }
else if (!strncmp(
"LOG_NOTICE", optarg, 10)) {
272 }
else if (!strncmp(
"LOG_ERR", optarg, 7)) {
275 fprintf(stderr,
"Unknown log level: %s\n", optarg);
287 fprintf(stderr,
"Error: -p PORT is required\n");
static void run_server(char *port, double fake_offset, int nb_rounds)
#define MSG_SYNC
sync request message: "SYNC client_send_time\n"
static void run_client(char *server, char *port, int nb_rounds)
static double get_time(void)
#define MSG_RESP
sync response message: "RESP client_send_time server_time\n"
NETWORK * netw
Network for server mode, accepting incomming.
double estimated_offset
add to local time to get estimated server time
double estimated_rtt
current estimated round-trip time
N_CLOCK_SYNC * n_clock_sync_new(void)
allocate and initialize a new clock sync estimator
int n_clock_sync_should_send(const N_CLOCK_SYNC *cs, double local_now)
check if it's time to send a new sync request (returns TRUE/FALSE)
double n_clock_sync_server_time(const N_CLOCK_SYNC *cs, double local_now)
get estimated server time given a local time value
void n_clock_sync_delete(N_CLOCK_SYNC **cs)
free a clock sync estimator
void n_clock_sync_mark_sent(N_CLOCK_SYNC *cs, double local_now)
mark that a sync request was just sent
int n_clock_sync_process_response(N_CLOCK_SYNC *cs, double client_send_time, double server_time, double local_now)
record a sync response: client_send_time is the local time the request was sent, server_time is the s...
clock synchronization estimator
#define FreeNoLog(__ptr)
Free Handler without log.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
#define LOG_NOTICE
normal but significant condition
#define LOG_INFO
informational
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
int netw_bind_udp(NETWORK **netw, char *addr, char *port, int ip_version)
Create a UDP bound socket for receiving datagrams.
ssize_t send_udp_data(void *netw, char *buf, uint32_t n)
send data via UDP on a connected socket
ssize_t netw_udp_sendto(NETWORK *netw, char *buf, uint32_t n, struct sockaddr *dest_addr, socklen_t dest_len)
send data via UDP to a specific destination address
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
ssize_t recv_udp_data(void *netw, char *buf, uint32_t n)
recv data via UDP from a connected socket
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
int netw_connect_udp(NETWORK **netw, char *host, char *port, int ip_version)
Connect a UDP socket to a remote host.
ssize_t netw_udp_recvfrom(NETWORK *netw, char *buf, uint32_t n, struct sockaddr *src_addr, socklen_t *src_len)
recv data via UDP and capture the source address
Clock synchronization estimator for networked games.
Common headers and low-level functions & define.