Last active
October 9, 2022 13:50
-
-
Save SebastienWae/d7edc1bc68b8136f56fc3023ae207744 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 <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