Skip to content

《Go Cookbook CN》系列 16:TCP/UDP服务器与客户端开发全解

约 3328 字大约 11 分钟

《Go Cookbook CN》系列Go语言

2026-04-02

本篇聚焦Go语言网络编程的核心基础,系统讲解TCP/UDP两大传输层协议的服务端与客户端开发流程,结合net包的核心API拆解实战步骤,同时配套netcat工具的测试方法。学完本篇,你将掌握Go中TCP/UDP网络程序的完整开发逻辑,理解套接字编程的核心原理,并能独立实现可运行的TCP/UDP服务端与客户端。

【本篇核心收获】

  • 理解OSI/TCP/IP协议分层模型、IP地址(IPv4/IPv6)、端口的核心概念,以及TCP/UDP协议的核心差异
  • 掌握Go net包创建TCP服务器/客户端的完整流程(监听、接受连接、数据读写),并能处理大尺寸数据
  • 掌握Go net包创建UDP服务器/客户端的完整流程(数据包监听、读写、地址交互)
  • 学会使用netcat(nc)工具测试TCP/UDP网络程序的连通性与功能有效性
  • 了解Go网络编程的进阶优化点(IPv6支持、底层TCP/UDP连接精细控制、goroutine处理多连接)

16.0 网络编程核心基础认知

计算机网络的核心价值在于多设备协同计算与资源共享,而网络协议是设备通信的规则,通常被抽象为分层模型。这是实现网络编程的前置基础,也是理解套接字(Socket)编程的核心前提。

16.0.1 网络协议分层模型

网络协议分层是为了简化通信逻辑,主流有两种模型:

模型类型分层结构核心说明
OSI模型七层(应用层、表示层、会话层、传输层、网络层、数据链路层、物理层)理论化分层,全面但复杂度高
TCP/IP模型(互联网协议套件)四层(应用层、传输层、互联网层、链路层)实际应用最广泛,简化了分层逻辑

16.0.2 核心分层协议与关键概念

1. 应用层

定义应用程序间的通信规则,如HTTP(80端口)、FTP(20/21端口)等,是直接面向业务的层级。

2. 传输层

负责端到端的数据包传输,核心协议有两个:

  • TCP:面向连接、可靠传输,通过确认机制和序列号保证数据双向、可靠、有序传输,开销较高
  • UDP:无连接、不可靠传输,不保证数据包的传递/顺序/重复性,但开销小、速度快

3. 互联网层

定义数据的组织形式(数据报)和设备寻址规则,核心是IP协议:

  • IPv4:4字节地址,点分四元组(如192.168.1.1),取值0~255,总地址约40亿个,已濒临耗尽
  • IPv6:16字节地址,八位冒号分隔的十六进制数(如2001:0db8:85a3:0000:0000:8a2e:0370:7334),地址空间超340万亿亿亿个

4. 端口

网络端点标识特定服务的数字,范围1~65535:

  • 1~1023:保留给标准服务(如HTTP=80、FTP=21)
  • 1024~65535:自定义服务使用

16.0.3 Go网络编程核心工具

Go标准库net包提供了完整的套接字编程能力,是实现TCP/UDP程序的核心依赖。同时,测试网络程序需用到netcat工具:

  • 类Unix系统(macOS/Linux):默认安装,命令为nc
  • Windows:需从Nmap项目等渠道下载
  • 核心用途:通过TCP/UDP读写网络数据,测试端口可用性与程序连通性

模块小结

本模块梳理了网络编程的核心前置概念:协议分层模型、TCP/UDP特性、IP/端口规则,以及Go开发和测试的核心工具,是后续实现TCP/UDP程序的基础认知。

16.1 创建TCP服务器

16.1.1 核心原理

TCP是面向连接的可靠传输协议,套接字(Socket)是网络通信的端点,抽象了底层网络复杂度。TCP服务器的核心流程为:监听传入连接 → 接受连接 → 读写数据,且需通过goroutine处理多客户端并发连接。

16.1.2 实现步骤(基础版)

步骤1:创建监听器

使用net.Listen监听指定地址和端口,格式为host:port(空host表示监听所有网络接口)。

步骤2:循环接受连接

通过listener.Accept()阻塞等待并接收客户端连接,返回net.Conn连接对象。

步骤3:goroutine处理连接

为每个连接启动独立goroutine,避免阻塞主线程,实现多客户端并发处理。

步骤4:数据读写与连接关闭

从连接读取数据,按需写入响应,处理完成后关闭连接。

16.1.3 基础版代码实现

package main

import (
    "io"
    "log"
    "net"
)

func main() {
    // 1. 创建监听器,监听本地 9000 端口上的 TCP 连接
    listener, err := net.Listen("tcp", "localhost:9000")
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close() // 确保程序退出时关闭监听器

    for {
        // 2. 接受传入的连接
        conn, err := listener.Accept()
        if err != nil {
            log.Fatal(err)
        }

        // 3. 为每个连接启动一个 goroutine 进行处理
        go func(c net.Conn) {
            defer c.Close() // 处理完毕后关闭连接

            buf := make([]byte, 1024)
            n, err := c.Read(buf) // 从连接中读取数据
            if err != nil {
                log.Println("读取错误:", err)
                return
            }
            log.Printf("收到: %s", string(buf[:n]))

            // 向客户端发送响应
            _, err = c.Write([]byte("Hello from TCP server"))
            if err != nil {
                log.Println("写入错误:", err)
            }
        }(conn)
    }
}

16.1.4 程序测试

  1. 启动服务器:
go run server.go
  1. 打开新终端,用nc作为客户端发送数据:
$ echo "Hello from TCP client" | nc localhost 9000
Hello from TCP server

测试结果:客户端发送的字符串会被服务器打印,同时服务器返回指定响应。

16.1.5 进阶优化:处理大尺寸数据

若客户端发送数据超过缓冲区大小,需循环读取直到遇到io.EOF(数据结束):

go func(c net.Conn) {
    defer c.Close()
    var data []byte
    buf := make([]byte, 32) // 使用较小的缓冲区分批读取

    for {
        n, err := c.Read(buf)
        if err != nil {
            if err == io.EOF {
                break // 数据已全部读取
            }
            log.Println("读取错误:", err)
            return
        }
        data = append(data, buf[:n]...)
    }
    log.Printf("收到完整数据: %s", string(data))

    _, err := c.Write([]byte("Hello from TCP server"))
    if err != nil {
        log.Println("写入错误:", err)
    }
}(conn)

16.1.6 进阶优化:IPv6支持

将监听地址的host部分留空,即可同时监听IPv4和IPv6:

listener, err := net.Listen("tcp", ":9000")

客户端强制使用IPv6连接:

echo "Hello" | nc -6 localhost 9000

16.1.7 进阶优化:底层TCP连接精细控制

若需设置KeepAlive等TCP属性,需使用net.ListenTCPAcceptTCP

addr, _ := net.ResolveTCPAddr("tcp", ":9000")
listener, _ := net.ListenTCP("tcp", addr)
defer listener.Close()

for {
    conn, _ := listener.AcceptTCP()
    conn.SetKeepAlive(true) // 可以设置 TCP 保活等属性
    // ... 处理连接
}

模块小结

本模块完整讲解了TCP服务器的开发流程:从核心原理到基础实现,再到测试和进阶优化(大尺寸数据、IPv6、底层控制),掌握了goroutine处理多连接、数据读写的核心逻辑。

16.2 创建TCP客户端

16.2.1 核心原理

TCP客户端的核心是通过net.Dial建立与服务器的TCP连接,然后通过连接对象完成数据发送(可选接收响应),流程比服务器更简单。

16.2.2 实现步骤

  1. 连接服务器:使用net.Dial("tcp", 地址)建立TCP连接。
  2. 发送数据:通过conn.Write将字节切片数据发送到服务器。
  3. 关闭连接:使用defer确保连接最终关闭。

16.2.3 基础版代码实现

package main

import (
    "log"
    "net"
)

func main() {
    // 连接到本地的 TCP 服务器
    conn, err := net.Dial("tcp", "localhost:9000")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close() // 确保关闭连接

    // 向服务器发送数据
    message := "Hello World from TCP client"
    n, err := conn.Write([]byte(message))
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("成功发送 %d 字节", n)
}

16.2.4 程序测试

  1. 启动nc监听服务器(模拟TCP服务端):
nc -l 9000
  1. 运行客户端程序:
go run client.go

测试结果:nc终端会显示客户端发送的消息,客户端日志打印发送的字节数。

16.2.5 进阶优化:底层TCP连接精细控制

使用net.DialTCP可指定本地地址等精细配置:

raddr, _ := net.ResolveTCPAddr("tcp", "localhost:9000")
conn, _ := net.DialTCP("tcp", nil, raddr) // 本地地址为 nil,系统自动选择
defer conn.Close()
conn.Write([]byte("Hello World from TCP Client"))

模块小结

本模块讲解了TCP客户端的核心实现逻辑:从连接建立到数据发送,再到进阶的底层连接控制,掌握了net.Dial/DialTCP的使用,以及与模拟服务器的测试方法。

16.3 创建UDP服务器

16.3.1 核心原理

UDP是无连接协议,通信无需建立持久连接,每个数据包独立路由。UDP服务器的核心流程为:监听UDP数据包 → 读取数据包并获取客户端地址 → 向客户端地址回复数据,核心API为net.ListenPacket

16.3.2 实现步骤

  1. 监听UDP端口:使用net.ListenPacket("udp", 地址)创建数据包连接。
  2. 循环读取数据包:通过ReadFrom读取数据并获取客户端地址。
  3. 回复客户端:通过WriteTo向指定客户端地址发送响应。

16.3.3 基础版代码实现

package main

import (
    "log"
    "net"
)

func main() {
    // 监听 UDP 9001 端口
    conn, err := net.ListenPacket("udp", ":9001")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    buf := make([]byte, 1024)
    for {
        // 读取数据包,并获取客户端地址
        n, addr, err := conn.ReadFrom(buf)
        if err != nil {
            log.Println("读取错误:", err)
            continue
        }
        log.Printf("%s 收到: %s", addr.String(), string(buf[:n]))

        // 向该客户端回复
        _, err = conn.WriteTo([]byte("Hello from UDP server"), addr)
        if err != nil {
            log.Println("回复错误:", err)
        }
    }
}

16.3.4 程序测试

  1. 启动UDP服务器:
go run udp_server.go
  1. 打开新终端,用nc作为UDP客户端发送数据:
$ echo "Hello from UDP client" | nc -u localhost 9001
Hello from UDP server

测试结果:服务器打印客户端地址和接收的数据,同时向客户端返回指定响应。

16.3.5 进阶优化:底层UDP连接精细控制

使用net.ListenUDP可获得*net.UDPConn,提供ReadFromUDP/WriteToUDP等专属方法:

addr, _ := net.ResolveUDPAddr("udp", ":9001")
conn, _ := net.ListenUDP("udp", addr) // 返回 *net.UDPConn
defer conn.Close()

buf := make([]byte, 1024)
for {
    n, cliAddr, _ := conn.ReadFromUDP(buf)
    log.Printf("%s 收到: %s", cliAddr, string(buf[:n]))
    conn.WriteToUDP([]byte("Reply"), cliAddr)
}

注:net.ListenPacket更通用(支持其他数据包协议),net.ListenUDP专为UDP设计,提供更多控制选项。

模块小结

本模块讲解了UDP服务器的核心实现:从无连接协议的原理,到基础版代码开发、测试,再到进阶的底层连接控制,掌握了数据包读写和地址交互的核心逻辑。

16.4 创建UDP客户端

16.4.1 核心原理

UDP客户端无需建立持久连接,核心是通过net.Dial("udp", 地址)创建连接对象,直接向服务器发送数据包,流程与TCP客户端类似,但协议层无连接保障。

16.4.2 实现步骤

  1. 连接UDP服务器:使用net.Dial("udp", 地址)创建UDP连接。
  2. 发送数据:通过conn.Write发送字节切片数据。
  3. 关闭连接:使用defer确保连接关闭。

16.4.3 基础版代码实现

package main

import (
    "log"
    "net"
)

func main() {
    // 连接到 UDP 服务器
    conn, err := net.Dial("udp", "localhost:9001")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // 发送数据
    _, err = conn.Write([]byte("Hello from UDP client"))
    if err != nil {
        log.Fatal(err)
    }
    log.Println("数据已发送")
}

16.4.4 程序测试

  1. 启动nc UDP监听服务器(模拟服务端):
nc -u -l 9001
  1. 运行UDP客户端:
go run udp_client.go

测试结果:nc终端显示客户端发送的消息,客户端日志打印“数据已发送”。

16.4.5 进阶优化:底层UDP连接精细控制

使用net.DialUDP可指定本地地址等精细配置:

raddr, _ := net.ResolveUDPAddr("udp", "localhost:9001")
conn, _ := net.DialUDP("udp", nil, raddr) // 本地地址为 nil
defer conn.Close()

conn.Write([]byte("Hello from UDP client"))

模块小结

本模块讲解了UDP客户端的核心开发逻辑:从连接创建到数据发送,再到进阶的底层连接控制,掌握了UDP无连接特性下的客户端实现与测试方法。

【本篇核心知识点速记】

  1. 核心概念:

    • TCP:面向连接、可靠、有序,开销高;UDP:无连接、不可靠、速度快
    • IPv4(4字节)地址耗尽,IPv6(16字节)是未来主流;端口1~1023为保留端口
    • 套接字(Socket)是网络通信端点,Go net包是套接字编程的核心依赖
    • netcat(nc)是测试网络程序的核心工具,支持TCP/UDP协议
  2. TCP编程核心:

    • 服务器:net.Listen监听 → Accept接受连接 → goroutine处理连接 → Read/Write读写数据
    • 客户端:net.Dial建立连接 → Write发送数据 → 关闭连接
    • 进阶:循环读取处理大尺寸数据、空host监听IPv4/IPv6、ListenTCP/DialTCP精细控制连接属性
  3. UDP编程核心:

    • 服务器:net.ListenPacket监听 → ReadFrom读数据包+客户端地址 → WriteTo回复
    • 客户端:net.Dial创建连接 → Write发送数据包 → 关闭连接
    • 进阶:ListenUDP/DialUDP提供UDP专属的精细控制能力
  4. 测试方法:

    • TCP服务器测试:nc直接连接端口发送数据
    • TCP客户端测试:nc -l 端口模拟服务器
    • UDP服务器测试:nc -u 连接端口发送数据
    • UDP客户端测试:nc -u -l 端口模拟服务器