Escribiendo un servidor de red en Go: de TCP a HTTP en una tarde

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué Go es el lenguaje ideal para la programación de redes?

\n\n

Si alguna vez has escrito un servidor de red en Python (especialmente usando socket o asyncio), sabes lo rápido que el código puede convertirse en un laberinto de callbacks y bucles de eventos. Go (Golang) resuelve este problema a nivel arquitectónico: las gorutinas y los canales hacen que el trabajo asíncrono con la red sea tan simple como escribir código secuencial.

\n\n

En este artículo, analizaremos la programación de redes en Go en la práctica. Aprenderás cómo armar un chat TCP completo en 15 minutos, enviar un paquete UDP y ejecutar un servidor HTTP con enrutamiento. Todos los ejemplos son funcionales, se pueden copiar y ejecutar.

\n\n

Fundamentos: el paquete net y su superpoder

\n\n

En Go, toda la magia de redes vive en el paquete estándar net. Proporciona una interfaz unificada para trabajar con TCP, UDP, sockets Unix e incluso SSL/TLS. La característica principal es que las funciones Listen, Dial y Accept funcionan de forma síncrona, pero no bloquean todo el programa porque cada gorutina es liviana.

\n\n

Vayamos directo al grano.

\n\n

Servidor TCP: chat en 10 líneas

\n\n

Crearemos un simple servidor eco que acepta conexiones y envía de vuelta lo que recibe. Esto es un clásico de la programación de redes en Go.

\n\n
package main\n\nimport (\n    "bufio"\n    "fmt"\n    "net"\n)\n\nfunc handleConnection(conn net.Conn) {\n    defer conn.Close()\n    scanner := bufio.NewScanner(conn)\n    for scanner.Scan() {\n        message := scanner.Text()\n        fmt.Printf("Recibido: %s\\n", message)\n        conn.Write([]byte("Eco: " + message + "\\n"))\n    }\n}\n\nfunc main() {\n    listener, _ := net.Listen("tcp", ":8080")\n    fmt.Println("Servidor TCP iniciado en :8080")\n    for {\n        conn, _ := listener.Accept()\n        go handleConnection(conn) // cada gorutina es un cliente separado\n    }\n}
\n\n

¿Qué sucede aquí?

\n
    \n
  • net.Listen("tcp", ":8080") — crea un listener TCP en el puerto 8080.
  • \n
  • El bucle infinito for acepta nuevas conexiones a través de Accept().
  • \n
  • Cada conexión se maneja en una gorutina separada go handleConnection().
  • \n
  • Dentro de la función, leemos líneas usando bufio.Scanner y enviamos una respuesta.
  • \n
\n\n

Ejecuta este código, abre una terminal y conéctate a través de nc localhost 8080 (o Putty). Cualquier mensaje regresará con el prefijo "Eco:". Sorprendentemente, esto ya es un servidor de red completo, capaz de atender a miles de clientes sin un solo callback.

\n\n

UDP: entrega rápida sin garantías

\n\n

Si TCP es como una llamada telefónica (estableces conexión y hablas), UDP es como las postales: las envías y te olvidas. Go hace que trabajar con UDP sea igual de simple.

\n\n

Cliente y servidor UDP

\n\n
// Servidor\npackage main\n\nimport (\n    "fmt"\n    "net"\n)\n\nfunc main() {\n    addr, _ := net.ResolveUDPAddr("udp", ":8081")\n    conn, _ := net.ListenUDP("udp", addr)\n    defer conn.Close()\n\n    buffer := make([]byte, 1024)\n    for {\n        n, clientAddr, _ := conn.ReadFromUDP(buffer)\n        message := string(buffer[:n])\n        fmt.Printf("Recibido de %s: %s\\n", clientAddr, message)\n        conn.WriteToUDP([]byte("Recibido: "+message+"\\n"), clientAddr)\n    }\n}\n\n// Cliente\n// En un archivo separado o gorutina:\nconn, _ := net.Dial("udp", "localhost:8081")\ndefer conn.Close()\nconn.Write([]byte("¡Hola, UDP!"))\nbuffer := make([]byte, 1024)\nn, _ := conn.Read(buffer)\nfmt.Println(string(buffer[:n]))
\n\n

Diferencia clave con TCP:

\n