#include #include #include #include #include #include #include #include #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; } } } } }