// broadcaster_seq.c #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #define BROADCAST_PORT 5000 #define MESSAGE "Hello from broadcast!" #define MAGIC_RBRC 0x52425243u /* 'RBRC' */ #define VERSION 1u /* --- portable htonll/ntohll --- */ static inline uint64_t htonll(uint64_t x) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return ((uint64_t)htonl(x & 0xFFFFFFFFULL) << 32) | htonl((uint32_t)(x >> 32)); #else return x; #endif } /* payload header (packed) */ #pragma pack(push, 1) typedef struct { uint32_t magic; /* MAGIC_RBRC */ uint32_t version; /* VERSION */ uint64_t seq_be; /* sequence number, big-endian */ uint64_t send_sec_be; /* CLOCK_REALTIME seconds, big-endian */ uint32_t send_nsec_be; /* CLOCK_REALTIME nanoseconds, big-endian */ } rb_hdr_t; #pragma pack(pop) static void error_exit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } static void add_ms(struct timespec *ts, int64_t ms) { ts->tv_nsec += (ms % 1000) * 1000000LL; ts->tv_sec += ms / 1000; if (ts->tv_nsec >= 1000000000L) { ts->tv_sec += 1; ts->tv_nsec -= 1000000000L; } } /* sleep until absolute time 't' on CLOCK_MONOTONIC */ static void sleep_until(const struct timespec *t) { while (1) { int rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, t, NULL); if (rc == 0) return; if (rc != EINTR) { errno = rc; error_exit("clock_nanosleep"); } } } int main(int argc, char *argv[]) { if (argc < 3 || argc > 4) { fprintf(stderr, "Usage: %s [start_seq]\n", argv[0]); return EXIT_FAILURE; } const char *iface = argv[1]; int period_ms = atoi(argv[2]); uint64_t seq = (argc == 4) ? strtoull(argv[3], NULL, 0) : 0ULL; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) error_exit("socket"); /* Enable broadcast */ int one = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) error_exit("setsockopt SO_BROADCAST"); /* Bind to a specific interface */ if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface)) < 0) error_exit("setsockopt SO_BINDTODEVICE"); /* (Optional) raise priority of packets a bit */ int prio = 6; setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)); /* best-effort if it fails */ struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(BROADCAST_PORT); addr.sin_addr.s_addr = inet_addr("255.255.255.255"); /* Prepare constant message tail */ const char *tail = MESSAGE; const size_t tail_len = strlen(tail); /* Timing: use absolute MONOTONIC to avoid drift */ struct timespec t_next; if (clock_gettime(CLOCK_MONOTONIC, &t_next) < 0) error_exit("clock_gettime MONOTONIC"); while (1) { /* Header with seq + send timestamp (CLOCK_REALTIME) */ rb_hdr_t hdr; struct timespec ts_rt; if (clock_gettime(CLOCK_REALTIME, &ts_rt) < 0) error_exit("clock_gettime REALTIME"); hdr.magic = htonl(MAGIC_RBRC); hdr.version = htonl(VERSION); hdr.seq_be = htonll(seq); hdr.send_sec_be = htonll((uint64_t)ts_rt.tv_sec); hdr.send_nsec_be = htonl((uint32_t)ts_rt.tv_nsec); /* Build iovec buffer on the stack: [header][message] */ uint8_t buf[sizeof(hdr) + 256]; memcpy(buf, &hdr, sizeof(hdr)); memcpy(buf + sizeof(hdr), tail, tail_len); size_t pkt_len = sizeof(hdr) + tail_len; ssize_t sent = sendto(sockfd, buf, pkt_len, 0, (struct sockaddr *)&addr, sizeof(addr)); if (sent < 0) { perror("sendto"); /* keep going */ } seq++; add_ms(&t_next, period_ms); sleep_until(&t_next); } close(sockfd); return 0; }