导航菜单

网络基础

Go 的标准库在 networking 方面提供了丰富的支持。net 包是所有网络操作的基础,而 net/http 则构建在其之上,提供了完整的 HTTP 客户端和服务端实现。

net/http 概览

http.Handler 接口

net/http 包的核心是 http.Handler 接口,任何实现了 ServeHTTP(ResponseWriter, *Request) 方法的类型都可以作为 HTTP 处理器:

type Handler interface {
    ServeHTTP(http.ResponseWriter, *http.Request)
}

http.HandlerFunc 是一个适配器类型,它将普通的函数转换为 http.Handler

// HandlerFunc 是一个带有 ServeHTTP 方法的类型
type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

net/url —— URL 解析与构建

net/url 包提供了 URL 的解析、构建和查询参数处理功能。

package main

import (
    "fmt"
    "net/url"
)

func main() {
    // 解析 URL
    u, err := url.Parse("https://example.com:8080/path?key=value&name=go#section")
    if err != nil {
        panic(err)
    }

    fmt.Println(u.Scheme)   // https
    fmt.Println(u.Host)     // example.com:8080
    fmt.Println(u.Hostname()) // example.com
    fmt.Println(u.Path)     // /path
    fmt.Println(u.RawQuery) // key=value&name=go
    fmt.Println(u.Fragment) // section

    // 获取查询参数
    fmt.Println(u.Query().Get("key"))  // value
    fmt.Println(u.Query().Get("name")) // go

    // 构建 URL
    u2 := &url.URL{
        Scheme:   "https",
        Host:     "api.example.com",
        Path:     "/v1/users",
        RawQuery: "page=1&limit=10",
    }
    fmt.Println(u2.String()) // https://api.example.com/v1/users?page=1&limit=10

    // 使用 url.Values 构建查询参数
    params := url.Values{}
    params.Set("page", "1")
    params.Set("limit", "10")
    params.Add("tag", "go")
    params.Add("tag", "web")
    fmt.Println(params.Encode()) // limit=10&page=1&tag=go&tag=web
}

net 包 —— TCP/UDP 编程

net 包提供了底层的网络 I/O 操作,包括 TCP 和 UDP 编程。

TCP 服务端

package main

import (
    "fmt"
    "io"
    "net"
)

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    defer listener.Close()
    fmt.Println("TCP 服务器启动,监听 :8080")

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("接受连接失败:", err)
            continue
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    buf := make([]byte, 1024)
    for {
        n, err := conn.Read(buf)
        if err != nil {
            if err != io.EOF {
                fmt.Println("读取错误:", err)
            }
            return
        }
        fmt.Printf("收到: %s", buf[:n])
        conn.Write([]byte("已收到你的消息\n"))
    }
}

TCP 客户端

package main

import (
    "fmt"
    "net"
)

func main() {
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    conn.Write([]byte("Hello, TCP Server!\n"))

    buf := make([]byte, 1024)
    n, _ := conn.Read(buf)
    fmt.Printf("服务器响应: %s", buf[:n])
}

http.Client —— 发起 HTTP 请求

http.Client 是 Go 中发起 HTTP 请求的主要工具。

基本 GET 请求

package main

import (
    "fmt"
    "io"
    "net/http"
)

func main() {
    resp, err := http.Get("https://httpbin.org/get")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("响应体: %s\n", string(body[:100]))
}

POST 请求

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    data := map[string]string{
        "name": "张三",
        "email": "zhangsan@example.com",
    }
    body, _ := json.Marshal(data)

    resp, err := http.Post(
        "https://httpbin.org/post",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    respBody, _ := io.ReadAll(resp.Body)
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("响应: %s\n", string(respBody[:200]))
}

自定义 Client

package main

import (
    "net/http"
    "time"
)

func main() {
    // 创建自定义的 http.Client
    client := &http.Client{
        Timeout: 10 * time.Second, // 请求超时
        Transport: &http.Transport{
            MaxIdleConns:        100,              // 最大空闲连接数
            MaxIdleConnsPerHost: 10,               // 每个主机最大空闲连接数
            IdleConnTimeout:     90 * time.Second, // 空闲连接超时
        },
    }

    // 使用自定义 client 发起请求
    resp, err := client.Get("https://httpbin.org/get")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
}

http.ServeMux —— 路由器

Go 1.22+ 引入了增强的 http.ServeMux,支持方法匹配和路径参数。

package main

import (
    "fmt"
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    // Go 1.22+ 新语法:方法匹配
    mux.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "获取用户列表")
    })

    mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "创建用户")
    })

    // 路径参数(Go 1.22+)
    mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id")
        fmt.Fprintf(w, "获取用户 ID: %s\n", id)
    })

    fmt.Println("服务器启动 :8080")
    http.ListenAndServe(":8080", mux)
}

练习题

练习 1

编写一个程序,解析 URL https://user:pass@example.com:443/api/v1/users?page=2&limit=20#results,输出其 scheme、user、host、path、query 参数和 fragment。

参考答案 (2 个标签)
net/url URL解析

解题思路:使用 url.Parse() 解析 URL 字符串,然后访问各个字段。

package main

import (
    "fmt"
    "net/url"
)

func main() {
    rawURL := "https://user:pass@example.com:443/api/v1/users?page=2&limit=20#results"
    u, _ := url.Parse(rawURL)

    fmt.Println("Scheme:", u.Scheme)     // https
    fmt.Println("User:", u.User)         // user:pass
    fmt.Println("Host:", u.Host)         // example.com:443
    fmt.Println("Hostname:", u.Hostname()) // example.com
    fmt.Println("Path:", u.Path)         // /api/v1/users
    fmt.Println("Fragment:", u.Fragment) // results
    fmt.Println("page:", u.Query().Get("page"))   // 2
    fmt.Println("limit:", u.Query().Get("limit")) // 20
}

练习 2

使用 http.Client 编写一个程序,向 https://httpbin.org/uuid 发送 GET 请求,并解析返回的 JSON 中的 uuid 字段。

参考答案 (2 个标签)
http.Client JSON解析

解题思路:使用 http.Get 发起请求,json.Decoder 直接解码响应体。

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Get("https://httpbin.org/uuid")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    var result struct {
        UUID string `json:"uuid"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        panic(err)
    }
    fmt.Println("UUID:", result.UUID)
}

搜索