引入

Go 文件操作 处理的是本地数据,但现代应用大多需要网络通信。Go 的 net 包封装了 TCP、UDP 等底层协议,配合 http 包 还能处理 HTTP,让 Go 写网络程序既简单又高效。

正文

定义

定义

net 包提供底层网络编程功能,包括 TCP 和 UDP 的监听、连接、读写。核心类型是 net.Conn(连接)和 net.Listener(监听器),它们实现了 io.Readerio.Writer 接口,网络数据可以像文件一样读写。

语法

import "net"
 
// TCP 服务器
listener, err := net.Listen("tcp", ":8080")
conn, err := listener.Accept()
 
// TCP 客户端
conn, err := net.Dial("tcp", "localhost:8080")
 
// UDP
addr, err := net.ResolveUDPAddr("udp", ":8080")
conn, err := net.ListenUDP("udp", addr)

例子

TCP 服务器

package main
 
import (
    "fmt"
    "net"
)
 
func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("监听失败:", err)
        return
    }
    defer listener.Close()
 
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        go handleConn(conn)
    }
}
 
func handleConn(conn net.Conn) {
    defer conn.Close()
    buf := make([]byte, 1024)
    n, err := conn.Read(buf)
    if err != nil {
        return
    }
    fmt.Println("收到:", string(buf[:n]))
    conn.Write([]byte("已收到"))
}

net.Listen 在指定端口监听,Accept 阻塞等待客户端连接。每个连接开一个 goroutine 处理,这是 Go 并发服务器的标准写法。

TCP 客户端

conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
    log.Fatal(err)
}
defer conn.Close()
 
conn.Write([]byte("hello server"))
 
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
fmt.Println("服务器回复:", string(buf[:n]))

net.Dial 连接到服务器,返回 net.ConnWrite 发送数据,Read 接收数据。用法和读写文件一样

TCP 并发服务器

func handleConn(conn net.Conn) {
    defer conn.Close()
 
    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        msg := scanner.Text()
        fmt.Println("收到:", msg)
        conn.Write([]byte("echo: " + msg + "\n"))
    }
}

bufio.NewScanner 包装 net.Conn,就能按行读取客户端消息net.Conn 实现了 io.Reader,所以所有读 Reader 的函数都能用。

UDP 服务器

addr, _ := net.ResolveUDPAddr("udp", ":8080")
conn, _ := net.ListenUDP("udp", addr)
defer conn.Close()
 
buf := make([]byte, 1024)
for {
    n, clientAddr, _ := conn.ReadFromUDP(buf)
    fmt.Println("收到:", string(buf[:n]), "来自:", clientAddr)
    conn.WriteToUDP([]byte("收到"), clientAddr)
}

UDP 是无连接协议,不需要 AcceptReadFromUDP 同时读取数据和发送方地址,WriteToUDP 向指定地址发送数据。

UDP 客户端

addr, _ := net.ResolveUDPAddr("udp", "localhost:8080")
conn, _ := net.DialUDP("udp", nil, addr)
defer conn.Close()
 
conn.Write([]byte("hello"))
 
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
fmt.Println("收到:", string(buf[:n]))

UDP 客户端用 DialUDP 连接到服务器后,可以像 TCP 一样用 Write / Read

HTTP 服务器(底层 net 包)

listener, _ := net.Listen("tcp", ":8080")
defer listener.Close()
 
for {
    conn, _ := listener.Accept()
    go func(c net.Conn) {
        defer c.Close()
        c.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello"))
    }(conn)
}

HTTP 本质上是 TCP + 文本协议。直接用 net 包也能写 HTTP 服务器,但一般用 http 包 更方便。

net.Dial 超时

conn, err := net.DialTimeout("tcp", "localhost:8080", 5*time.Second)
if err != nil {
    log.Fatal("连接超时:", err)
}
defer conn.Close()

DialTimeout 设置连接超时,避免一直阻塞等待。生产环境必须设超时。

常见写法

写法说明
net.Listen("tcp", addr)TCP 监听
listener.Accept()接受 TCP 连接
net.Dial("tcp", addr)TCP 连接
net.DialTimeout(network, addr, timeout)带超时的连接
conn.Read(buf) / conn.Write(buf)读写连接数据
net.ResolveUDPAddr("udp", addr)解析 UDP 地址
net.ListenUDP("udp", addr)UDP 监听
conn.ReadFromUDP(buf)读取 UDP 数据和来源地址
conn.WriteToUDP(buf, addr)向指定地址发送 UDP 数据
conn.SetDeadline(t)设置读写截止时间

特点

  • net.Conn 同时实现 io.Readerio.Writer网络数据和文件用法一样
  • TCP 服务器标准模式:ListenAccept → 每个连接开 goroutine
  • UDP 无连接,ReadFromUDP / WriteToUDP 处理每个数据包的来源地址
  • net.Dial 没有超时,生产环境要用 DialTimeout
  • 设置 conn.SetDeadline 可以防止连接永远阻塞
  • HTTP 协议建立在 TCP 之上,http 包 是对 net 包的高层封装

理解

net 包把网络编程变得很直白:TCP 就是 Listen + Accept + Read/Write,UDP 就是 ReadFrom/WriteTonet.Conn 实现了 io.Readerio.Writer,所以文件操作的那套函数直接就能用在网络连接上。Go 的并发模型让每个连接一个 goroutine 变得轻松,这就是 Go 网络编程的核心优势。大多数时候直接用 net/http 更省事,但理解底层的 net 包能帮你排查网络问题。

引出

网络编程和 HTTP 服务写好了,怎么验证它们是否正确工作?接下来看 Go 测试,学习 Go 内置的测试框架和编写测试用例的方法。