【C/C++】从 POSIX Socket 到 TCP 生命周期:一文理解网络 IO 的核心原理

从零构建C++ TCP通信框架:Socket API、粘包处理与高并发设计 TCP(传输控制协议)是构建可靠网络通信的基石,它通过三次握手建立连接、滑动窗口机制保证数据可靠传输与流量控制。理解TCP的流式特性是解决网络编程核心挑战——如粘包/拆包问题的关键。在工程实践中,直接使用操作系统提供的原生Socket API进行开发,能深入掌握连接管理、字节序处理、I/O模型等底层原理,这是学习高性能网络编程不可逾越的一步。基于此构建的C++通信框架,通过封装Buffer类处理不定长数据流、设计TcpSession管理连接生命周期,并结合多线程或IO多路复用技术(如epoll),能够有效支 阅读详情

【C/C++】从 POSIX Socket 到 TCP 生命周期:一文理解网络 IO 的核心原理

一、先建立一张总图:socket API 调用链

客户端与服务器的 API 看起来是两条不同的路径,但它们最终都围绕同一件事:让用户态代码拿到一个文件描述符 fd,并通过这个 fd 操作内核里的 TCP 连接。

在这里插入图片描述

典型客户端调用顺序:

socket() -> bind() 可选 -> connect() -> send()/recv() -> close()

典型服务器调用顺序:

socket() -> bind() -> listen() -> accept() -> recv()/send() -> close()

这里有一个很重要的理解:网络编程表面上是在读写 fd,本质上是在驱动内核维护 socket、TCP 控制块、收发缓冲区以及 TCP 状态机。

二、socket():创建 fd 与 TCP 控制块

socket() 的结果通常是一个整数 fd:

int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
    perror("socket");
    return -1;
}

对应用层来说,fd 像一个“句柄”;对内核来说,它背后关联着 socket 对象和协议控制块。对于 TCP 连接,控制块里会保存本端地址、对端地址、端口、状态、窗口、序列号、重传定时器等信息。

可以把它粗略理解成:

用户态 fd
  -> 内核 file
    -> socket
      -> TCP 控制块

这也是为什么我们说“fd 对应一个连接”,更准确地说是“fd 通过内核对象间接引用一个连接”。

三、bind():把 IP 和端口写入控制块

服务器必须 bind(),因为它需要告诉内核:我要监听哪个本地 IP 和端口。

struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    perror("bind");
    return -1;
}

客户端通常不需要显式 bind()。如果不手动绑定,内核会自动选择一个本地临时端口。只有在需要固定本地端口、多网卡选择出口 IP、或做特殊网络测试时,客户端才常见显式 bind()。

四、listen(backlog):进入 LISTEN,并准备队列

listen(fd, backlog) 不是“开始 accept”,而是把 socket 变成监听 socket,让 TCP 状态进入 LISTEN,并准备处理连接建立过程中的队列。

在这里插入图片描述

常见理解里,服务端连接建立会涉及两个队列:

  • SYN Queue:半连接队列,保存已经收到 SYN、回复了 SYN+ACK,但还没有收到第三次 ACK 的连接。
  • Accept Queue:全连接队列,三次握手完成后,连接进入这里,等待应用层 accept() 取走。

历史上,不同内核版本对 backlog 的语义有所变化。工程上更实用的记法是:

  • listen(backlog) 影响全连接排队能力。
  • SYN 队列还会受到 tcp_max_syn_backlog、SYN Cookie 等机制影响。
  • accept() 不及时,会让全连接队列堆积,最终导致新连接建立变慢或失败。

可以用下面的命令观察相关配置:

sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
ss -lnt

五、TCP 三次握手:确认双方初始序列号

三次握手不只是“连上了”,更关键的是双方同步初始序列号,并确认双方收发能力正常。

在这里插入图片描述

简化过程如下:

1. 客户端 connect(),发送 SYN,seq = x,进入 SYN_SENT
2. 服务端收到 SYN,进入 SYN_RCVD,回复 SYN + ACK,seq = y,ack = x + 1
3. 客户端收到后进入 ESTABLISHED,回复 ACK,ack = y + 1
4. 服务端收到第三次 ACK,连接进入 ESTABLISHED,并进入 accept 队列

第三次 ACK 到达服务端时,内核会根据五元组查找对应半连接:

源 IP、源端口、目的 IP、目的端口、协议

找到后,连接从半连接队列迁移到全连接队列。此时应用层调用 accept(),才能得到一个新的连接 fd。

一个容易忽略的点:P2P 同时打开

普通 C/S 模型里,服务器先 listen(),客户端再 connect()。但 TCP 协议本身支持 simultaneous open:双方都没有处于 LISTEN,而是同时发起 connect(),双方互相发送 SYN,也可能建立连接。这类场景在 P2P、打洞和协议实验里更容易遇到。

六、accept():从全连接队列取连接

accept() 做的事情可以粗略理解为:

从 accept queue 取出一个已完成握手的连接
为这个连接分配一个新的 fd
让应用层后续通过这个 fd recv/send

如果监听 fd 设置了边缘触发 EPOLLET,必须把监听 fd 设置成非阻塞,并且在一次事件通知里循环 accept(),直到返回 EAGAIN。

while (1) {
    int cfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK);
    if (cfd >= 0) {
        // 把 cfd 加入 epoll,后续关注读写事件
        add_epoll(epfd, cfd, EPOLLIN | EPOLLRDHUP | EPOLLET);
        continue;
    }

    if (errno == EAGAIN || errno == EWOULDBLOCK) {
        // accept queue 已经取空
        break;
    }

    perror("accept4");
    break;
}

边缘触发的核心是“状态从无到有时通知一次”。如果你只 accept() 一次,队列里剩下的连接可能不会再次触发通知,导致连接被饿住。

七、send/write 与 recv/read:读写的是内核缓冲区

send() 并不等于“数据已经到达对端业务代码”,它通常只是把数据拷贝到本机内核发送缓冲区,后续由 TCP 协议栈负责分段、重传、拥塞控制和确认。

ssize_t n = send(fd, data, len, 0);
if (n < 0) {
    perror("send");
}

recv() 也不是直接从网卡取数据,而是从内核接收缓冲区读取已经到达、按序交付给应用层的数据。

char buf[4096];
ssize_t n = recv(fd, buf, sizeof(buf), 0);
if (n > 0) {
    // buf[0..n) 是本次读到的数据
} else if (n == 0) {
    // 对端关闭连接
} else {
    perror("recv");
}

TCP 在传输阶段还会涉及:

  • 滑动窗口:控制发送方最多可以发送多少未确认数据。
  • 慢启动:拥塞窗口从小到大试探网络容量。
  • 拥塞控制:根据丢包、延迟等信号调节发送速率。
  • 延迟确认:接收端可能稍后再 ACK,以减少小包。
  • 超时重传:数据迟迟没有确认时重新发送。

这些机制都说明:应用层的一次 send(),不等于网络上的一次完整传输。

八、epoll + Reactor:把连接存储和事件分发解耦

当连接数上来之后,服务器通常不会为每个连接创建一个线程,而是用 epoll 等 IO 多路复用机制管理大量 fd。

在这里插入图片描述

典型 Reactor 思路:

连接表保存 fd -> Connection
epoll 负责监听 fd 就绪事件
epoll_wait 返回活跃事件
Reactor 根据事件类型分发给不同 handler

如果你要优化 Reactor 的连接存储,可以考虑按 fd 直接索引一个连接数组。比如预分配 1048576 个槽位:

#define MAX_CONN 1048576

typedef struct connection {
    int fd;
    char rbuf[4096];
    char wbuf[4096];
    size_t wlen;
} connection_t;

static connection_t *connections[MAX_CONN];

connection_t *get_conn(int fd) {
    if (fd < 0 || fd >= MAX_CONN) {
        return NULL;
    }
    return connections[fd];
}

这种方式查询快,代价是数组空间固定。如果 fd 上限很大、连接稀疏,也可以改成哈希表或对象池。

九、完整代码片段:epoll 边缘触发 Echo Server

下面是一个精简版 Linux C 示例,重点展示监听 socket、非阻塞、epoll_wait、循环 accept/read 的结构。

#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#define MAX_EVENTS 1024

static int set_nonblock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

static void add_epoll(int epfd, int fd, uint32_t events) {
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.events = events;
    ev.data.fd = fd;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        perror("epoll_ctl add");
        close(fd);
    }
}

int main(void) {
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
        perror("socket");
        return 1;
    }

    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(listenfd, SOMAXCONN) < 0) {
        perror("listen");
        return 1;
    }

    if (set_nonblock(listenfd) < 0) {
        perror("set_nonblock");
        return 1;
    }

    int epfd = epoll_create1(0);
    if (epfd < 0) {
        perror("epoll_create1");
        return 1;
    }

    add_epoll(epfd, listenfd, EPOLLIN | EPOLLET);

    struct epoll_event events[MAX_EVENTS];
    char buf[4096];

    while (1) {
        int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
        if (n < 0) {
            if (errno == EINTR) continue;
            perror("epoll_wait");
            break;
        }

        for (int i = 0; i < n; ++i) {
            int fd = events[i].data.fd;
            uint32_t ev = events[i].events;

            if (fd == listenfd) {
                while (1) {
                    int cfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK);
                    if (cfd >= 0) {
                        add_epoll(epfd, cfd, EPOLLIN | EPOLLRDHUP | EPOLLET);
                        continue;
                    }
                    if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                    perror("accept4");
                    break;
                }
                continue;
            }

            if (ev & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
                close(fd);
                continue;
            }

            if (ev & EPOLLIN) {
                while (1) {
                    ssize_t len = read(fd, buf, sizeof(buf));
                    if (len > 0) {
                        // Echo:读到什么就写回什么。真实业务要处理半包、粘包和写缓冲。
                        ssize_t off = 0;
                        while (off < len) {
                            ssize_t wn = write(fd, buf + off, (size_t)(len - off));
                            if (wn > 0) {
                                off += wn;
                            } else if (wn < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
                                break;
                            } else {
                                close(fd);
                                break;
                            }
                        }
                        continue;
                    }
                    if (len == 0) {
                        close(fd);
                        break;
                    }
                    if (errno == EAGAIN || errno == EWOULDBLOCK) {
                        break;
                    }
                    perror("read");
                    close(fd);
                    break;
                }
            }
        }
    }

    close(epfd);
    close(listenfd);
    return 0;
}

编译运行:

gcc -O2 -Wall -Wextra epoll_echo.c -o epoll_echo
./epoll_echo

客户端可以用 nc 测试:

nc 127.0.0.1 8080

十、TCP 四次挥手:为什么主动关闭方会 TIME_WAIT

连接关闭时,close() 会回收用户态 fd,并驱动 TCP 发送 FIN。常见过程如下:

在这里插入图片描述

简化状态迁移:

A 主动 close:FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED
B 被动关闭:CLOSE_WAIT -> LAST_ACK -> CLOSED

为什么经常是四次?因为 TCP 是全双工协议。A 说“我不发了”不代表 B 也立刻不发。B 可以先 ACK A 的 FIN,等业务层也关闭后再发送自己的 FIN。

TIME_WAIT 的意义主要有两个:

  • 确保最后一个 ACK 有机会被对端收到;如果丢了,对端会重传 FIN。
  • 等待网络中旧报文自然消失,避免影响后续复用同一四元组的新连接。

还有一种特殊情况:双方同时 close(),都在 FIN_WAIT_1 时先收到对方 FIN,就会进入 CLOSING,最终仍会走向 TIME_WAIT 或 CLOSED。

至于 shutdown(),它用于半关闭连接,例如只关闭写方向但继续读。普通业务如果不需要半关闭,直接 close() 更简单;如果协议需要“我发完了,但还要等你响应”,再考虑 shutdown(fd, SHUT_WR)。

十一、客户端最小代码片段

最后给一个最小 TCP 客户端,用来和前面的服务器配合测试。

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void) {
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return 1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);

    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("connect");
        return 1;
    }

    const char *msg = "hello tcp\n";
    send(fd, msg, strlen(msg), 0);

    char buf[1024];
    ssize_t n = recv(fd, buf, sizeof(buf) - 1, 0);
    if (n > 0) {
        buf[n] = '\0';
        printf("recv: %s", buf);
    }

    close(fd);
    return 0;
}

总结

网络 IO 的学习不能只停留在 API 名字上。更好的方式是把 API、内核对象和 TCP 状态机对应起来:

  • socket() 创建 fd,并关联内核 socket/TCP 控制块。
  • bind() 设置本地 IP 和端口。
  • listen() 进入监听状态,并准备连接队列。
  • connect() 触发三次握手。
  • accept() 从全连接队列取出连接,并返回新的 fd。
  • send/recv 操作的是内核缓冲区,不等于数据立刻到达对端应用。
  • epoll 负责事件通知,Reactor 负责事件分发。
  • close() 触发连接关闭,主动关闭方通常会进入 TIME_WAIT。

当这些概念连成一条线,Linux 网络编程里的很多“为什么”就会变得清楚:为什么 ET 模式要非阻塞?为什么 accept 要循环?为什么 send 可能只写入部分数据?为什么服务端会出现大量 CLOSE_WAIT 或 TIME_WAIT?这些问题的答案,基本都藏在 fd、缓冲区、队列和 TCP 状态机之间。

学习链接: https://github.com/0voice

从TinyHttpd入门C++网络编程:解析HTTP服务器核心原理与实战 网络编程是构建现代分布式系统的基石,其核心在于理解客户端与服务器之间通过协议进行通信的原理。HTTP协议作为Web通信的基础,定义了请求与响应的标准格式。从技术实现层面看,一个基础的HTTP服务器需要处理socket通信、并发模型和协议解析等关键环节。其技术价值在于通过最简化的设计,清晰地揭示Web服务器从接收请求到返回响应的完整流程,是学习高性能服务端开发的绝佳起点。典型的应用场景包括教学演示、原型验证以及对Web服务器底层机制的理解。本文以经典的TinyHttpd项目为例,深入剖析其多进程模型和CGI机 阅读详情

相关推荐

UE5 C++ TCP服务器开发:从BlankProgram到高性能网络服务

网络编程是现代软件开发的核心技能之一,其底层原理基于Socket通信机制,通过TCP/IP协议栈实现可靠的数据传输。在游戏开发和高性能服务领域,网络架构直接决定了系统的并发处理能力和响应效率。UE5作为领先的游戏引擎,其C++框架不仅支持蓝图可视化编程,更提供了强大的底层扩展能力,能够构建脱离编辑器运行的独立服务器程序。通过结合原生Socket API与UE5的模块化系统,开发者可以创建轻量级、可独立部署的网络服务,适用于游戏服务器、后台数据处理和自动化测试等多种应用场景。本文以构建TCP服务器为例,详细解

weixin_30580341的博客 351

【Linux网络】深入理解Linux IO多路复用:从本质到select服务器实战

在上一篇文章中,我们详细讲解了 Linux 下的五种 IO 模型,其中IO 多路复用是目前高并发服务器的基石。它用一个线程就能同时处理成百上千个连接,彻底解决了阻塞 IO"一个连接一个线程" 的性能瓶颈。而select,作为 Linux 系统上第一个实现 IO 多路复用的系统调用,虽然现在已经逐渐被更高效的epoll取代,但它是理解所有多路复用技术的基础。几乎所有操作系统都实现了select接口,掌握它不仅能帮你理解多路复用的核心思想,也是面试中绕不开的考点。本文将从最底层的多路转接本质讲起,带你彻底搞懂s

会当凌绝顶,一览众山小 6432

Linux C语言网络编程:从Socket到epoll的并发服务器实战

网络编程是构建分布式系统和网络应用的核心技术,其基础在于理解操作系统提供的进程间通信机制。在Linux环境下,系统调用和内核机制为网络通信提供了最直接的访问途径。其核心原理围绕Socket API展开,通过创建、绑定、监听和连接等系统调用,实现不同主机间的数据交换。这项技术的核心价值在于能够以接近硬件和操作系统内核的方式,实现对网络通信的精细控制和高性能处理,尤其适合开发需要极致性能和高并发的服务器程序,如Web服务器、实时通信服务和分布式系统节点。在实际应用中,开发者需要根据场景选择合适的并发模型,例如多

weixin_34007886的博客 700

C++ TCP网络编程实战:从Socket API到工业级通信模块构建

网络编程是构建分布式系统、游戏服务器和物联网应用的核心基础,其中TCP协议因其可靠的数据传输特性成为关键选择。TCP通过连接管理、确认应答和流量控制等机制,确保了数据在不可靠网络中的有序、无差错送达,这一原理使其在金融交易、文件传输等数据完整性要求高的场景中不可或缺。理解阻塞与非阻塞I/O模型、Socket编程接口以及粘包处理等底层技术,对于开发高性能、高可用的网络服务具有重要价值。本文聚焦于C++语言,通过手把手实战,深入讲解如何利用标准库和系统API,从零构建一个处理并发连接、实现可靠数据收发的TCP通

weixin_33924770的博客 348

C++网络编程实战:从Socket封装到事件驱动模型的设计与实现

网络编程是构建分布式系统和后端服务的核心技术基础,其核心在于通过操作系统提供的Socket API在不同主机间建立可靠或不可靠的数据通道。理解Socket的工作原理、IO模型和协议栈是掌握网络编程的关键。从基础的阻塞IO到高性能的事件驱动模型(如epoll/kqueue),技术的演进旨在解决C10K乃至C100K问题,提升系统的并发处理能力和资源利用率。在实际工程中,一个设计良好的网络抽象层能显著提升代码的可维护性和健壮性,有效处理连接管理、错误恢复和资源释放等复杂问题。本文聚焦于C++环境,通过封装原生S

weixin_30323631的博客 375

网络编程面试八股:TCP/IP、Socket与Epoll核心知识点全解析

网络编程是后端开发与Linux服务端岗位面试中的核心能力,其底层逻辑涉及TCP/IP协议栈、Socket通信模型与I/O多路复用机制。理解TCP三次握手与四次挥手的状态流转,掌握滑动窗口、流量控制与拥塞控制的设计思想,是构建可靠网络服务的基石。同时,Socket编程从阻塞到非阻塞、从多线程到事件驱动的演进,以及select、poll、epoll在高并发场景下的取舍,直接体现工程师的工程实践水平。面对粘包拆包、TIME_WAIT堆积、半连接队列溢出等高频问题,不仅需要懂原理,更要具备借助抓包工具与自动化压测定

weixin_34242819的博客 311

C++原生实现HTTP服务器:从Socket到协议解析的底层实践

网络编程是构建分布式系统和Web服务的核心技术基础,其核心原理在于通过套接字(Socket)实现进程间通信。HTTP协议作为应用层协议,基于TCP/IP栈,定义了客户端与服务器之间的请求-响应交互模型。理解底层实现能帮助开发者优化性能、排查问题,并在资源受限或高定制化场景中实现完全控制。通过原生C++实现HTTP服务,开发者可以深入掌握从连接建立、数据读写到协议解析的完整流程,这对于嵌入式开发、高性能中间件和网络库开发具有重要价值。本文聚焦于使用C++标准库和操作系统API,从零构建一个支持基础HTTP/1

weixin_30879833的博客 540

Linux下C++编程实战:从环境搭建到系统级开发

C++作为一门贴近系统底层的编程语言,其核心价值在于对内存、硬件和操作系统资源的精细控制。理解编译链接原理、内存管理机制和系统调用是掌握C++的关键。在工程实践中,这些知识能帮助开发者构建高性能、高可靠的服务器端、嵌入式及基础设施软件。Linux操作系统以其透明的工具链和“一切皆文件”的哲学,为深入理解这些概念提供了绝佳环境。通过GCC/G++、GDB、Make/CMake等工具,开发者可以完整实践从源代码到可执行文件的构建、调试与优化全流程。本文聚焦于在Linux环境下进行C++系统编程的实战路径,涵盖环

weixin_34008784的博客 592

C++从零构建网盘系统:Socket、协议设计、断点续传与高并发优化

网络编程是构建分布式系统的基石,其核心在于理解数据如何在网络中可靠传输。TCP/IP协议栈提供了端到端的通信保障,而应用层协议设计则决定了服务的具体形态与效率。掌握Socket编程、I/O多路复用及并发模型,对于开发高性能服务器具有极高的技术价值,广泛应用于即时通讯、文件传输和云计算存储等场景。本文以C++实现一个支持多用户和断点续传的网盘系统为例,深入探讨了从定义二进制协议、处理TCP粘包拆包,到实现文件分块上传和利用SQLite管理元数据等关键技术细节。通过将业务逻辑与网络I/O解耦,并引入线程池处理计

weixin_33976072的博客 326

Linux C系统编程实战指南:从epoll网络编程到内存管理避坑

在Linux系统编程领域,掌握核心系统调用和编程模型是构建高性能、高可靠软件的基础。理解进程间通信、内存管理和网络I/O多路复用等底层原理,是提升代码质量和性能的关键。这些技术广泛应用于服务器开发、嵌入式系统和基础设施软件中,能够有效解决高并发、低延迟等工程挑战。本文聚焦于Linux C编程的实战场景,深入解析epoll事件驱动模型、边缘触发与水平触发的核心差异,并探讨共享内存对齐、缓存伪共享等高级内存管理技巧,旨在帮助开发者规避常见陷阱,编写更健壮的系统级代码。

cunfuxiao7305的博客 771

epoll与io_uring深度对比:I/O多路复用与真正异步I/O的本质差异

I/O模型是操作系统与应用程序协同处理数据流动的基础机制。从阻塞I/O到非阻塞轮询,再到I/O多路复用(如epoll)和真正异步I/O(如io_uring),其演进核心在于‘等待成本’的转移路径:由线程挂起、忙等、事件驱动,最终走向内核全权接管与零拷贝完成。epoll通过红黑树+就绪链表实现高效事件通知,已成为高并发网络服务的事实标准;而io_uring依托共享内存环形缓冲区与内核专用线程,突破系统调用瓶颈,支撑存储与网络I/O的极致性能。二者并非替代关系,而是适配不同负载特征——epoll胜在确定性低延迟

rongdmmap的博客 499

【Linux网络】深入理解Linux IO多路复用:select服务器完善、内核原理与poll实战

上一篇我们从零搭建了 select 服务器的基础框架,实现了新连接的接受与托管。但当时的代码还比较粗糙,所有逻辑都挤在一个处理函数里,也没有处理客户端数据读写与连接关闭。这一篇我们先把 select 服务器完善成一个完整的 echo 服务器,引出事件编程的核心思想;然后深入内核层面,拆解 select 到底是怎么实现「同时等待多个 fd」的;最后我们再认识 poll —— 作为 select 的改进版,它解决了什么问题,又留下了什么遗憾,并亲手把 select 服务器改写成 poll 版本。

会当凌绝顶,一览众山小 4631

ROS Talker/Listener原理深度解析:从Hello World到工业级通信

ROS话题(Topic)是机器人分布式系统中最基础的异步通信机制,其发布-订阅模型基于节点(Node)进程隔离、主节点(Master)动态注册与P2P数据传输三大原理,兼顾松耦合性与实时性。该机制避免了RPC调用的阻塞风险,支撑激光雷达、IMU、视觉等多源高频数据流并行处理,在AGV调度、无人机编队、手术机器人等场景中成为低延迟通信的事实标准。本文以经典talker/listener为例,深入剖析节点生命周期、回调队列调度、消息缓冲策略及底层TCP直连机制,揭示ROS如何在Linux进程模型之上构建确定性通

weixin_30876945的博客 401

C++网络编程实战:从零构建高并发即时通信服务器

网络编程是构建分布式系统的核心技术,其核心在于理解TCP/IP协议栈与Socket通信原理。通过Socket API,应用程序能够在网络中进行可靠的数据传输。在网络通信中,解决TCP粘包/拆包问题是关键,通常需要设计应用层协议来明确消息边界,例如采用“长度头+类型头+载荷体”的二进制格式,并注意字节序转换。这项技术的价值在于能够支撑起各种需要实时数据交换的应用场景,如即时通讯、在线游戏和物联网设备交互。本文以构建一个类似“飞鸽传书”的局域网通信系统为例,深入探讨了如何运用IO复用、线程池和自定义协议等**网

weixin_33874713的博客 389

C++开发者进阶指南:从Linux核心编程到AI基础设施与音视频实战

C++作为一门高性能系统编程语言,其核心价值在于对底层硬件和操作系统资源的精细控制。其工作原理基于直接内存管理和编译型语言的特性,能够实现零成本抽象,在性能敏感场景中具有不可替代的技术优势。这一特性使其在高性能计算、基础设施软件和实时系统中应用广泛。特别是在AI基础设施领域,模型推理引擎和计算框架底层对极致性能的追求,以及音视频处理中对编解码效率和实时性的严苛要求,都深度依赖C++的工程实践能力。本文聚焦于Linux环境下C++开发的工程化路径,涵盖从开发环境配置、内存管理与多线程并发,到网络编程、AI推理

weixin_30384217的博客 397

单节锂电池充电管理芯片应用原理阐述及应用领域

当电池电压超过2.9V时,采用恒流模式对电池充电,充电电流由PROG 管脚和GND之间的电阻RPROG确定。充电电压固定于4.2V,而充电电流可通过一个电阻器进行外部设置当充电电流在达到最终浮充电压之后降至设定值1/10时,WD4054将自动终止充电循环。在待机模式中,WD4054 对 BAT 引脚电压进行监控,只有当 BAT 引脚电压低于再充电阈值电压4.1V时(对应电池容量80%~90%),才会开始新的充电循环,重新对电池进行充电,这就避免了对电池进行不必要的反复充电,有效延长电池的使用寿命。

weixin_41141180的博客 943

C++ Boost.Asio实现多线程端口扫描器:从异步I/O到性能优化

网络编程的核心在于高效处理并发连接,而异步I/O模型是实现这一目标的关键技术。通过操作系统提供的I/O多路复用机制,程序可以用少量线程管理大量网络套接字,在连接就绪时通过回调函数处理,从而极大提升吞吐量。这种技术在构建高性能网络工具时具有重要价值,例如端口扫描器就需要快速探测大量目标端口的状态。本文以C++ Boost.Asio库为基础,深入剖析了如何利用异步I/O和多线程技术实现一个高效的端口扫描器,重点解决了超时控制、线程同步等工程实践问题,并探讨了线程池和连接池等性能优化方案,为网络诊断和系统运维提供

weixin_30706691的博客 403

Calibre PEX提参实战:如何用hcell和xcell文件精准屏蔽模块(附避坑指南)

本文深入解析Calibre PEX工具中hcell与xcell文件的协同工作机制,提供模块级寄生参数提取方案。通过实战案例和避坑指南,帮助工程师精准屏蔽已验证IP模块,提升仿真效率和数据一致性,适用于65nm以下工艺节点的复杂SoC设计。

weixin_29275257的博客 626

C++多线程套接字服务器实战:从阻塞模型到线程池优化

套接字编程是网络通信的基础,它通过TCP/IP协议栈实现进程间数据交换。其核心原理在于操作系统提供的socket API,它抽象了网络端点,允许程序通过文件描述符进行读写操作。这项技术的价值在于构建分布式系统和客户端-服务器应用,是实现Web服务、即时通讯、游戏联机等场景的底层支撑。在实际工程中,单线程阻塞式模型无法满足并发需求,因此多线程架构成为提升服务器吞吐量的关键。通过为每个连接创建独立线程,服务器可以同时处理多个客户端请求,但需注意线程资源管理。本文聚焦于C++多线程套接字服务器的实现,详细解析了*

weixin_34289454的博客 606

Global Mapper与ArcGIS双剑合璧:自动化与半自动矢量化提取地物轮廓实战

本文详细介绍了如何结合Global Mapper的自动化批量提取与ArcGIS的半自动精细修整功能,高效完成地物轮廓矢量化工作。通过实战案例,阐述了利用Global Mapper快速抓取初步轮廓,再借助ArcGIS的ArcScan等工具进行精细化编辑和优化的完整工作流,有效提升地理信息数据处理效率。

weixin_29284657的博客 119

滑模控制算法(SMC)讲解-案例(附C代码)

该文章介绍了滑模控制算法的原理实现步骤,特点及关键问题,最后以车辆控制案例来讲解滑模控制的整个步骤实现

heda3的博客 4111

Windows平台UDP Socket编程实战:从基础到高性能网络通信

UDP(用户数据报协议)是一种无连接、不可靠但高效的网络传输协议,其核心原理在于面向数据报的通信模型,每个数据包独立传输且边界清晰。相比TCP的可靠连接,UDP具有更小的协议开销和更低的延迟,这使得它在实时性要求高的场景中展现出独特的技术价值。在视频流媒体、在线游戏、物联网设备通信和局域网服务发现等应用场景中,UDP能够有效避免TCP重传机制带来的延迟累积。Windows平台下的UDP Socket编程涉及Winsock库的特殊行为,例如Socket错误码处理(如WSAECONNRESET 10054)和缓

weixin_30788619的博客 371

【PowerBI专栏】PowerBI导入MYSQL数据库

本文介绍了在PowerBI中连接MySQL数据库的方法。首先需要下载并安装MySQL官方.NET连接驱动(推荐8.4.0版本),安装完成后重启PowerBI。连接时需填写服务器IP和数据库名称等必填参数。文章详细说明了连接配置步骤,包括账户设置、数据表选择以及可能遇到的数据格式转换问题处理。最后指导用户完成数据清洗后进入PowerPivot进行后续计算。特别提醒最新9.4版本可能存在兼容性问题,建议使用稳定的8.4版本连接器。

技术与管理的平衡木- 690
上一篇: 【C/C++】C1000K 百万连接测试:Linux 参数、fd 限制和 epoll 压测客户端
下一篇: 【C/C++】从 setjmp 到 ucontext 再到 hook:C 语言协程是怎么跑起来的?
SilentSlot
博客等级 码龄16年 44粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值