188 lines
3.8 KiB
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|