Go QUIC

QUIC(Quick UDP Internet Connections)是一种基于 UDP 的传输层协议,由 Google 开发,现已成为 IETF 标准。QUIC 结合了 TCP 的可靠性和 TLS 的安全性,同时提供更快的连接建立和更好的多路复用。

Go 标准库没有直接提供 QUIC 支持,最常用的是 lucas-clemente/quic-go 库。

下面是一个示例:

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/tls"
    "crypto/x509"
    "encoding/binary"
    "encoding/pem"
    "fmt"
    "io"
    "log"
    "math/big"
    "net"
    "os"
    "sync"
    "time"

    "github.com/lucas-clemente/quic-go"
)

// 1. TLS 证书生成(用于 QUIC 加密)
func generateTLSConfig() *tls.Config {
    key, err := rsa.GenerateKey(rand.Reader, 1024)
    if err != nil {
        panic(err)
    }

    template := x509.Certificate{
        SerialNumber: big.NewInt(1),
        NotBefore:    time.Now(),
        NotAfter:     time.Now().Add(365 * 24 * time.Hour),
        KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    }

    certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
    if err != nil {
        panic(err)
    }

    keyPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: x509.MarshalPKCS1PrivateKey(key),
    })

    certPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "CERTIFICATE",
        Bytes: certDER,
    })

    tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
    if err != nil {
        panic(err)
    }

    return &tls.Config{
        Certificates: []tls.Certificate{tlsCert},
        NextProtos:   []string{"quic-echo-example"},
    }
}

// 2. 基本 QUIC 服务器
type BasicQUICServer struct {
    listener quic.Listener
}

func NewBasicQUICServer() *BasicQUICServer {
    return &BasicQUICServer{}
}

func (s *BasicQUICServer) Start(addr string) error {
    tlsConfig := generateTLSConfig()

    listener, err := quic.ListenAddr(addr, tlsConfig, nil)
    if err != nil {
        return fmt.Errorf("启动 QUIC 服务器失败: %v", err)
    }

    s.listener = listener
    fmt.Printf("QUIC 服务器启动在 %s\n", addr)

    for {
        // 接受新连接
        sess, err := s.listener.Accept(context.Background())
        if err != nil {
            log.Printf("接受连接错误: %v", err)
            continue
        }

        fmt.Printf("新 QUIC 连接来自 %s\n", sess.RemoteAddr().String())

        // 为每个连接启动一个 goroutine
        go s.handleSession(sess)
    }
}

func (s *BasicQUICServer) handleSession(sess quic.Session) {
    defer sess.CloseWithError(0, "会话结束")

    for {
        // 接受新流
        stream, err := sess.AcceptStream(context.Background())
        if err != nil {
            if err.Error() != "NO_ERROR: No recent network activity" {
                log.Printf("接受流错误: %v", err)
            }
            return
        }

        fmt.Printf("新流建立: ID=%d, 来自 %s\n", stream.StreamID(), sess.RemoteAddr())

        // 为每个流启动一个 goroutine
        go s.handleStream(stream)
    }
}

func (s *BasicQUICServer) handleStream(stream quic.Stream) {
    defer stream.Close()

    buffer := make([]byte, 1024)

    for {
        // 读取数据
        n, err := stream.Read(buffer)
        if err != nil {
            if err != io.EOF {
                log.Printf("读取流错误: %v", err)
            }
            break
        }

        if n == 0 {
            continue
        }

        message := string(buffer[:n])
        fmt.Printf("收到消息: %s (长度: %d)\n", message, n)

        // 回显消息
        response := fmt.Sprintf("服务器回复: %s", message)
        _, err = stream.Write([]byte(response))
        if err != nil {
            log.Printf("写入流错误: %v", err)
            break
        }

        fmt.Printf("发送回复: %s\n", response)
    }

    fmt.Printf("流 %d 关闭\n", stream.StreamID())
}

func (s *BasicQUICServer) Stop() {
    if s.listener != nil {
        s.listener.Close()
    }
}

// 3. 基本 QUIC 客户端
type BasicQUICClient struct {
    session quic.Session
}

func NewBasicQUICClient() *BasicQUICClient {
    return &BasicQUICClient{}
}

func (c *BasicQUICClient) Connect(addr string) error {
    tlsConfig := &tls.Config{
        InsecureSkipVerify: true, // 仅用于测试
        NextProtos:         []string{"quic-echo-example"},
    }

    sess, err := quic.DialAddr(addr, tlsConfig, nil)
    if err != nil {
        return fmt.Errorf("连接 QUIC 服务器失败: %v", err)
    }

    c.session = sess
    fmt.Printf("连接到 QUIC 服务器 %s\n", addr)

    return nil
}

func (c *BasicQUICClient) SendMessage(message string) error {
    // 打开新流
    stream, err := c.session.OpenStream()
    if err != nil {
        return fmt.Errorf("打开流失败: %v", err)
    }
    defer stream.Close()

    fmt.Printf("发送消息: %s\n", message)

    // 发送消息
    _, err = stream.Write([]byte(message))
    if err != nil {
        return fmt.Errorf("发送消息失败: %v", err)
    }

    // 设置读取超时
    stream.SetReadDeadline(time.Now().Add(5 * time.Second))

    // 读取回复
    buffer := make([]byte, 1024)
    n, err := stream.Read(buffer)
    if err != nil {
        return fmt.Errorf("读取回复失败: %v", err)
    }

    response := string(buffer[:n])
    fmt.Printf("收到回复: %s\n", response)

    return nil
}

func (c *BasicQUICClient) Close() {
    if c.session != nil {
        c.session.CloseWithError(0, "客户端关闭")
    }
}

// 4. 文件传输 QUIC 服务器
type FileTransferServer struct {
    listener quic.Listener
}

func NewFileTransferServer() *FileTransferServer {
    return &FileTransferServer{}
}

func (s *FileTransferServer) Start(addr string) error {
    tlsConfig := generateTLSConfig()

    listener, err := quic.ListenAddr(addr, tlsConfig, nil)
    if err != nil {
        return err
    }

    s.listener = listener
    fmt.Printf("文件传输 QUIC 服务器启动在 %s\n", addr)

    for {
        sess, err := s.listener.Accept(context.Background())
        if err != nil {
            log.Printf("接受连接错误: %v", err)
            continue
        }

        go s.handleFileSession(sess)
    }
}

func (s *FileTransferServer) handleFileSession(sess quic.Session) {
    defer sess.CloseWithError(0, "文件传输完成")

    for {
        stream, err := sess.AcceptStream(context.Background())
        if err != nil {
            log.Printf("接受流错误: %v", err)
            return
        }

        go s.handleFileTransfer(stream)
    }
}

func (s *FileTransferServer) handleFileTransfer(stream quic.Stream) {
    defer stream.Close()

    // 读取文件名
    filenameBuffer := make([]byte, 256)
    n, err := stream.Read(filenameBuffer)
    if err != nil {
        log.Printf("读取文件名错误: %v", err)
        return
    }

    filename := string(filenameBuffer[:n])
    fmt.Printf("开始接收文件: %s\n", filename)

    // 创建文件
    file, err := os.Create("received_" + filename)
    if err != nil {
        log.Printf("创建文件错误: %v", err)
        stream.Write([]byte("ERROR: 无法创建文件"))
        return
    }
    defer file.Close()

    // 接收文件数据
    buffer := make([]byte, 4096)
    totalBytes := 0

    for {
        n, err := stream.Read(buffer)
        if err != nil {
            if err == io.EOF {
                break
            }
            log.Printf("读取文件数据错误: %v", err)
            break
        }

        if n > 0 {
            file.Write(buffer[:n])
            totalBytes += n
        }
    }

    fmt.Printf("文件接收完成: %s, 大小: %d 字节\n", filename, totalBytes)
    stream.Write([]byte(fmt.Sprintf("SUCCESS: 文件接收成功, 大小: %d 字节", totalBytes)))
}

// 5. 文件传输 QUIC 客户端
type FileTransferClient struct {
    session quic.Session
}

func NewFileTransferClient() *FileTransferClient {
    return &FileTransferClient{}
}

func (c *FileTransferClient) Connect(addr string) error {
    tlsConfig := &tls.Config{
        InsecureSkipVerify: true,
        NextProtos:         []string{"quic-echo-example"},
    }

    sess, err := quic.DialAddr(addr, tlsConfig, nil)
    if err != nil {
        return err
    }

    c.session = sess
    return nil
}

func (c *FileTransferClient) SendFile(filename string) error {
    stream, err := c.session.OpenStream()
    if err != nil {
        return err
    }
    defer stream.Close()

    // 发送文件名
    _, err = stream.Write([]byte(filename))
    if err != nil {
        return err
    }

    // 读取文件
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    // 发送文件数据
    buffer := make([]byte, 4096)
    totalBytes := 0

    for {
        n, err := file.Read(buffer)
        if err != nil {
            if err == io.EOF {
                break
            }
            return err
        }

        if n > 0 {
            _, err = stream.Write(buffer[:n])
            if err != nil {
                return err
            }
            totalBytes += n
        }
    }

    fmt.Printf("文件发送完成: %s, 大小: %d 字节\n", filename, totalBytes)

    // 读取服务器响应
    responseBuffer := make([]byte, 1024)
    n, err := stream.Read(responseBuffer)
    if err != nil && err != io.EOF {
        return err
    }

    if n > 0 {
        fmt.Printf("服务器响应: %s\n", string(responseBuffer[:n]))
    }

    return nil
}

func (c *FileTransferClient) Close() {
    if c.session != nil {
        c.session.CloseWithError(0, "客户端关闭")
    }
}

// 6. 多路复用聊天服务器
type QUICChatServer struct {
    listener   quic.Listener
    clients    map[quic.Session]*ChatClient
    clientsMu  sync.RWMutex
    broadcast  chan []byte
}

type ChatClient struct {
    session quic.Session
    streams map[quic.StreamID]quic.Stream
}

func NewQUICChatServer() *QUICChatServer {
    return &QUICChatServer{
        clients:   make(map[quic.Session]*ChatClient),
        broadcast: make(chan []byte, 100),
    }
}

func (s *QUICChatServer) Start(addr string) error {
    tlsConfig := generateTLSConfig()

    listener, err := quic.ListenAddr(addr, tlsConfig, nil)
    if err != nil {
        return err
    }

    s.listener = listener
    fmt.Printf("QUIC 聊天服务器启动在 %s\n", addr)

    // 启动广播器
    go s.broadcastMessages()

    for {
        sess, err := s.listener.Accept(context.Background())
        if err != nil {
            log.Printf("接受连接错误: %v", err)
            continue
        }

        go s.handleChatSession(sess)
    }
}

func (s *QUICChatServer) handleChatSession(sess quic.Session) {
    client := &ChatClient{
        session: sess,
        streams: make(map[quic.StreamID]quic.Stream),
    }

    // 注册客户端
    s.clientsMu.Lock()
    s.clients[sess] = client
    clientCount := len(s.clients)
    s.clientsMu.Unlock()

    fmt.Printf("新聊天客户端连接: %s, 总客户端数: %d\n", sess.RemoteAddr(), clientCount)

    // 发送欢迎消息
    welcomeMsg := fmt.Sprintf("欢迎加入聊天室! 当前在线用户: %d", clientCount)
    s.sendToClient(client, welcomeMsg)

    // 通知其他用户
    joinMsg := fmt.Sprintf("新用户加入: %s", sess.RemoteAddr())
    s.broadcast <- []byte(joinMsg)

    defer func() {
        s.clientsMu.Lock()
        delete(s.clients, sess)
        clientCount = len(s.clients)
        s.clientsMu.Unlock()

        leaveMsg := fmt.Sprintf("用户离开: %s, 剩余用户: %d", sess.RemoteAddr(), clientCount)
        s.broadcast <- []byte(leaveMsg)

        sess.CloseWithError(0, "聊天会话结束")
        fmt.Printf("聊天客户端断开: %s, 剩余: %d\n", sess.RemoteAddr(), clientCount)
    }()

    for {
        stream, err := sess.AcceptStream(context.Background())
        if err != nil {
            log.Printf("接受流错误: %v", err)
            return
        }

        client.streams[stream.StreamID()] = stream
        go s.handleChatStream(client, stream)
    }
}

func (s *QUICChatServer) handleChatStream(client *ChatClient, stream quic.Stream) {
    defer func() {
        stream.Close()
        delete(client.streams, stream.StreamID())
    }()

    buffer := make([]byte, 1024)

    for {
        n, err := stream.Read(buffer)
        if err != nil {
            if err != io.EOF {
                log.Printf("读取聊天消息错误: %v", err)
            }
            break
        }

        if n > 0 {
            message := string(buffer[:n])
            chatMsg := fmt.Sprintf("[%s]: %s", client.session.RemoteAddr(), message)
            fmt.Printf("聊天消息: %s\n", chatMsg)

            // 广播消息
            s.broadcast <- []byte(chatMsg)
        }
    }
}

func (s *QUICChatServer) sendToClient(client *ChatClient, message string) {
    if len(client.streams) == 0 {
        return
    }

    // 使用第一个流发送消息
    for _, stream := range client.streams {
        _, err := stream.Write([]byte(message))
        if err != nil {
            log.Printf("发送消息错误: %v", err)
        }
        break
    }
}

func (s *QUICChatServer) broadcastMessages() {
    for message := range s.broadcast {
        s.clientsMu.RLock()
        for _, client := range s.clients {
            go s.sendToClient(client, string(message))
        }
        s.clientsMu.RUnlock()
    }
}

// 7. 性能测试工具
type QUICBenchmark struct {
    serverAddr string
}

func NewQUICBenchmark(serverAddr string) *QUICBenchmark {
    return &QUICBenchmark{
        serverAddr: serverAddr,
    }
}

func (b *QUICBenchmark) RunSpeedTest() error {
    tlsConfig := &tls.Config{
        InsecureSkipVerify: true,
        NextProtos:         []string{"quic-echo-example"},
    }

    sess, err := quic.DialAddr(b.serverAddr, tlsConfig, nil)
    if err != nil {
        return err
    }
    defer sess.CloseWithError(0, "测试完成")

    stream, err := sess.OpenStream()
    if err != nil {
        return err
    }
    defer stream.Close()

    // 生成测试数据
    testData := make([]byte, 1024 * 1024) // 1MB
    rand.Read(testData)

    startTime := time.Now()

    // 发送数据
    _, err = stream.Write(testData)
    if err != nil {
        return err
    }

    // 读取回显(测量往返时间)
    echoBuffer := make([]byte, len(testData))
    totalRead := 0
    for totalRead < len(testData) {
        n, err := stream.Read(echoBuffer[totalRead:])
        if err != nil {
            return err
        }
        totalRead += n
    }

    duration := time.Since(startTime)
    speed := float64(len(testData)) / duration.Seconds() / 1024 / 1024 // MB/s

    fmt.Printf("速度测试结果:\n")
    fmt.Printf("  数据大小: %d bytes\n", len(testData))
    fmt.Printf("  耗时: %v\n", duration)
    fmt.Printf("  速度: %.2f MB/s\n", speed)
    fmt.Printf("  往返时间: %v\n", duration)

    return nil
}

func (b *QUICBenchmark) RunConcurrentTest(concurrent int) {
    fmt.Printf("启动 %d 个并发连接测试...\n", concurrent)

    var wg sync.WaitGroup
    startTime := time.Now()

    for i := 0; i < concurrent; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()

            tlsConfig := &tls.Config{
                InsecureSkipVerify: true,
                NextProtos:         []string{"quic-echo-example"},
            }

            sess, err := quic.DialAddr(b.serverAddr, tlsConfig, nil)
            if err != nil {
                log.Printf("连接 %d 失败: %v", id, err)
                return
            }
            defer sess.CloseWithError(0, "测试完成")

            stream, err := sess.OpenStream()
            if err != nil {
                log.Printf("打开流 %d 失败: %v", id, err)
                return
            }
            defer stream.Close()

            message := fmt.Sprintf("并发测试消息 %d", id)
            stream.Write([]byte(message))

            buffer := make([]byte, 1024)
            n, _ := stream.Read(buffer)
            if n > 0 {
                fmt.Printf("连接 %d 测试完成: %s\n", id, string(buffer[:n]))
            }
        }(i)
    }

    wg.Wait()
    duration := time.Since(startTime)
    fmt.Printf("并发测试完成: %d 个连接, 耗时: %v\n", concurrent, duration)
}

// 8. 主函数和示例演示
func demonstrateBasicQUIC() {
    fmt.Println("=== 基本 QUIC 示例 ===")

    // 启动服务器
    server := NewBasicQUICServer()
    go func() {
        time.Sleep(100 * time.Millisecond)
        server.Start("localhost:4242")
    }()

    time.Sleep(1 * time.Second)

    // 客户端测试
    client := NewBasicQUICClient()
    err := client.Connect("localhost:4242")
    if err != nil {
        log.Printf("连接失败: %v", err)
        return
    }
    defer client.Close()

    // 发送测试消息
    messages := []string{
        "Hello QUIC!",
        "这是第二条消息",
        "测试多消息传输",
    }

    for _, msg := range messages {
        err = client.SendMessage(msg)
        if err != nil {
            log.Printf("发送消息失败: %v", err)
        }
        time.Sleep(500 * time.Millisecond)
    }
}

func demonstrateFileTransfer() {
    fmt.Println("\n=== 文件传输示例 ===")

    // 创建测试文件
    testContent := "这是 QUIC 文件传输测试内容\n" +
                   "QUIC 协议基于 UDP,提供快速可靠的文件传输\n" +
                   "支持多路复用和快速连接建立\n"

    err := os.WriteFile("test_file.txt", []byte(testContent), 0644)
    if err != nil {
        log.Printf("创建测试文件失败: %v", err)
        return
    }
    defer os.Remove("test_file.txt")
    defer os.Remove("received_test_file.txt")

    // 启动文件服务器
    server := NewFileTransferServer()
    go func() {
        server.Start("localhost:4243")
    }()

    time.Sleep(1 * time.Second)

    // 客户端文件传输
    client := NewFileTransferClient()
    err = client.Connect("localhost:4243")
    if err != nil {
        log.Printf("连接失败: %v", err)
        return
    }
    defer client.Close()

    err = client.SendFile("test_file.txt")
    if err != nil {
        log.Printf("文件传输失败: %v", err)
    }
}

func demonstrateChatServer() {
    fmt.Println("\n=== 聊天服务器示例 ===")

    server := NewQUICChatServer()
    go server.Start("localhost:4244")

    fmt.Println("聊天服务器已启动,可以使用客户端连接测试")
    time.Sleep(10 * time.Second) // 让服务器运行一段时间
}

func demonstrateBenchmark() {
    fmt.Println("\n=== 性能测试示例 ===")

    // 先启动一个测试服务器
    server := NewBasicQUICServer()
    go server.Start("localhost:4245")

    time.Sleep(1 * time.Second)

    benchmark := NewQUICBenchmark("localhost:4245")

    // 速度测试
    fmt.Println("1. 速度测试:")
    benchmark.RunSpeedTest()

    fmt.Println("\n2. 并发测试:")
    benchmark.RunConcurrentTest(5)
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("QUIC 示例用法:")
        fmt.Println("  基本示例: go run quic.go basic")
        fmt.Println("  文件传输: go run quic.go file")
        fmt.Println("  聊天服务器: go run quic.go chat")
        fmt.Println("  性能测试: go run quic.go benchmark")
        fmt.Println("  全部演示: go run quic.go all")
        return
    }

    mode := os.Args[1]

    switch mode {
    case "basic":
        demonstrateBasicQUIC()
    case "file":
        demonstrateFileTransfer()
    case "chat":
        demonstrateChatServer()
    case "benchmark":
        demonstrateBenchmark()
    case "all":
        demonstrateBasicQUIC()
        demonstrateFileTransfer()
        demonstrateChatServer()
        demonstrateBenchmark()
    default:
        fmt.Println("未知模式")
    }

    // 等待一段时间让演示完成
    time.Sleep(5 * time.Second)
}

QUIC 编程关键要点

  1. ​ 核心概念 ​

​Session​:QUIC 连接,包含多个流

​Stream​:独立的双向数据流

​TLS 集成 ​:QUIC 内置 TLS 1.3 加密

  1. ​ 重要特性

    ​0-RTT​:支持 0 往返时间的连接恢复

​ 多路复用 ​:多个流独立传输,无队头阻塞

​ 连接迁移 ​:网络变化时保持连接

​ 前向纠错 ​:减少重传需求

  1. ​ 最佳实践 ​

​ 证书管理 ​:生产环境使用有效的 TLS 证书

​ 流管理 ​:合理管理多个流的使用

​ 错误处理 ​:妥善处理连接和流错误

​ 超时控制 ​:设置合理的超时时间

​ 资源清理 ​:及时关闭会话和流

  1. ​ 适用场景 ​

​HTTP/3​:下一代 Web 协议

​ 实时通信 ​:视频会议、在线游戏

​ 移动应用 ​:网络切换时保持连接

​ 大文件传输 ​:高速可靠的文件传输

  1. ​ 性能优势 ​

​ 连接建立 ​:比 TCP+TLS 快 3-5 倍

​ 多路复用 ​:避免队头阻塞问题

​ 拥塞控制 ​:更先进的拥塞控制算法

​ 错误恢复 ​:快速重传和前向纠错

QUIC 是下一代互联网传输协议,特别适合需要低延迟、高可靠性的应用场景。Go 语言的 quic-go 库提供了完整的 QUIC 实现,使得开发高性能网络应用变得更加容易。

通关密语:quic