Files
ratio-miniserv/mini_serv.c

188 lines
3.8 KiB
C

#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFSIZE 200000
typedef struct {
int id;
char *msg;
} client_t;
client_t clients[FD_SETSIZE];
char recv_buf[BUFSIZE];
char send_buf[BUFSIZE];
int maxfd = 0;
int lastid = -1;
int sockfd = -1;
fd_set main_fd, read_fd, write_fd;
int extract_message(char **buf, char **msg) {
char *newbuf;
int i;
*msg = 0;
if (*buf == 0)
return (0);
i = 0;
while ((*buf)[i]) {
if ((*buf)[i] == '\n') {
newbuf = calloc(1, sizeof(*newbuf) * (strlen(*buf + i + 1) + 1));
if (newbuf == 0)
return (-1);
strcpy(newbuf, *buf + i + 1);
*msg = *buf;
(*msg)[i + 1] = 0;
*buf = newbuf;
return (1);
}
i++;
}
return (0);
}
char *str_join(char *buf, char *add) {
char *newbuf;
int len;
if (buf == 0)
len = 0;
else
len = strlen(buf);
newbuf = malloc(sizeof(*newbuf) * (len + strlen(add) + 1));
if (newbuf == 0)
return (0);
newbuf[0] = 0;
if (buf != 0)
strcat(newbuf, buf);
free(buf);
strcat(newbuf, add);
return (newbuf);
}
void send_all(int cli_fd) {
for (int i = 0; i <= maxfd; i++)
if (FD_ISSET(i, &main_fd) && i != sockfd && i != cli_fd)
send(i, send_buf, strlen(send_buf), 0);
}
void append_client(int cli_fd) {
lastid++;
clients[cli_fd].id = lastid;
clients[cli_fd].msg = NULL;
FD_SET(cli_fd, &main_fd);
bzero(send_buf, BUFSIZE);
sprintf(send_buf, "server: client %d just arrived\n", lastid);
send_all(cli_fd);
}
void rm_client(int cli_fd) {
bzero(send_buf, BUFSIZE);
sprintf(send_buf, "server: client %d, just left\n", clients[cli_fd].id);
send_all(cli_fd);
clients[cli_fd].id = -1;
if (clients[cli_fd].msg != NULL) {
free(clients[cli_fd].msg);
clients[cli_fd].msg = NULL;
}
FD_CLR(cli_fd, &main_fd);
close(cli_fd);
}
int read_client(int cli_fd) {
bzero(recv_buf, BUFSIZE);
int bytes = recv(cli_fd, recv_buf, BUFSIZE, 0);
if (bytes <= 0) {
if (strlen(clients[cli_fd].msg) > 0) {
for (int i = 0; i < maxfd; i++)
if (FD_ISSET(i, &main_fd) && i != sockfd && i != cli_fd)
send(i, clients[cli_fd].msg, strlen(clients[cli_fd].msg), 0);
}
rm_client(cli_fd);
return (0);
}
recv_buf[bytes] = 0;
clients[cli_fd].msg = str_join(clients[cli_fd].msg, recv_buf);
char *line = NULL;
while (extract_message(&clients[cli_fd].msg, &line)) {
bzero(send_buf, BUFSIZE);
sprintf(send_buf, "client %d: %s", clients[cli_fd].id, line);
send_all(cli_fd);
free(line);
}
return (1);
}
int send_error(void) {
for (int i = 0; i < maxfd; i++)
if (FD_ISSET(i, &main_fd))
close(i);
close(sockfd);
write(STDERR_FILENO, "Fatal error\n", 12);
return (EXIT_FAILURE);
}
int main(int ac, char **av) {
if (ac != 2) {
write(STDERR_FILENO, "Wrong number of argument\n", 25);
return (EXIT_FAILURE);
}
socklen_t len;
struct sockaddr_in servaddr, cli;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
return (send_error());
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(2130706433); // 127.0.0.1
servaddr.sin_port = htons(atoi(av[1]));
if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
return (send_error());
if (listen(sockfd, SOMAXCONN) != 0)
return (send_error());
FD_ZERO(&main_fd);
FD_SET(sockfd, &main_fd);
maxfd = sockfd;
while (1) {
read_fd = write_fd = main_fd;
int ret = select(maxfd + 1, &read_fd, &write_fd, NULL, NULL);
if (ret < 0)
return (send_error());
for (int i = 0; i <= maxfd; i++) {
if (FD_ISSET(i, &read_fd)) {
if (i == sockfd) {
len = sizeof(cli);
int cli_fd = accept(i, (struct sockaddr *)&cli, &len);
if (cli_fd < 0)
return (send_error());
if (cli_fd > maxfd)
maxfd = cli_fd;
append_client(cli_fd);
break;
} else {
if (!read_client(i))
break;
}
}
}
}
}