No es un blog de cine, ¿eh? |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <errno.h> #define BACKLOG 10 // para listen() #define MYBUFSIZE 1024 int main(int argc, char *argv[]) { // test argumentos if (argc != 2) { // error args printf("%s: numero argumentos equivocado\n", argv[0]); printf("uso: %s port [i.e.: %s 9999]\n", argv[0], argv[0]); return EXIT_FAILURE; } // crea un socket int my_socket; if ((my_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { // error socket() printf("%s: could not create socket (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } // prepara la struct sockaddr_in para este server struct sockaddr_in server; // (local) server socket info server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(atoi(argv[1])); // bind informaciones del server al socket if (bind(my_socket, (struct sockaddr *)&server, sizeof(server)) < 0) { // error bind() printf("%s: bind failed (%s)", argv[0], strerror(errno)); return EXIT_FAILURE; } // start escucha con una cola de max BACKLOG conexiones if (listen(my_socket, BACKLOG) < 0) { // error listen() printf("%s: listen failed (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } // acepta conexiones de un client entrante printf("%s: espera connexiones entrantes...\n", argv[0]); socklen_t socksize = sizeof(struct sockaddr_in); struct sockaddr_in client; // (remote) client socket info int client_sock; if ((client_sock = accept(my_socket, (struct sockaddr *)&client, &socksize)) < 0) { // error accept() printf("%s: accept failed (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } // loop di ricezione messaggi dal client int read_size; char client_msg[MYBUFSIZE]; while ((read_size = recv(client_sock, client_msg, MYBUFSIZE, 0)) > 0 ) { // send messaggio di ritorno al client printf("%s: recibido mensaje del sock %d: %s\n", argv[0], client_sock, client_msg); char server_msg[MYBUFSIZE]; sprintf(server_msg, "me has escrito: %s", client_msg); write(client_sock, server_msg, strlen(server_msg)); // clear buffer memset(client_msg, 0, MYBUFSIZE); } // loop terminado: test motivo if (read_size < 0) { // error recv() printf("%s: recv failed\n", argv[0]); return EXIT_FAILURE; } else if (read_size == 0) { // Ok: el client se ah desconectado printf("%s: client disconnected\n", argv[0]); } // salgo con Ok return EXIT_SUCCESS; }
Ok, como se nota es ampliamente comentado y así se auto-explica, por lo cual no voy a detenerme sobre las instrucciones y/o grupos de instrucciones (¡leer los comentarios! ¡Están ahí para eso!), pero voy a añadir, solamente, algunos detalles estructurales.
la estructura es la clásica y básica de un Socket Server:
- socket() - crea un socket
- prepara la estructura sockaddr_in para este servidor
- bind() - bind informaciones del servidor al socket
- listen() - empieza a escuchar con una cola de max BACKLOG conexiones
- accept() - acepta conexiones entrantes de un cliente
- recv() - bucle de recepción mensajes del cliente
Notar que, además, el main() comienza con una prueba de los argumentos con anexo ejemplo de uso en caso de error: esto le da al código un aire profesional que no puede faltar en una aplicación de este tipo.
Desde el punto de vista del estilo, he escrito este código sin respetar mi estilo favorito (hay múltiples puntos de salida) pero para un programa casi-secuencial como esto no es un estilo despreciable, y además... ¡me ha venido así! Por otra parte en su momento dije que se necesita un poco de elasticidad, que no hay que ser demasiado rígidos.
Un último detalle sobre la #define BACKLOG: encontrar el valor apropiado es un tema casi filosófico, así que os remito a una buena descripción que he encontrado en la red. Nos vemos para el cliente y, como siempre, no contengáis la respiración esperando...
¡Hasta el próximo post!