Skip to content

Instantly share code, notes, and snippets.

@SebastienWae
Last active October 9, 2022 13:50
Show Gist options
  • Save SebastienWae/d7edc1bc68b8136f56fc3023ae207744 to your computer and use it in GitHub Desktop.
Save SebastienWae/d7edc1bc68b8136f56fc3023ae207744 to your computer and use it in GitHub Desktop.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <fcntl.h> // TEST ONLY
int clients[FD_SETSIZE];
int serverSocket;
void putstr_fd(char *str, int fd) {
write(fd, str, strlen(str));
}
void fatal_error() {
putstr_fd("Fatal error\n", 2);
exit(1);
}
void close_all() {
close(serverSocket);
for (size_t i = 0; i < FD_SETSIZE; i++) {
if (clients[i] > 0)
close(i);
}
}
void send_all(int sender, char* buf, int len) {
for (int i = 0; i < FD_SETSIZE; i++) {
if (i != sender && i != serverSocket && clients[i] != -1) {
int total = 0;
int bytesleft = len;
int n;
while(total < len) {
n = send(i, buf+total, bytesleft, 0);
if (n == -1) {
close_all();
fatal_error();
}
total += n;
bytesleft -= n;
}
}
}
}
int main(int argc, char **argv) {
if (argc < 2) {
putstr_fd("Wrong number of arguments", 2);
exit(1);
}
memset(&clients, -1, sizeof(int) * FD_SETSIZE);
fd_set sockets, readReady;
FD_ZERO(&sockets);
FD_ZERO(&readReady);
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0)
fatal_error();
fcntl(serverSocket, F_SETFL, O_NONBLOCK); // TEST ONLY
FD_SET(serverSocket, &sockets);
struct sockaddr_in addr;
socklen_t addr_size = sizeof(addr);
memset(&addr, 0, addr_size);
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[1]));
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(serverSocket, (const struct sockaddr *)&addr, addr_size) < 0) {
close(serverSocket);
fatal_error();
}
if (listen(serverSocket, SOMAXCONN) < 0) {
close(serverSocket);
fatal_error();
}
int maxFd = serverSocket;
int currId = 0;
while(1) {
readReady = sockets;
if (select(maxFd + 1, &readReady, NULL, NULL, NULL) < 0) { // check for write ready?
close_all();
fatal_error();
}
for(int fd = 0; fd <= maxFd; fd++) {
if (FD_ISSET(fd, &readReady)) {
if (fd == serverSocket) {
int newFd = accept(serverSocket, (struct sockaddr *)&addr, &addr_size);
if (newFd < 0) {
close_all();
fatal_error();
}
fcntl(newFd, F_SETFL, O_NONBLOCK); // TEST ONLY
FD_SET(newFd, &sockets);
maxFd = maxFd > newFd ? maxFd : newFd;
clients[newFd] = currId;
char buf[33];
int len = sprintf(buf, "server: client %d just arrived\n", currId);
send_all(newFd, buf, len);
++currId;
} else {
char message[65536]; // max MTU
int res = recv(fd, message, 65536, 0);
message[res] = 0;
if (res < 0) {
close_all();
fatal_error();
}
if (res == 0) {
char buf[30];
int len = sprintf(buf, "server: client %d just left\n", clients[fd]);
send_all(fd, buf, len);
FD_CLR(fd, &sockets);
close(fd);
clients[fd] = -1;
} else {
char *end;
int offset = 0;
while (message[offset]) {
end = strstr(&message[offset], "\n");
if (end)
*end = 0;
else
end = &message[offset] + strlen(&message[offset]);
char buf[end - &message[offset] + 14];
int len = sprintf(buf, "client %d: %s\n", clients[fd], &message[offset]);
send_all(fd, buf, len);
offset += end - &message[offset] + 1;
}
}
}
}
}
}
close_all();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment