Programación de redes en C++: de sockets a asincronía
\n\n¿Alguna vez se ha preguntado cómo funcionan los chats, los juegos en línea o los servidores web? En el centro de cada uno de ellos se encuentra la comunicación en red. Y si desea escribir aplicaciones de red rápidas, fiables y de bajo nivel, la programación de redes en C++ es lo que necesita. En este artículo no solo repasaremos la teoría, sino que escribiremos código real: desde un simple chat TCP hasta un servidor asíncrono con Boost.Asio. ¡Prepare sus IDE, será interesante!
\n\n¿Por qué C++ para redes? Tres razones
\nMuchos eligen Python o Java para tareas de red, pero C++ ofrece tres ventajas clave:
\n- \n
- Velocidad: Sobrecarga mínima, acceso directo a las llamadas al sistema (syscalls). \n
- Control: Usted gestiona la memoria, los búferes y los tiempos manualmente. \n
- Ecosistema:
Boost.Asio,libcurl,Poco— bibliotecas potentes para cualquier tarea. \n
Pero un gran poder conlleva una gran responsabilidad: un solo error en la gestión de un socket y su servidor se caerá. Analicemos cómo evitarlo.
\n\nFundamentos: sockets y modelo OSI
\nAntes de escribir código, recordemos qué es un socket. Es una abstracción que permite a una aplicación enviar y recibir datos a través de la red. En C++ trabajamos a través de sys/socket.h (Linux) o winsock2.h (Windows).
TCP vs UDP: ¿cuándo usar cada uno?
\n- \n
- TCP (SOCK_STREAM): Garantiza la entrega y el orden de los paquetes. Ideal para chats, HTTP, bases de datos. \n
- UDP (SOCK_DGRAM): Más rápido, pero sin garantías. Se utiliza en streaming, DNS, juegos en línea (donde es más fácil tolerar la pérdida de paquetes que la latencia). \n
Escribimos un servidor y cliente TCP en C++ (sockets puros)
\nCreemos un servidor eco simple. Recibe un mensaje y lo envía de vuelta. Es el clásico «Hola, mundo» en la programación de redes en C++.
\n\nParte del servidor (echo_server.cpp)
\n#include <iostream>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <unistd.h>\n#include <cstring>\n\nint main() {\n int server_fd, client_fd;\n struct sockaddr_in address;\n int opt = 1;\n int addrlen = sizeof(address);\n char buffer[1024] = {0};\n\n // 1. Creamos el socket\n if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {\n perror("socket failed");\n exit(EXIT_FAILURE);\n }\n\n // 2. Vinculamos al puerto (opción reuse port)\n if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {\n perror("setsockopt");\n exit(EXIT_FAILURE);\n }\n address.sin_family = AF_INET;\n address.sin_addr.s_addr = INADDR_ANY;\n address.sin_port = htons(8080);\n\n if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {\n perror("bind failed");\n exit(EXIT_FAILURE);\n }\n\n // 3. Escuchamos conexiones entrantes\n if (listen(server_fd, 3) < 0) {\n perror("listen");\n exit(EXIT_FAILURE);\n }\n\n std::cout << "Servidor iniciado en el puerto 8080. Esperando conexiones..." << std::endl;\n\n // 4. Aceptamos al cliente\n if ((client_fd = accept(server_fd, (struct sockaddr *)&address,\n (socklen_t*)&addrlen)) < 0) {\n perror("accept");\n exit(EXIT_FAILURE);\n }\n\n // 5. Leemos y enviamos de vuelta (eco)\n int valread = read(client_fd, buffer, 1024);\n std::cout << "Recibido: " << buffer << std::endl;\n send(client_fd, buffer, strlen(buffer), 0);\n std::cout << "Respuesta eco enviada" << std::endl;\n\n close(client_fd);\n close(server_fd);\n return 0;\n}\n\nParte del cliente (echo_client.cpp)
\n#include <