mini_serv · Compañero

▶ Aprender mini_serv (Compañero) paso a paso · explicación de mini_serv

Otras versiones: Optim

Código línea a línea

Tres columnas: el código, el trozo del subject o del main que obliga a escribir esa línea, y el por qué.

mini_serv.c
CódigoSubjectPor qué
#include <netinet/in.h>127.0.0.1struct sockaddr_in, htons, htonl.
#include <stdio.h>sprintfsprintf.
#include <stdlib.h>malloc, realloc, free, calloc, bzero, atoi, sprintf, strlen, exitmalloc, realloc, free, atoi, exit.
#include <string.h>strstrstrlen, strcpy, strstr, bzero.
#include <sys/socket.h>socket, accept, listen, send, recv, bindsocket, bind, listen, accept, send, recv.
#include <unistd.h>write, closewrite, close.
int sock, maxfd, next_id;the first client will receive the id 0 and each new client will received the last client id + 1El socket que escucha, el fd más alto (para select) y el próximo id. Globales: una copia de cada cosa, sin pasarlas por todas las funciones.
int ids[1024], lens[1024];the client will be given an id.Versión compañero, por fd: el id del cliente y cuántas letras lleva de la línea a medias. 1024 = FD_SETSIZE, lo máximo que vigila select.
char msgs[1024][400000];a single message can contains multiple \nLa línea a medias de cada cliente, fija y sin malloc. Las globales empiezan a 0 y el sistema solo da memoria a lo que se toca: arranca con ~1 MB.
char out[400100], in[4096];out: el mensaje a repartir (línea + prefijo). in: lo que trae cada recv.
fd_set all, rd, wr;use selectall: todos los fds abiertos. rd/wr: la copia que select rellena con los listos para leer / escribir.
void fatal(char *msg)it should write in stderr "Fatal error" followed by a \n and exit with status 1Un solo sitio para los dos errores: recibe el texto ya con su \n.
{
write(2, msg, strlen(msg));write in stderr2 = stderr.
exit(1);exit with status 1
}
void send_all(int from)a message is sent to all the client that was connected to the serverManda out a todos menos a from.
{
for (int fd = 0; fd <= maxfd; fd++)Recorro todos los fds posibles: los abiertos están en all.
if (fd != from && FD_ISSET(fd, &wr))if they don't read your message you must NOT disconnect themNi al que lo manda, y solo si select dice que se puede escribir sin bloquear. El de escucha nunca está en wr.
send(fd, out, strlen(out), MSG_NOSIGNAL);sendMSG_NOSIGNAL: si el cliente ya se cayó, send falla sin SIGPIPE y el servidor sigue. El error se ignora: tras listen solo es fatal quedarse sin memoria.
}
void add_client(void)When a client connect to the server:Hay alguien llamando al socket de escucha.
{
int fd = accept(sock, NULL, NULL);acceptaccept crea un fd nuevo para ese cliente. No necesito su dirección: NULL, NULL.
if (fd < 0)before the program start accepting connectionSi falla ya estamos aceptando conexiones: no es fatal, lo ignoro.
return ;
if (fd >= 1024)No cabe en los arrays ni en el fd_set: lo cierro y sigo.
{
close(fd);Memory or fd leaks are forbiddenCada accept con su close: fd que no se usa, se cierra.
return ;
}
if (fd > maxfd)select necesita el fd más alto + 1.
maxfd = fd;
ids[fd] = next_id++;the first client will receive the id 0 and each new client will received the last client id + 1Primero 0, luego 1, 2… aunque alguien se vaya, el contador no baja.
lens[fd] = 0;Empieza sin nada a medias (el fd puede venir de un cliente que se fue).
FD_SET(fd, &all);Entra en el conjunto que vigila select.
sprintf(out, "server: client %d just arrived\n", ids[fd]);"server: client %d just arrived\n"%d = el id, no el fd.
send_all(fd);a message is sent to all the client that was connected to the serverA todos menos a fd.
}
void remove_client(int fd)When a client disconnect from the server:recv devolvió 0 (o error): se fue.
{
sprintf(out, "server: client %d just left\n", ids[fd]);"server: client %d just left\n"El aviso antes de cerrar, mientras sé su id.
send_all(fd);a message is sent to all the client that was connected to the serverA todos menos a fd.
FD_CLR(fd, &all);select deja de vigilarlo.
close(fd);Memory or fd leaks are forbiddenCada accept con su close: fd que no se usa, se cierra.
}
void read_client(int fd)clients must be able to send messages to your program.select dice que hay algo que leer en este fd.
{
int n = recv(fd, in, sizeof(in), 0);recvLo que haya llegado: media línea o varias.
if (n <= 0)When a client disconnect from the server:0 = el cliente cerró; -1 = error. En ambos casos fuera.
{
remove_client(fd);
return ;
}
for (int i = 0; i < n; i++)a single message can contains multiple \nLetra a letra lo recibido.
{
if (in[i] != '\n')Aún no acaba la línea: la guardo.
{
if (lens[fd] < 399999)Tope: una línea gigante se corta en vez de salirse del array (deja sitio al \0).
msgs[fd][lens[fd]++] = in[i];
continue ;Siguiente vuelta: tras un error de select, vuelvo a esperar; dentro de un for, paso al siguiente.
}
msgs[fd][lens[fd]] = '\0';Llegó el \n: cierro el string.
sprintf(out, "client %d: %s\n", ids[fd], msgs[fd]);"client %d: " before every line!Prefijo + línea + el \n que no guardé.
send_all(fd);a message is sent to all the client that was connected to the serverA todos menos a fd.
lens[fd] = 0;Empieza sin nada a medias (el fd puede venir de un cliente que se fue).
}
}
int main(int ac, char **av)This program will take as first argument the port to bind to
{
if (ac != 2)If no argument is givenPrograma + puerto.
fatal("Wrong number of arguments\n");"Wrong number of arguments" followed by a \n
sock = socket(AF_INET, SOCK_STREAM, 0);sockfd = socket(AF_INET, SOCK_STREAM, 0);IPv4 + TCP. Igual que en el main.c.
if (sock < 0)If a System Calls returns an error before the program start accepting connection
fatal("Fatal error\n");If a System Calls returns an error before the program start accepting connectionsocket, bind o listen fallan: fatal.
struct sockaddr_in addr;struct sockaddr_in servaddr, cli;La dirección donde escucho.
bzero(&addr, sizeof(addr));bzero(&servaddr, sizeof(servaddr));A ceros antes de rellenar.
addr.sin_family = AF_INET;servaddr.sin_family = AF_INET;IPv4.
addr.sin_addr.s_addr = htonl(2130706433);Your program must only listen to 127.0.0.1
servaddr.sin_addr.s_addr = htonl(2130706433);
2130706433 = 0x7F000001 = 127.0.0.1. htonl: a orden de red. Copiado del main.c.
addr.sin_port = htons(atoi(av[1]));the port to bind toEl puerto del argumento, a orden de red.
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0 || listen(sock, 128) < 0)If a System Calls returns an error before the program start accepting connection, it should write in stderr "Fatal error"bind: me quedo esa dirección. listen: empiezo a aceptar (128 en cola). Cualquiera falla, fatal.
fatal("Fatal error\n");If a System Calls returns an error before the program start accepting connectionsocket, bind o listen fallan: fatal.
maxfd = sock;Por ahora el único fd.
FD_ZERO(&all);Conjunto vacío…
FD_SET(sock, &all);…con el socket de escucha dentro.
while (1)El servidor no acaba nunca.
{
rd = wr = all;use selectselect modifica los conjuntos: le paso copias y conservo all.
if (select(maxfd + 1, &rd, &wr, NULL, NULL) < 0)Your program must be non-blocking
use select and NEVER check EAGAIN
Espera hasta que algún fd esté listo para leer o escribir. Sin timeout (NULL): puede esperar para siempre.
continue ;Siguiente vuelta: tras un error de select, vuelvo a esperar; dentro de un for, paso al siguiente.
for (int fd = 0; fd <= maxfd; fd++)Recorro todos los fds posibles: los abiertos están en all.
{
if (!FD_ISSET(fd, &rd))Este fd no tiene nada que leer.
continue ;Siguiente vuelta: tras un error de select, vuelvo a esperar; dentro de un for, paso al siguiente.
if (fd == sock)When a client connect to the server:Lectura en el socket de escucha = una conexión nueva.
add_client();
else
read_client(fd);clients must be able to send messages to your program.En un cliente = datos (o se ha ido).
}
}
}