Skip to content

Instantly share code, notes, and snippets.

@Shankhara
Created September 25, 2022 14:43
Show Gist options
  • Save Shankhara/d63cb517c745cef1bbe5115beca7fd4c to your computer and use it in GitHub Desktop.
Save Shankhara/d63cb517c745cef1bbe5115beca7fd4c to your computer and use it in GitHub Desktop.
#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