高性能 TCP 服务器的 Go 语言实现技巧:从原理到实践

1. 引言

在现代软件的数字城市中,TCP 服务器就像忙碌的交通枢纽,精准、高效地引导数据流。从支撑电商平台的微服务到实时聊天系统,再到物联网设备的数据收集,TCP 服务器是分布式系统的核心支柱。它们需要处理成千上万的并发连接,同时保持低延迟,为用户提供流畅的体验。

本文面向有 1-2 年 Go 开发经验的开发者,假设你熟悉基本网络编程(如 net 包和 goroutine),但对高性能优化尚感陌生。Go 就像一把网络编程的瑞士军刀:轻量、灵活、功能强大,凭借其 goroutine、标准库和并发原语,成为构建高性能 TCP 服务器的理想选择。我们的目标是提供从原理到实践的实用技巧,分享真实项目经验,帮助你打造健壮、高效的 TCP 服务器。接下来,我们将从 Go 的核心优势入手,逐步揭开高性能服务器的实现奥秘。


2. Go 语言在 TCP 服务器开发中的核心优势

在动手写代码之前,我们先来了解为什么 Go 是构建 TCP 服务器的优选语言。想象 Go 是一辆为网络编程赛道设计的跑车:轻便、高效、并发性能卓越。以下是 Go 在 TCP 服务器开发中的五大优势,每一项都为高性能提供了坚实支持。

2.1 轻量级协程(goroutine)

传统线程模型像重型货车,资源占用高,而 Go 的 goroutine 像敏捷的电动车,内存占用仅几 KB,却能支持数千甚至数十万并发任务。这种轻量级并发模型让 Go 在处理大量客户端连接时游刃有余。

实际影响:每个客户端连接分配一个 goroutine,无需担心线程开销,轻松应对高并发。

2.2 标准库 net 包

Go 的 net 包就像一个精心设计的工具箱,提供了简洁而强大的网络编程接口,无需依赖第三方库即可实现 TCP 服务器的核心功能。它的 API 简单直观,降低了开发复杂性。

实际影响:开发者可以快速构建可靠的 TCP 服务器,专注于业务逻辑而非底层细节。

2.3 内置并发原语

Go 的 channel 和 select 机制如同交响乐团的指挥家,协调多个 goroutine 的工作。channel 实现安全的任务分发,select 则高效处理多路 I/O 操作,特别适合管理复杂的网络事件。

实际影响:通过 channel 实现任务队列,优化资源分配,避免并发瓶颈。

2.4 垃圾回收与性能优化

Go 的垃圾回收器(GC)像一位高效的清洁工,悄无声息地清理内存,确保长时间运行的服务器保持稳定。Go 1.18+ 的 GC 优化进一步降低了暂停时间,适合低延迟场景。

实际影响:开发者无需手动管理内存,专注于逻辑开发,同时通过调优减少 GC 压力。

2.5 跨平台支持

Go 的编译模型像一个便携式行李箱:一次编译,处处运行。无论是 Linux、Windows 还是 macOS,Go 的 TCP 服务器无需额外运行时依赖,部署简单高效。

实际影响:从云端到边缘设备,Go 服务器都能轻松部署,适应多样化环境。

特性 对 TCP 服务器的益处 示例场景
Goroutine 低内存占用,支持高并发 管理 10,000+ 客户端连接
net 简洁的网络编程接口 快速搭建 TCP 服务器
Channel & select 安全高效的并发协调 广播消息给多个客户端
垃圾回收 长期运行的稳定性 24/7 聊天服务器
跨平台支持 部署灵活,适应多种环境 物联网设备数据采集

过渡:了解了 Go 的优势,我们已经为构建高性能 TCP 服务器奠定了理论基础。接下来,我们将深入核心实现技巧,探讨如何将这些优势转化为高效的代码和架构。


3. 高性能 TCP 服务器的核心实现技巧

将 Go 的优势应用于实际开发,就像将跑车引擎装进赛车,需要精心的设计和调优。本节深入探讨连接管理、并发模型优化、数据序列化与协议设计、性能监控等关键技巧,结合真实项目经验,助你在高并发、低延迟场景中游刃有余。

3.1 高效的连接管理

TCP 服务器的核心是处理客户端连接,就像机场调度中心管理飞机的起降。Go 的 net.Listenernet.Conn 提供了坚实基础,但高性能需要额外的优化。

  • 使用 net.Listenernet.Connnet.Listener 监听端口,接受连接;net.Conn 管理每个连接的读写,每个连接由单独的 goroutine 处理。
  • 连接池设计:通过限制 goroutine 数量,像工厂流水线一样管理连接,防止资源耗尽。
  • 超时控制:设置读写超时,像为飞机分配停机时间,避免资源长期占用。

代码示例:简单 TCP 服务器实现。

go 复制代码
package main

import (
    "fmt"
    "net"
    "time"
)

// handleConnection 处理单个客户端连接
func handleConnection(conn net.Conn) {
    // 确保连接关闭,防止资源泄漏
    defer conn.Close()
    // 设置 10 秒读超时,防止客户端长时间无响应
    conn.SetReadDeadline(time.Now().Add(10 * time.Second))
    buffer := make([]byte, 1024) // 缓冲区用于读取客户端数据
    for {
        n, err := conn.Read(buffer) // 读取客户端发送的数据
        if err != nil {
            fmt.Println("Error reading:", err) // 记录读取错误(如客户端断开)
            return
        }
        fmt.Printf("Received: %s", buffer[:n]) // 打印收到的数据
        conn.Write([]byte("Message received\n")) // 回复客户端
    }
}

func main() {
    // 监听 TCP 端口 8080
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err)
        return
    }
    defer listener.Close() // 确保监听器关闭
    fmt.Println("Server listening on :8080")
    for {
        // 接受新连接
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting:", err)
            continue
        }
        // 为每个连接启动一个 goroutine
        go handleConnection(conn)
    }
}

示意图:连接管理流程

yaml 复制代码
[客户端] ----> [net.Listener: 监听端口] ----> [Accept: 新连接]
                                                    |
                                                    v
                                             [goroutine: handleConnection]
                                                    |
                                                    v
                                              [net.Conn: 读/写数据]

踩坑经验 :在高并发场景下,未设置超时导致"僵尸连接"占用资源。解决方案 :为 net.Conn 设置 SetReadDeadlineSetWriteDeadline,并结合心跳机制检测连接存活。

3.2 并发模型优化

Go 的并发模型像一个高效的交响乐团,每个 goroutine 是乐手,调度器是指挥家。优化并发模型是高性能服务器的关键。

  • goroutine 调度:Go 调度器自动分配 CPU 资源,但高并发下需限制 goroutine 数量以防内存耗尽。
  • Worker Pool 模式:通过固定数量的 goroutine 处理任务,像流水线工人一样分工协作。
  • 踩坑经验 :goroutine 泄漏是常见问题,未正确处理错误可能导致 goroutine 未退出。使用 pprof 定位泄漏点。

代码示例:Worker Pool 处理 TCP 连接。

go 复制代码
package main

import (
    "fmt"
    "net"
)

// WorkerPool 管理一组工作 goroutine
type WorkerPool struct {
    tasks chan net.Conn // 任务通道,用于分发连接
}

// NewWorkerPool 创建指定大小的工作池
func NewWorkerPool(size int) *WorkerPool {
    pool := &WorkerPool{tasks: make(chan net.Conn, 100)}
    for i := 0; i < size; i++ {
        go pool.worker() // 启动固定数量的工作 goroutine
    }
    return pool
}

// worker 处理通道中的连接任务
func (p *WorkerPool) worker() {
    for conn := range p.tasks {
        handleConnection(conn) // 调用前述的 handleConnection 函数
    }
}

// handleConnection 同上,略
func handleConnection(conn net.Conn) {
    defer conn.Close()
    conn.SetReadDeadline(time.Now().Add(10 * time.Second))
    buffer := make([]byte, 1024)
    for {
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("Error reading:", err)
            return
        }
        fmt.Printf("Received: %s", buffer[:n])
        conn.Write([]byte("Message received\n"))
    }
}

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err)
        return
    }
    defer listener.Close()
    pool := NewWorkerPool(10) // 创建 10 个工作 goroutine
    fmt.Println("Server listening on :8080")
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting:", err)
            continue
        }
        pool.tasks <- conn // 将连接分发到工作池
    }
}

表格:goroutine vs Worker Pool 对比

方案 优点 缺点 适用场景
每连接一个 goroutine 简单,适合低并发 高并发下内存占用高,易泄漏 小规模服务器
Worker Pool 控制资源使用,稳定性能 实现稍复杂,需管理任务队列 高并发、资源受限环境

踩坑经验 :早期项目中,未限制 goroutine 数量导致内存飙升。解决方案 :使用 runtime.NumGoroutine() 监控,并引入 Worker Pool 模式,显著提升稳定性。

3.3 数据序列化与协议设计

TCP 是数据流协议,像一条没有分界线的河流,容易出现粘包/拆包问题。设计高效的序列化和协议格式是高性能服务器的关键。

  • 高效序列化:JSON 易用但解析慢;Protobuf 紧凑高效但需 schema;自定义二进制协议性能最佳但开发复杂。
  • 协议设计:协议需支持扩展性,如添加长度前缀明确消息边界。
  • 踩坑经验 :粘包问题常导致消息解析错误。解决方案:使用长度前缀协议。

代码示例:长度前缀协议解决粘包问题。

go 复制代码
package main

import (
    "encoding/binary"
    "io"
    "net"
)

// readMessage 读取带长度前缀的消息
func readMessage(conn net.Conn) ([]byte, error) {
    lenBuf := make([]byte, 4) // 读取 4 字节长度前缀
    _, err := io.ReadFull(conn, lenBuf) // 确保读取完整长度
    if err != nil {
        return nil, err
    }
    length := binary.BigEndian.Uint32(lenBuf) // 解析长度
    data := make([]byte, length) // 分配数据缓冲区
    _, err = io.ReadFull(conn, data) // 读取指定长度的数据
    return data, err
}

// handleConnection 使用长度前缀协议处理连接
func handleConnection(conn net.Conn) {
    defer conn.Close()
    for {
        data, err := readMessage(conn)
        if err != nil {
            fmt.Println("Error reading:", err)
            return
        }
        fmt.Printf("Received: %s\n", data)
        conn.Write([]byte("Message received\n"))
    }
}

表格:序列化方案对比

方案 速度 易用性 数据大小 适用场景
JSON 较慢 较大 调试、开发初期
Protobuf 高性能、跨语言场景
自定义二进制协议 最快 最小 极致性能需求(如游戏服务器)

踩坑经验 :在聊天服务器开发中,JSON 解析导致 CPU 使用率激增。解决方案:切换到 Protobuf,性能提升 30%,但需注意 schema 版本兼容性。

3.4 性能监控与优化

性能监控就像为服务器装上仪表盘,实时了解运行状态。Go 提供了丰富的工具来优化性能。

  • 使用 runtime :通过 runtime.NumGoroutine()runtime.MemStats() 监控 goroutine 数量和内存使用。
  • 集成 Prometheus :使用 expvar 或 Prometheus 客户端收集指标,如连接数、请求延迟。
  • 踩坑经验 :高负载下 JSON 解析成为瓶颈。解决方案 :通过 pprof 定位热点函数,优化为 Protobuf,CPU 使用率降低 20%。

示意图:性能监控流程

css 复制代码
[Server] ----> [runtime: 监控 goroutine/内存]
                     |
                     v
[Prometheus: 收集指标] ----> [Grafana: 可视化]

4. 实际应用场景与项目经验

理论只有在实战中才能发挥价值,就像将跑车引擎装进赛车,我们需要通过真实场景检验 TCP 服务器的设计。本节分享三个典型场景:实时聊天服务器、物联网设备数据采集和游戏服务器,剖析实现要点和踩坑经验。

4.1 实时聊天服务器

场景描述:支持万人级并发消息传递,类似微信或 Discord,要求低延迟和高可靠性。

实现要点

  • 连接管理 :为每个客户端分配 goroutine,维护连接映射(map[clientID]*net.Conn)。
  • 消息广播:使用 channel 作为消息队列,广播消息给客户端。
  • 并发优化:通过 Worker Pool 限制广播任务的 goroutine 数量。

代码示例:消息广播机制。

go 复制代码
package main

import (
    "fmt"
    "net"
    "sync"
)

// ClientManager 管理所有客户端连接
type ClientManager struct {
    clients    map[string]*net.Conn
    broadcast  chan []byte
    mutex      sync.Mutex
}

// NewClientManager 初始化客户端管理器
func NewClientManager() *ClientManager {
    return &ClientManager{
        clients:   make(map[string]*net.Conn),
        broadcast: make(chan []byte, 100),
    }
}

// handleConnection 处理客户端连接
func (cm *ClientManager) handleConnection(conn net.Conn, clientID string) {
    defer func() {
        cm.mutex.Lock()
        delete(cm.clients, clientID) // 移除断开连接的客户端
        cm.mutex.Unlock()
        conn.Close()
    }()
    cm.mutex.Lock()
    cm.clients[clientID] = &conn // 注册新客户端
    cm.mutex.Unlock()

    buffer := make([]byte, 1024)
    for {
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("Client disconnected:", clientID)
            return
        }
        cm.broadcast <- buffer[:n] // 将消息放入广播队列
    }
}

// broadcastMessages 广播消息给所有客户端
func (cm *ClientManager) broadcastMessages() {
    for msg := range cm.broadcast {
        cm.mutex.Lock()
        for _, conn := range cm.clients {
            (*conn).Write(msg) // 发送消息
        }
        cm.mutex.Unlock()
    }
}

func main() {
    cm := NewClientManager()
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err)
        return
    }
    defer listener.Close()
    go cm.broadcastMessages() // 启动广播 goroutine
    clientID := 0
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting:", err)
            continue
        }
        go cm.handleConnection(conn, fmt.Sprintf("client-%d", clientID))
        clientID++
    }
}

表格:广播机制优缺点

广播方式 优点 缺点 适用场景
单 goroutine 广播 简单,适合小规模连接 高并发下锁竞争严重 小型聊天室
Worker Pool 广播 降低锁竞争,资源可控 实现复杂,需管理队列 万人级并发聊天

踩坑经验 :广播时使用全局锁导致高并发下性能瓶颈。解决方案 :引入无锁队列(chan)分发消息,用固定数量的 worker goroutine 处理广播,性能提升 40%。

4.2 物联网设备数据采集

场景描述:物联网设备高频上报数据,服务器需处理每秒数万条消息,异步写入数据库。

实现要点

  • 批量处理:缓存数据到内存,定期批量写入数据库,减少 I/O 开销。
  • 异步写入:通过 channel 传递数据给数据库写入 goroutine。
  • 连接池:限制数据库连接数,防止资源耗尽。

代码示例:批量数据处理。

go 复制代码
package main

import (
    "fmt"
    "net"
    "time"
)

// DataProcessor 处理设备数据
type DataProcessor struct {
    dataChan chan []byte // 数据通道
}

// NewDataProcessor 初始化数据处理器
func NewDataProcessor() *DataProcessor {
    dp := &DataProcessor{dataChan: make(chan []byte, 1000)}
    go dp.processData() // 启动数据处理 goroutine
    return dp
}

// processData 批量处理数据
func (dp *DataProcessor) processData() {
    batch := make([][]byte, 0, 100) // 批量缓存
    ticker := time.NewTicker(time.Second) // 每秒处理一次
    for {
        select {
        case data := <-dp.dataChan:
            batch = append(batch, data)
            if len(batch) >= 100 { // 达到批量阈值
                dp.saveToDB(batch)
                batch = batch[:0]
            }
        case <-ticker.C:
            if len(batch) > 0 { // 定时处理剩余数据
                dp.saveToDB(batch)
                batch = batch[:0]
            }
        }
    }
}

// saveToDB 模拟数据库写入
func (dp *DataProcessor) saveToDB(batch [][]byte) {
    fmt.Printf("Saving %d records to DB\n", len(batch))
}

func handleConnection(conn net.Conn, dp *DataProcessor) {
    defer conn.Close()
    buffer := make([]byte, 1024)
    for {
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("Error reading:", err)
            return
        }
        dp.dataChan <- buffer[:n] // 发送数据到处理通道
    }
}

示意图:物联网数据处理流程

css 复制代码
[设备] ----> [net.Listener] ----> [goroutine: handleConnection]
                                            |
                                            v
                                     [chan: dataChan]
                                            |
                                            v
                                  [DataProcessor: 批量写入 DB]

踩坑经验 :未使用批量写入导致数据库连接池耗尽。解决方案 :引入批量处理和连接池,结合 context 管理写入超时,降低数据库压力。

4.3 游戏服务器

场景描述:游戏服务器需低延迟、高吞吐量,处理玩家输入并同步状态。

实现要点

  • 自定义二进制协议:使用紧凑格式减少带宽占用。
  • 零拷贝优化:复用缓冲区,减少内存分配。
  • 异常处理:妥善处理客户端异常断开。

代码示例:二进制协议处理。

go 复制代码
package main

import (
    "encoding/binary"
    "fmt"
    "io"
    "net"
)

// GameMessage 表示游戏消息
type GameMessage struct {
    Type uint8  // 消息类型
    Data []byte // 消息内容
}

// handleGameConnection 处理游戏客户端连接
func handleGameConnection(conn net.Conn) {
    defer conn.Close()
    for {
        // 读取消息类型(1 字节)
        typeBuf := make([]byte, 1)
        _, err := io.ReadFull(conn, typeBuf)
        if err != nil {
            fmt.Println("Error reading type:", err)
            return
        }
        // 读取消息长度(4 字节)
        lenBuf := make([]byte, 4)
        _, err = io.ReadFull(conn, lenBuf)
        if err != nil {
            fmt.Println("Error reading length:", err)
            return
        }
        length := binary.BigEndian.Uint32(lenBuf)
        // 读取消息内容
        data := make([]byte, length)
        _, err = io.ReadFull(conn, data)
        if err != nil {
            fmt.Println("Error reading data:", err)
            return
        }
        fmt.Printf("Received game message: Type=%d, Data=%s\n", typeBuf[0], data)
        conn.Write([]byte("OK\n"))
    }
}

表格:协议选择对比

协议类型 延迟 带宽占用 开发复杂性 适用场景
JSON 原型开发
Protobuf 跨平台游戏
自定义二进制协议 高性能 MMO

踩坑经验 :客户端异常断开导致 goroutine 泄漏。解决方案 :使用 context 管理连接生命周期,结合 runtime.NumGoroutine() 监控。


5. 最佳实践与注意事项

打造高性能 TCP 服务器就像建造一座坚固大厦:需要稳固的地基(连接管理)、高效的结构(并发模型)和完善的防护(安全和监控)。以下是最佳实践和注意事项。

5.1 连接管理

  • 限制最大连接数 :使用信号量(如 semaphore.Weighted)控制并发。
  • 优雅关闭连接 :通过 context 确保资源释放。

代码示例:优雅关闭 TCP 服务器。

go 复制代码
package main

import (
    "context"
    "fmt"
    "net"
    "sync"
    "time"
)

// Server 管理 TCP 服务器
type Server struct {
    listener net.Listener
    wg       sync.WaitGroup
}

// NewServer 创建服务器
func NewServer() *Server {
    return &Server{}
}

// Start 启动服务器
func (s *Server) Start(ctx context.Context, addr string) error {
    var err error
    s.listener, err = net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    fmt.Println("Server listening on", addr)
    go s.acceptConnections(ctx)
    return nil
}

// acceptConnections 接受连接
func (s *Server) acceptConnections(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            s.listener.Close()
            return
        default:
            conn, err := s.listener.Accept()
            if err != nil {
                fmt.Println("Error accepting:", err)
                continue
            }
            s.wg.Add(1)
            go s.handleConnection(ctx, conn)
        }
    }
}

// handleConnection 处理连接
func (s *Server) handleConnection(ctx context.Context, conn net.Conn) {
    defer s.wg.Done()
    defer conn.Close()
    buffer := make([]byte, 1024)
    for {
        select {
        case <-ctx.Done():
            return
        default:
            n, err := conn.Read(buffer)
            if err != nil {
                fmt.Println("Error reading:", err)
                return
            }
            conn.Write([]byte("Message received\n"))
        }
    }
}

// Shutdown 优雅关闭服务器
func (s *Server) Shutdown() {
    s.listener.Close()
    s.wg.Wait() // 等待所有连接处理完成
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    server := NewServer()
    go func() {
        if err := server.Start(ctx, ":8080"); err != nil {
            fmt.Println("Server failed:", err)
        }
    }()
    // 模拟运行一段时间后关闭
    time.Sleep(10 * time.Second)
    cancel()
    server.Shutdown()
}

5.2 错误处理

  • 统一错误日志格式 :使用 zaplogrus 记录结构化日志。
  • 使用 context:控制请求生命周期,确保资源释放。

踩坑经验 :杂乱的错误日志增加调试难度。解决方案 :使用 zap 设置 JSON 格式日志,添加请求 ID 跟踪。

5.3 性能测试

  • 工具 :使用 wrkab 测试 QPS、响应时间和错误率。

表格:性能测试工具对比

工具 优点 缺点 适用场景
wrk 高性能,支持 Lua 脚本定制 配置稍复杂 高并发压力测试
ab 简单易用,Apache 自带 功能较单一 快速验证小型服务器

5.4 安全考虑

  • 防止 DDoS :使用 golang.org/x/time/rate 限制连接速率。
  • TLS 加密 :通过 crypto/tls 保护数据安全。

踩坑经验 :未启用 TLS 导致数据泄露。解决方案:启用 TLS 并定期轮换证书。

5.5 部署建议

  • Docker 容器化:简化部署和版本管理。
  • Kubernetes:实现自动扩展和高可用。

表格:部署方案对比

方案 优点 缺点 适用场景
裸机部署 简单,适合小型项目 扩展性和维护性差 开发测试
Docker 容器化 一致性强,易于版本管理 需学习容器技术 中大型项目
Kubernetes 高可用,自动扩展 配置复杂,资源占用高 生产级高并发服务

踩坑经验 :无容器化部署导致升级中断。解决方案:使用 Docker 和 Kubernetes 实现滚动更新。


6. 总结与展望

构建高性能 TCP 服务器就像烹饪一道佳肴:Go 提供了优质食材(goroutines、net 包、并发原语),我们通过连接管理、并发优化和协议设计将其烹制成高效服务。从聊天服务器到物联网和游戏服务器,Go 的轻量级并发和简洁 API 让开发者以最小代价实现高性能。

实践建议

  • 从小处着手:从简单 TCP 服务器开始,逐步引入 Worker Pool 和自定义协议。
  • 监控为王:集成 Prometheus 和 pprof 实时监控。
  • 拥抱社区:参考 Redis 的 Go 实现等开源项目。
  • 持续优化:通过性能测试和 profiling 解决瓶颈。

未来展望:Go 在网络编程领域潜力无限。eBPF 集成将进一步优化网络性能,QUIC 支持将提升低延迟通信能力。鼓励探索 WebSocket 或 gRPC 等技术,迎接更复杂挑战。

个人心得:Go 的简洁性和并发模型让我在开发高性能服务器时事半功倍。结合社区资源和监控工具,可以快速迭代和优化,强烈推荐在实际项目中实践这些技巧。


7. 参考资料

相关推荐
蝸牛ちゃん1 小时前
万字深度详解DHCP服务:动态IP地址分配的自动化引擎
网络·网络协议·tcp/ip·系统架构·自动化·软考高级·dhcp
SoFlu软件机器人7 小时前
秒级构建消息驱动架构:描述事件流程,生成 Spring Cloud Stream+RabbitMQ 代码
分布式·架构·rabbitmq
之墨_7 小时前
【大语言模型入门】—— Transformer 如何工作:Transformer 架构的详细探索
语言模型·架构·transformer
帽儿山的枪手8 小时前
HVV期间,如何使用SSH隧道绕过内外网隔离限制?
linux·网络协议·安全
charlie1145141918 小时前
设计自己的小传输协议 导论与概念
c++·笔记·qt·网络协议·设计·通信协议
(Charon)10 小时前
【C语言网络编程】HTTP 客户端请求(基于 Socket 的完整实现)
网络·网络协议·http
Jacob023412 小时前
为什么现在越来越多项目选择混合开发?从 WebAssembly 在无服务器中的表现说起
架构·rust·webassembly
乌恩大侠13 小时前
USRP X440 和USRP X410 直接RF采样架构的优势
5g·fpga开发·架构·usrp·usrp x440·usrp x410
十字路口的火丁13 小时前
Go 项目通过 mysql 唯一键实现的分布式锁存在什么问题?
go
俞凡15 小时前
[大厂实践] Netflix 时间序列数据抽象层实践
架构