Created
September 25, 2022 14:43
-
-
Save Shankhara/d63cb517c745cef1bbe5115beca7fd4c 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 <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <netdb.h> | |
#include <sys/socket.h> | |
#include <sys/select.h> | |
#include <netinet/in.h> | |
#undef max | |
#define max(x, y) (x > y ? x : y) | |
#define BUF_SIZE 10 | |
fd_set rfds; | |
int sockfd = 0; | |
typedef struct s_list | |
{ | |
int id; | |
int fd; | |
struct s_list *next; | |
} t_list; | |
int list_len(t_list *list) | |
{ | |
int i = 0; | |
while (list) | |
{ | |
i++; | |
list = list->next; | |
} | |
return i; | |
} | |
void shut_down(t_list *clients) | |
{ | |
FD_ZERO(&rfds); | |
while (clients) | |
{ | |
t_list *to_remove = clients; | |
close(clients->fd); | |
clients = clients->next; | |
free(to_remove); | |
} | |
} | |
void ft_error(t_list *clients) | |
{ | |
char msg[16] = "Fatal error\n"; | |
write(2, msg, strlen(msg)); | |
if (sockfd > 1) | |
close(sockfd); | |
if (clients) | |
shut_down(clients); | |
exit(EXIT_FAILURE); | |
} | |
void wrong_argument_error() | |
{ | |
char msg[32] = "Wrong number of arguments\n"; | |
write(2, msg, strlen(msg)); | |
exit(EXIT_FAILURE); | |
} | |
void tcp_connect(char *arg) | |
{ | |
int port = atoi(arg); | |
if (!port) | |
wrong_argument_error(); | |
sockfd = socket(AF_INET, SOCK_STREAM, 0); | |
if (sockfd == -1) | |
ft_error(NULL); | |
struct sockaddr_in servaddr; | |
bzero(&servaddr, sizeof(servaddr)); | |
servaddr.sin_family = AF_INET; | |
servaddr.sin_addr.s_addr = htonl(2130706433); //127.0.0.1 | |
servaddr.sin_port = htons(port); | |
int ret = bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)); | |
if (ret == -1) | |
ft_error(NULL); | |
ret = listen(sockfd, 10); | |
if (ret == -1) | |
ft_error(NULL); | |
} | |
int fd_reset(t_list *clients) | |
{ | |
FD_ZERO(&rfds); | |
FD_SET(sockfd, &rfds); | |
int nfds = sockfd; | |
while (clients) | |
{ | |
int fd = clients->fd; | |
FD_SET(fd, &rfds); | |
nfds = max(fd, nfds); | |
clients = clients->next; | |
} | |
return (nfds); | |
} | |
t_list *create_client(int fd, t_list *clients) | |
{ | |
static int client_count; | |
t_list *client = malloc(sizeof(t_list)); | |
if (!client) | |
ft_error(clients); | |
client->id = client_count++; | |
client->fd = fd; | |
client->next = NULL; | |
return (client); | |
} | |
void add_client(t_list **clients, t_list *new_client) | |
{ | |
if (!*clients) | |
{ | |
*clients = new_client; | |
return; | |
} | |
t_list *iter = *clients; | |
while (iter->next) | |
iter = iter->next; | |
iter->next = new_client; | |
} | |
void message_all(char *msg, t_list *client, t_list *clients, int msg_len) | |
{ | |
while (clients) | |
{ | |
if (clients != client) | |
write(clients->fd, msg, msg_len); | |
clients = clients->next; | |
} | |
} | |
void server_announce(t_list *client, t_list *clients, int pos) | |
{ | |
char msg[32]; | |
if (!pos) | |
sprintf(msg, "server: client %d just arrived\n", client->id); | |
else | |
sprintf(msg, "server: client %d just left\n", client->id); | |
message_all(msg, client, clients, strlen(msg)); | |
} | |
void welcome_client(t_list **clients) | |
{ | |
int fd = accept(sockfd, NULL, NULL); | |
if (fd > 0) | |
{ | |
t_list *new_client = create_client(fd, *clients); | |
add_client(clients, new_client); | |
server_announce(new_client, *clients, 0); | |
} | |
} | |
int client_moved(t_list *clients) | |
{ | |
while (clients) | |
{ | |
int fd = clients->fd; | |
if (FD_ISSET(fd, &rfds)) | |
return (fd); | |
clients = clients->next; | |
} | |
return 0; | |
} | |
t_list *get_client(t_list *clients, int fd) | |
{ | |
while (clients) | |
{ | |
if (clients->fd == fd) | |
return clients; | |
clients = clients->next; | |
} | |
return NULL; | |
} | |
void remove_client(int fd, t_list **clients) | |
{ | |
FD_CLR(fd, &rfds); | |
close(fd); | |
t_list *to_remove = get_client(*clients, fd); | |
server_announce(to_remove, *clients, 1); | |
if (*clients == to_remove) | |
{ | |
*clients = (*clients)->next; | |
free(to_remove); | |
return; | |
} | |
t_list *iter = *clients; | |
while (iter->next != to_remove) | |
iter = iter->next; | |
iter->next = to_remove->next; | |
free(to_remove); | |
} | |
void send_by_one(char c, t_list *client, t_list *clients) | |
{ | |
while (clients) | |
{ | |
if (clients != client) | |
write(clients->fd, &c, 1); | |
clients = clients->next; | |
} | |
} | |
void spreader(char *buf, int fd, t_list *clients, int nbytes) | |
{ | |
t_list *mover = get_client(clients, fd); | |
char str[32]; | |
sprintf(str, "client %d: ", mover->id); | |
int msg_len = strlen(str); | |
static int prev_nbytes; | |
if (prev_nbytes != BUF_SIZE) | |
message_all(str, mover, clients, msg_len); | |
prev_nbytes = nbytes; | |
int i = 0; | |
while (buf[i]) | |
{ | |
send_by_one(buf[i], mover, clients); | |
if (buf[i] == '\n') | |
{ | |
if (!buf[i + 1]) | |
return; | |
message_all(str, mover, clients, msg_len); | |
} | |
i++; | |
} | |
} | |
int main(int ac, char **av) | |
{ | |
if (ac != 2) | |
wrong_argument_error(); | |
tcp_connect(av[1]); | |
t_list *clients = NULL; | |
char buf[BUF_SIZE] = {0}; | |
int select_moves = 0; | |
while (1) | |
{ | |
int nfds = fd_reset(clients); | |
select(nfds + 1, &rfds, NULL, NULL, NULL); | |
printf("select: %d\n", select_moves++); | |
if (FD_ISSET(sockfd, &rfds)) | |
{ | |
welcome_client(&clients); | |
continue; | |
} | |
int fd = client_moved(clients); | |
if (fd) | |
{ | |
int nbytes = recv(fd, buf, BUF_SIZE, 0); | |
spreader(buf, fd, clients, nbytes); | |
bzero(buf, nbytes); | |
if (nbytes == 0) | |
{ | |
remove_client(fd, &clients); | |
continue; | |
} | |
} | |
} | |
} | |
//send the max you can, not one by one ! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment