用Go编写网络服务器:从TCP到HTTP,一晚搞定

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么Go是网络编程的理想语言?

\n\n

如果你曾经用Python编写过网络服务器(特别是使用socket或asyncio),你就会知道代码会多么迅速地变成回调函数和事件循环的迷宫。Go(Golang)在架构层面解决了这个问题:协程和通道使得异步网络操作变得像编写顺序代码一样简单。

\n\n

在本文中,我们将通过实践来学习Go网络编程。你将了解如何在15分钟内构建一个完整的TCP聊天室、发送UDP数据包并启动一个带路由功能的HTTP服务器。所有示例都是可运行的,你可以直接复制并运行它们。

\n\n

基础:net包及其超强能力

\n\n

在Go中,所有的网络魔法都存在于标准库net包中。它提供了一个统一的接口来处理TCP、UDP、Unix套接字甚至SSL/TLS。其核心特点是Listen、Dial和Accept函数同步运行,但不会阻塞整个程序,因为每个协程都非常轻量。

\n\n

让我们直接进入正题。

\n\n

TCP服务器:10行代码的聊天室

\n\n

我们将创建一个简单的回显服务器,它接受连接并将接收到的内容原样返回。这是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("收到: %s\\n", message)\n        conn.Write([]byte("回显: " + message + "\\n"))\n    }\n}\n\nfunc main() {\n    listener, _ := net.Listen("tcp", ":8080")\n    fmt.Println("TCP服务器已在 :8080 启动")\n    for {\n        conn, _ := listener.Accept()\n        go handleConnection(conn) // 每个协程处理一个客户端\n    }\n}
\n\n

这里发生了什么?

\n
    \n
  • net.Listen("tcp", ":8080") — 在8080端口创建一个TCP监听器。
  • \n
  • 无限for循环通过Accept()接受新连接。
  • \n
  • 每个连接在独立的协程go handleConnection()中处理。
  • \n
  • 在函数内部,我们通过bufio.Scanner读取行数据并发送响应。
  • \n
\n\n

运行这段代码,打开终端并通过nc localhost 8080(或Putty)连接。任何消息都会以“回显:”为前缀返回。令人惊讶的是,这已经是一个完整的网络服务器,无需任何回调就能服务数千个客户端。

\n\n

UDP:无保障的快速投递

\n\n

如果TCP像电话通话(建立连接后通信),那么UDP就像明信片:发送后便不再关心。Go让UDP编程同样简单。

\n\n

UDP客户端和服务器

\n\n
// 服务器\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("从 %s 收到: %s\\n", clientAddr, message)\n        conn.WriteToUDP([]byte("已接收: "+message+"\\n"), clientAddr)\n    }\n}\n\n// 客户端\n// 在单独的文件或协程中:\nconn, _ := net.Dial("udp", "localhost:8081")\ndefer conn.Close()\nconn.Write([]byte("你好, UDP!"))\nbuffer := make([]byte, 1024)\nn, _ := conn.Read(buffer)\nfmt.Println(string(buffer[:n]))
\n\n

与TCP的关键区别:

\n