Last active
March 20, 2020 19:11
-
-
Save iAmGroute/e5b271affa8b217a12e50654c50deb3c to your computer and use it in GitHub Desktop.
A simple C server program that reads from stdin and sends to all connected clients
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(doesn't seem to work for h264) | |
HTTP/1.1 206 | |
access-control-allow-origin: * | |
connection: keep-alive | |
content-type: video/h264 | |
content-range: bytes 0- | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <signal.h> // signal | |
#include <stdio.h> // printf | |
#include <stdlib.h> // exit | |
#include <string.h> // memcpy | |
#include <unistd.h> // close, lseek | |
#include <arpa/inet.h> // htons | |
#include <sys/select.h> // select | |
#include <sys/socket.h> // socket | |
#define TCP_PORT 35001 | |
#define TCP_BACKLOG 5 | |
#define MAX_CLIENTS 1024 | |
#define BUF_SIZE 262144 | |
#define assert_sys(x) if (x < 0) { perror(#x); exit(1); } | |
typedef unsigned char u8; | |
u8 read_buf[16384] = {0}; | |
u8 buf[BUF_SIZE] = {0}; | |
uint buf_r[MAX_CLIENTS] = {0}; | |
uint buf_w = 0; | |
int nfds = 0; | |
fd_set readfds, writefds; | |
fd_set readables, writables; | |
void kick(int fd) | |
{ | |
printf("----- Kicking %d\n", fd); | |
close(fd); | |
FD_CLR(fd, &readfds); | |
FD_CLR(fd, &writefds); | |
if (fd + 1 >= nfds) nfds = fd; | |
} | |
int main(void) | |
{ | |
// Ignore the broken pipe signal | |
signal(SIGPIPE, SIG_IGN); | |
// Init the fd sets and their mirrors | |
FD_ZERO(&readfds); FD_ZERO(&writefds); | |
FD_ZERO(&readables); FD_ZERO(&writables); | |
// Init the local (listening) address | |
struct sockaddr_in sa = { | |
.sin_family = AF_INET, | |
.sin_port = htons(TCP_PORT), | |
.sin_addr.s_addr = htonl(INADDR_ANY) | |
}; | |
// Server socket | |
int server_fd = socket(PF_INET, SOCK_STREAM, 0); assert_sys(server_fd); | |
int reuseaddr = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); assert_sys(reuseaddr); | |
int bind_ok = bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)); assert_sys(bind_ok); | |
int listen_ok = listen(server_fd, TCP_BACKLOG); assert_sys(listen_ok); | |
// Will read from stdin and the server socket | |
FD_SET( 0, &readfds); | |
FD_SET(server_fd, &readfds); | |
nfds = server_fd + 1; | |
while (1) { | |
// Copy fd sets, because select will change them | |
memcpy(&readables, &readfds, sizeof(fd_set)); | |
memcpy(&writables, &writefds, sizeof(fd_set)); | |
int select_ok = select(nfds, &readables, &writables, NULL, NULL); assert_sys(select_ok); | |
// Read from / write to client sockets | |
for (int i = server_fd + 1; i < nfds; i++) { | |
if (FD_ISSET(i, &readables)) { | |
ssize_t n = read(i, read_buf, sizeof(read_buf) - 1); | |
if (n <= 0) { kick(i); continue; } | |
read_buf[n] = 0; | |
printf("----- Read from %d\n%s\n", i, read_buf); | |
} | |
if (FD_ISSET(i, &writables)) { | |
// Send from main buffer | |
uint pos_r = buf_r[i]; | |
uint tosend = (buf_w >= pos_r ? buf_w : BUF_SIZE) - pos_r; | |
if (tosend == 0) { FD_CLR(i, &writefds); continue; } | |
ssize_t n = write(i, &buf[pos_r], tosend); | |
if (n < 0) { kick(i); continue; } | |
buf_r[i] = (pos_r + n) % BUF_SIZE; | |
} | |
} | |
// Accept from server socket | |
if (FD_ISSET(server_fd, &readables)) { | |
socklen_t len = sizeof(struct sockaddr_in); | |
int client_fd = accept(server_fd, (struct sockaddr *)&sa, &len); assert_sys(client_fd); | |
printf("----- New client %d\n", client_fd); | |
if (client_fd > MAX_CLIENTS) { kick(client_fd); continue; } | |
FD_SET(client_fd, &readfds); | |
if (client_fd >= nfds) nfds = client_fd + 1; | |
buf_r[client_fd] = buf_w; | |
} | |
// Read from stdin | |
if (FD_ISSET(0, &readables)) { | |
ssize_t n = read(0, &buf[buf_w], BUF_SIZE - buf_w); assert_sys(n); | |
if (n <= 0) return 0; | |
buf_w = (buf_w + n) % BUF_SIZE; | |
// All active sockets will be in `readfds` so just copy the entire set | |
// instead of setting them in `writefds` one-by-one | |
memcpy(&writefds, &readfds, sizeof(fd_set)); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <fcntl.h> // open | |
#include <signal.h> // signal | |
#include <stdio.h> // printf | |
#include <stdlib.h> // exit | |
#include <string.h> // memcpy | |
#include <unistd.h> // close, lseek | |
#include <arpa/inet.h> // htons | |
#include <sys/select.h> // select | |
#include <sys/socket.h> // socket | |
#define TCP_PORT 35001 | |
#define TCP_BACKLOG 5 | |
#define MAX_CLIENTS 1024 | |
#define BUF_SIZE 262144 | |
#define assert_sys(x) if (x < 0) { perror(#x); exit(1); } | |
typedef unsigned char u8; | |
enum { | |
WAIT_GREET = 0, | |
DO_GREET = 1, | |
HAVE_GREETED = 2 | |
}; | |
u8 *greet_buf = NULL; | |
size_t greet_size = -1; | |
u8 read_buf[16384] = {0}; | |
u8 buf[BUF_SIZE] = {0}; | |
uint buf_r[MAX_CLIENTS] = {0}; | |
u8 greet_status[MAX_CLIENTS] = {0}; | |
uint buf_w = 0; | |
int nfds = 0; | |
fd_set readfds, writefds; | |
fd_set readables, writables; | |
void kick(int fd) | |
{ | |
printf("----- Kicking %d\n", fd); | |
greet_status[fd] = WAIT_GREET; | |
close(fd); | |
FD_CLR(fd, &readfds); | |
FD_CLR(fd, &writefds); | |
if (fd + 1 >= nfds) nfds = fd; | |
} | |
int main(void) | |
{ | |
// Read the "greet" file and save to `greet_buf` | |
// `greet_size` will be the actual size of the data read | |
int greet_fd = open("greet", O_RDONLY); assert_sys(greet_fd); | |
off_t greet_seek = lseek(greet_fd, 0, SEEK_END); assert_sys(greet_seek); | |
greet_size = greet_seek + 1; | |
greet_seek = lseek(greet_fd, 0, SEEK_SET); assert_sys(greet_seek); | |
greet_buf = calloc(1, greet_size); | |
if (!greet_buf) { printf("Can't allocate %ld bytes of memory\n", greet_size); return 1; } | |
ssize_t remaining = greet_size; | |
greet_size = 0; | |
while (remaining) { | |
ssize_t read_n = read(greet_fd, &greet_buf[greet_size], remaining); assert_sys(read_n); | |
if (read_n <= 0) break; | |
greet_size += read_n; | |
remaining -= read_n; | |
} | |
// Ignore the broken pipe signal | |
signal(SIGPIPE, SIG_IGN); | |
// Init the fd sets and their mirrors | |
FD_ZERO(&readfds); FD_ZERO(&writefds); | |
FD_ZERO(&readables); FD_ZERO(&writables); | |
// Init the local (listening) address | |
struct sockaddr_in sa = { | |
.sin_family = AF_INET, | |
.sin_port = htons(TCP_PORT), | |
.sin_addr.s_addr = htonl(INADDR_ANY) | |
}; | |
// Server socket | |
int server_fd = socket(PF_INET, SOCK_STREAM, 0); assert_sys(server_fd); | |
int reuseaddr = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); assert_sys(reuseaddr); | |
int bind_ok = bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)); assert_sys(bind_ok); | |
int listen_ok = listen(server_fd, TCP_BACKLOG); assert_sys(listen_ok); | |
// Will read from stdin and the server socket | |
FD_SET( 0, &readfds); | |
FD_SET(server_fd, &readfds); | |
nfds = server_fd + 1; | |
while (1) { | |
// Copy fd sets, because select will change them | |
memcpy(&readables, &readfds, sizeof(fd_set)); | |
memcpy(&writables, &writefds, sizeof(fd_set)); | |
int select_ok = select(nfds, &readables, &writables, NULL, NULL); assert_sys(select_ok); | |
// Read from / write to client sockets | |
for (int i = server_fd + 1; i < nfds; i++) { | |
if (FD_ISSET(i, &readables)) { | |
ssize_t n = read(i, read_buf, sizeof(read_buf) - 1); | |
if (n <= 0) { kick(i); continue; } | |
read_buf[n] = 0; | |
printf("----- Read from %d\n%s\n", i, read_buf); | |
if (greet_status[i] == WAIT_GREET) { | |
if (n >= 3 && | |
read_buf[n-3] == '\n' && | |
read_buf[n-2] == '\r' && | |
read_buf[n-1] == '\n' | |
) { | |
printf("----- Greet OK\n"); | |
greet_status[i] = DO_GREET; | |
FD_SET(i, &writefds); | |
} | |
} | |
} | |
if (FD_ISSET(i, &writables)) { | |
if (greet_status[i] == HAVE_GREETED) { | |
// Send from main buffer | |
uint pos_r = buf_r[i]; | |
uint tosend = (buf_w >= pos_r ? buf_w : BUF_SIZE) - pos_r; | |
if (tosend == 0) { FD_CLR(i, &writefds); continue; } | |
ssize_t n = write(i, &buf[pos_r], tosend); | |
if (n < 0) { kick(i); continue; } | |
buf_r[i] = (pos_r + n) % BUF_SIZE; | |
} | |
else { | |
// Send from greet buffer | |
uint pos_r = buf_r[i]; | |
uint tosend = greet_size - pos_r; | |
ssize_t n = write(i, &greet_buf[pos_r], tosend); | |
if (n < 0) { kick(i); continue; } | |
if (n < tosend) buf_r[i] = pos_r + n; | |
else { | |
buf_r[i] = buf_w; | |
greet_status[i] = HAVE_GREETED; | |
} | |
} | |
} | |
} | |
// Accept from server socket | |
if (FD_ISSET(server_fd, &readables)) { | |
socklen_t len = sizeof(struct sockaddr_in); | |
int client_fd = accept(server_fd, (struct sockaddr *)&sa, &len); assert_sys(client_fd); | |
printf("----- New client %d\n", client_fd); | |
if (client_fd > MAX_CLIENTS) { kick(client_fd); continue; } | |
FD_SET(client_fd, &readfds); | |
if (client_fd >= nfds) nfds = client_fd + 1; | |
buf_r[client_fd] = 0; | |
greet_status[client_fd] = WAIT_GREET; | |
} | |
// Read from stdin | |
if (FD_ISSET(0, &readables)) { | |
ssize_t n = read(0, &buf[buf_w], BUF_SIZE - buf_w); assert_sys(n); | |
if (n <= 0) return 0; | |
buf_w = (buf_w + n) % BUF_SIZE; | |
// All active sockets will be in `readfds` so just copy the entire set | |
// instead of setting them in `writefds` one-by-one | |
memcpy(&writefds, &readfds, sizeof(fd_set)); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The file 'greet' must have '\r\n' line endings and also have an empty line ('\r\n\r\n') at the end.