#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int PORT = DEFAULT_PORT; int nfds = 1; struct pollfd fds[MAX_CONNECTIONS * 2]; ht_t *USERS; ht_t *CHAN; void handle_sigint(int sig) { for (int i = 0; i < nfds; i++) { if (fds[i].fd >= 0) { close(fds[i].fd); } } exit(0); } /* modifying function: modifies `in` to point to new location after opcode */ int parse_op(char *in) { char buf[MAX_OP_LEN]; buf[0] = '\0'; char cur; cur = in[0]; bool found = false; for (int i = 0; i < 11; i++) { if (cur == '\t') { found = true; in++; break; } else if (cur == '\0') { break; } buf[i] = in[0]; in++; cur = in[0]; } return found ? encode_client_opcode(buf) : CO_UNRECOGNIZED; } array_t *parse_args(char *buf) { return NULL; } void set_non_blocking(int sock) { int flags = fcntl(sock, F_GETFL, 0); int code = fcntl(sock, F_SETFL, flags | O_NONBLOCK); die_lz(code, "fcntl()"); } int main(int argc, char **argv) { char buffer[MAX_BUFSIZE]; char res_buffer[MAX_BUFSIZE]; struct pollfd *local_fds1 = fds; struct pollfd *local_fds2 = fds + MAX_CONNECTIONS; struct pollfd *local_fds = local_fds1; /* We start by initializing our net-related structs */ signal(SIGINT, handle_sigint); int listen_sd = socket(AF_INET6, SOCK_STREAM, 0); int optval = 1; die_lz(listen_sd, "socket()"); int reuse = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)); die_lz(reuse, "setsockopt()"); int nonblock = ioctl(listen_sd, FIONBIO, (char *)&optval); die_lz(nonblock, "ioctl()"); set_non_blocking(listen_sd); /* Make new address */ struct sockaddr_in6 addr; alloc_zero(&addr, sizeof(addr)); addr.sin6_family = AF_INET6; memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); addr.sin6_port = htons(DEFAULT_PORT); int sock_bind = bind(listen_sd, (struct sockaddr *)&addr, sizeof(addr)); die_lz(sock_bind, "bind()"); int sock_listen = listen(listen_sd, 32); die_lz(sock_listen, "listen()"); /* The first fd entry is the listening socket for new connections; all the rest are sockets we read from and write to. Whenever a client connects, we know from the listening socket. */ alloc_zero(fds, sizeof(fds)); fds[0].fd = listen_sd; fds[0].events = POLLIN; int timeout = 6000; int sock_poll; bool end_server = false; /* mainloop */ do { bool compress_array = false; /* if an fd loses connection, we want to remove all -1's from the fds array */ sock_poll = poll(local_fds, nfds, timeout); die_lz(sock_poll, "poll()"); for (int i = 0; i < nfds; i++) { if (local_fds[i].revents == 0) continue; if (local_fds[i].revents != POLLIN) { end_server = true; break; } if (local_fds[i].fd == listen_sd) { printf("socket is readable\n"); int new_sd; do { new_sd = accept(listen_sd, NULL, NULL); if (new_sd < 0 && errno != EWOULDBLOCK) { perror("accept() failed"); end_server = true; break; } local_fds[nfds].fd = new_sd; local_fds[nfds].events = POLLIN; nfds++; } while (new_sd >= 0); } else { bool close_conn = false; int fd_recv; int fd_send; fd_recv = recv(local_fds[i].fd, buffer, sizeof(buffer), 0); if (fd_recv < 0 && errno != EWOULDBLOCK) { perror("recv() failed"); close_conn = true; } else if (fd_recv == 0) { printf("Connection closed\n"); close_conn = true; } else { /* echo server -- replace this with buffer parsing */ /* TODO: reply to client based on user input */ fd_send = send(local_fds[i].fd, buffer, fd_recv, 0); if (fd_send < 0) { perror("send()"); close_conn = true; } } if (close_conn) { close(local_fds[i].fd); local_fds[i].fd = 0; compress_array = true; } } } if (compress_array) { printf("switching...\n"); int cur_nfds = nfds; nfds = 0; for (int i = 0; i < cur_nfds; i++) { if (local_fds[i].fd != 0) { local_fds2[nfds] = local_fds[i]; nfds ++; } } local_fds1 = local_fds2; local_fds2 = local_fds; local_fds = local_fds1; alloc_zero(local_fds2, MAX_CONNECTIONS); } } while (!end_server); for (int i = 0; i < nfds; i++) { if (fds[i].fd >= 0) { close(fds[i].fd); } } return 0; }