C++网络编程实战:从socket到epoll的核心技术解析
简介面向初学网络编程的读者这份PDF教程以Visual C和MFC类库为开发背景系统讲解Windows平台下网络程序开发所需的基础知识。内容从网络编程概念入手先展开OSI七层网络模型说明各层职责并借助数据逐层封装与解封装的过程帮助读者直观理解网络通信的基本原理随后介绍TCP/IP协议簇区分TCP与UDP的不同应用场景再结合C/S编程模型梳理服务器监听、客户端连接请求及双方基于IP地址与端口通信的完整流程。此外还补充了Sockets套接字类型、网络字节顺序及CAsyncSocket类的使用要点为后续学习MFC网络编程打下基础。全文采用图示与分步讲解相结合适合自学入门或作为课堂教学辅助材料资源为单个PDF文件整包约114KB目前已有564人学习是快速建立VC网络编程知识框架的实用参考。1. 为什么拿《C网络编程实例.pdf》当入门主线很多人学 c 网络编程是从 socket() 这一行 API 开始的但翻完两三百页理论书依然写不出一个能稳定跑十分钟的服务端。真正让我入门的是照着《C网络编程实例.pdf》这种实例驱动的材料一点点敲先跑通 TCP 回显再改多线程最后上 epoll。它解决的痛点很明确——c入门阶段最缺的不是语法而是「一个可运行的最小程序 为什么这么写」的组合。这份资料适合两类人一类是已经会 C 语法但没碰过 socket 网络编程的开发者另一类是写过一些脚本但想回补 C 工程细节的从业者。它能让你在一天内跑通第一个客户端/服务端程序并且理解阻塞、非阻塞、粘包这些绕不开的概念。下面我按自己带团队时常用的落地路径把这套实例拆成「原理 → 复现 → 排错 → 工程化」四步。2. 从 C API 到 C 封装socket 调用链与 TCP/UDP 参数2.1 把 socket()、bind()、listen() 封装成 RAII理由与代码骨架Windows 和 Linux 的 socket 调用链大体一致socket() 创建 fdbind() 绑定地址listen() 进入监听accept() 接受连接。问题在于这中间任何一步出错fd 就可能泄漏。C 语言写法里漏掉 close() 的例子我见过太多所以拿到实例的第一步我建议先把 fd 封装成 RAII 对象。class TcpSocket { public: TcpSocket(int fd -1) : fd_(fd) {} ~TcpSocket() { if (fd_ 0) { close(fd_); // 析构时统一关闭避免 fd 泄漏 } } TcpSocket(const TcpSocket) delete; TcpSocket operator(const TcpSocket) delete; TcpSocket(TcpSocket other) noexcept : fd_(other.fd_) { other.fd_ -1; // 移动后原对象不再持有 fd } int fd() const { return fd_; } private: int fd_; };这段代码把 fd 的所有权交给了对象生命周期来管理。参数说明里有两个重点第一拷贝构造和赋值被 delete因为两个对象同时持有同一个 fd 会在析构时 double close第二移动语义把原对象的 fd 置为 -1保证只有一个对象负责关闭。实际写 accept() 返回的客户端 fd 时我会先放进 TcpSocket再传给业务线程避免中途异常导致 fd 无人关闭。2.2 阻塞与非阻塞的切换fcntl 与 setsockopt 的边界实例里最常见的第一道坎是「程序卡在 recv() 不返回」。默认 socket 是阻塞的没有数据时线程就挂在那。有两种改法用 fcntl 把 fd 设为非阻塞或者用 setsockopt 设超时。两者效果不同非阻塞模式下 recv() 会立即返回 -1errno 为 EAGAIN 或 EWOULDBLOCK而超时模式下还在阻塞只是到时间后返回 -1errno 为 EAGAIN。// 设置为非阻塞 int flags fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); // 或者设置接收超时 3 秒 struct timeval tv {3, 0}; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, tv, sizeof(tv));参数说明F_GETFL 先取出原有状态再加 O_NONBLOCK不能直接赋值否则会丢掉其他标志位。超时时间 tv 的精度到微秒对业务而言3 秒适合心跳超时5 秒适合重试等待。如果两个都设置了非阻塞优先级更高超时设置会失效。我一般在写客户端时优先用超时因为非阻塞 read 循环的代码复杂度会让新手立刻翻车。2.3 粘包半包与缓冲区设计TCP 字节流不是消息流很多初学者以为 send() 一次对端 recv() 就能收到同样的数据这是 socket 网络编程里最大的误解。TCP 是字节流不保证消息边界于是出现粘包两次 send 的数据被一次 recv 收走半包一次 send 的数据分两次 recv 收完。实例里通常用一个自定义协议头来定义消息长度常见做法是「4 字节长度 消息体」。// 读满 n 个字节处理半包 size_t readn(int fd, char* buf, size_t n) { size_t already 0; while (already n) { ssize_t len recv(fd, buf already, n - already, 0); if (len 0) { // len 0 对端关闭len 0 需要看 errno return already; // 把已读到的返回让上层决定 } already len; } return already; }参数说明readn 循环里的偏移量 buf already 是最容易写错的地方每次 recv 后必须把剩余长度减小、指针后移。返回值 len 为 0 表示对端关闭连接此时应该结束会话len 为 -1 且 errno 为 EAGAIN 时对非阻塞 fd 来说是正常情况要退出等待下次事件。实例里如果能看到这个函数基本就是用来解决半包问题的粘包则由上层协议头长度字段来判断。3. 照着实例写 TCP 回显服务器从单线程到 std::thread 多线程3.1 最小可跑版本阻塞式 Echo Server 完整代码回显服务器是网络编程实例的「hello world」客户端发什么服务端原样返回什么。第一版我建议用阻塞单线程不用考虑并发先把 accept → recv → send 的闭环跑通。#include sys/socket.h #include netinet/in.h #include unistd.h #include cstring #include cstdio int main() { int listen_fd socket(AF_INET, SOCK_STREAM, 0); sockaddr_in addr; memset(addr, 0, sizeof(addr)); addr.sin_family AF_INET; addr.sin_port htons(9000); // 端口 9000 addr.sin_addr.s_addr htonl(INADDR_ANY); // 监听所有网卡 bind(listen_fd, (sockaddr*)addr, sizeof(addr)); listen(listen_fd, 32); // 等待队列长度 32 while (true) { int client_fd accept(listen_fd, nullptr, nullptr); char buf[1024]; while (true) { ssize_t n recv(client_fd, buf, sizeof(buf), 0); if (n 0) break; // 对端关闭或出错 send(client_fd, buf, n, 0); // 原样返回 } close(client_fd); } close(listen_fd); return 0; }逻辑说明先 socket 创建 IPv4 TCP 套接字bind 绑定端口和地址listen 进入监听。外层循环每次 accept 到一个新连接内层循环收发数据直到对方关闭。这里 recv 的返回值 n 同时是发送长度避免把缓冲区末尾未初始化的数据发出去。参数说明端口用 htons 转网络字节序地址 INADDR_ANY 表示本机所有 IPlisten 的第二个参数 32 是连接请求队列上限对入门实验够用高并发场景要调大或用 epoll。3.2 多线程处理多个客户端thread 参数传递与引用陷阱单线程服务端一次只能服务一个客户端第二个客户端必须等第一个断开。实例里常见的升级是用 std::thread 为每个连接开线程。这里 c stl 的 thread 比 pthread_create 好用的地方是参数直接传值但坑也在这如果传引用线程里访问的可能是已经销毁的对象。void handle_client(int fd) { char buf[1024]; while (true) { std::ssize_t n recv(fd, buf, sizeof(buf), 0); if (n 0) break; send(fd, buf, n, 0); } close(fd); } // 在 accept 之后 std::thread t(handle_client, client_fd); t.detach(); // 分离线程不阻塞主循环逻辑说明handle_client 的参数 fd 是 int按值传递线程持有自己的副本主循环继续 accept 下一连接。如果你图省事把参数写成intclient_fd 会在下一轮循环被重新赋值线程里读到的 fd 就错了。这也是 c 引用 指针 和 值传递 区别中最容易在并发场景踩的一次。注意点detach 后线程失控程序退出时可能还在跑工程上应该用线程池配合 join但实例教学阶段detach 能让服务端立刻支持多客户端。fd 的关闭放线程内做主线程不 close避免重复关闭。3.3 客户端怎么写才不容易卡死非阻塞与超时服务端写好了客户端反而容易翻车。最常见的现象是 connect 到一个不可达 IP 时卡很久或者 recv 等响应时永久阻塞。实例里往往用 select 给 socket 设置超时而不是简单地把 fd 设为非阻塞。#include sys/select.h int wait_readable(int fd, int timeout_ms) { fd_set rds; FD_ZERO(rds); FD_SET(fd, rds); timeval tv {timeout_ms / 1000, (timeout_ms % 1000) * 1000}; return select(fd 1, rds, nullptr, nullptr, tv); } // 用法 int ret wait_readable(sockfd, 3000); if (ret 1) { recv(sockfd, buf, sizeof(buf), 0); } else if (ret 0) { printf(recv timeout\n); }逻辑说明select 的第一个参数要传最大 fd 加 1因为内核要扫描整个 fd 集合。返回值 1 表示可读0 表示超时-1 表示出错。参数说明timeout_ms 拆成秒和微秒两个字段select 在 Linux 上会把剩余时间写回 timeval所以如果要循环调用每次都要重置。这个办法比单纯 O_NONBLOCK 好理解也让客户端在正常无数据时能优雅退出不用靠信号或额外线程。4. 进阶实例HTTP 客户端与 epoll 高并发服务器怎么写4.1 手写 HTTP/1.1 GETURL 解析、connect、send、recv 的完整流程当实例从 TCP 泛化到具体协议第一个建议是手写 HTTP 客户端。它能把 socket 操作和文本协议解析结合起来。以一个简单 URLhttp://example.com:8080/index.html为例需要拆出 host、port、path然后 socket 连接并发送请求行。std::string request GET path HTTP/1.1\r\n Host: host \r\n Connection: close\r\n // 响应结束后服务器主动关闭 \r\n; send(sockfd, request.data(), request.size(), 0); std::string response; char buf[4096]; while (true) { ssize_t n recv(sockfd, buf, sizeof(buf), 0); if (n 0) break; response.append(buf, n); // 用 append 保留 \0 }逻辑说明HTTP/1.1 默认 Keep-Alive如果不发 Connection: close服务器不会关闭连接你的 recv 循环会一直等。加了这个头响应收完后服务端会正常关闭recv 返回 0循环退出。参数说明buf 是 char 数组如果直接用response buf会遇到字符串截断问题因为 recv 收到的字节里可能有 \0所以必须用 append(buf, n) 指定长度。解析响应时先用\r\n\r\n找到头部结束位置再按 Content-Length 读 body这是 HTTP 协议里最常见的边界处理。4.2 epoll 的 LT 与 ET为什么生产环境我优先选 LT多线程模型撑到几百连接后线程切换会成为瓶颈。实例到中段一定会引入 epoll。epoll 对 fd 有两种触发模式水平触发 LT 和边缘触发 ET。我直接给建议如果实例代码没刻意讲 ET一律用 LT。对比项LT 水平触发ET 边缘触发可读通知缓冲区有数据就会通知只有新数据到达时通知一次是否必须非阻塞可以阻塞必须非阻塞读取要求能读多少读多少必须一次读完实现难度低高容易漏读适用场景绝大多数业务追求极致吞吐的大文件传输原因ET 场景下如果一次 recv 没把数据读完要等到下一次新数据到达才会再次通知这中间数据一直留在内核缓冲区。所以 ET 要求循环读直到 EAGAIN而且 fd 必须是非阻塞否则最后一次读会卡住线程。LT 模式只要缓冲区还有数据就会一直通知虽然多一次 epoll_wait 返回但逻辑简单得多。实例里如果告诉你设置 EPOLLET通常是配合while (recv() 0)的循环别照抄。4.3 把事件循环封装成回调可复用的 reactor 框架雏形到这一步可以把 epoll 逻辑从业务里抽出来做成一个简单的 reactor注册 fd 和回调函数事件循环负责分发。这个结构在后续接数据库、接消息队列时能直接复用。class Reactor { public: void add_handler(int fd, std::functionvoid(int) handler) { epoll_event ev; ev.events EPOLLIN; ev.data.fd fd; epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, ev); handlers_[fd] std::move(handler); } void loop() { epoll_event events[64]; while (true) { int n epoll_wait(epfd_, events, 64, -1); for (int i 0; i n; i) { int fd events[i].data.fd; handlers_[fd](fd); // 回调业务逻辑 } } } private: int epfd_ epoll_create(1); std::mapint, std::functionvoid(int) handlers_; };逻辑说明add_handler 把 fd 挂到 epoll 并保存回调事件循环里拿到事件的 fd 后直接调用对应的函数。参数说明epoll_wait 的第三个参数 64 是单次返回的最大事件数不是 epoll 能监控的上限-1 表示永久等待如果做定时任务可以改成超时毫秒数。要注意回调里如果处理耗时太长会阻塞整个循环所以业务重的场景要在回调里再投递到线程池。5. C 网络编程避坑手册5 个最容易翻车的地方5.1 SIGPIPE 导致进程直接退出写已关闭连接时的信号默认行为现象客户端主动断开后服务端继续 send()进程直接退出看不到任何报错。原因Linux 下写一个对端已关闭的 socket内核会发送 SIGPIPE 信号默认行为是终止进程。很多新手以为返回值会是 -1但信号优先于返回值处理。解决在服务端启动时调用signal(SIGPIPE, SIG_IGN)忽略它或者在 send 时加 MSG_NOSIGNAL 标志。我一般两种都加因为线上不能赌每次 send 都记得带标志。设置之后send 返回 -1errno 为 EPIPE你就能正常处理这个错误并清理连接。5.2 recv() 的缓冲区不是字符串长度、截断、\0 的处理现象收到的数据用printf(%s, buf)打印内容不完整或者后面跟乱码。原因recv 读到的字节流不保证以 \0 结尾而你当成了 C 字符串printf 会一直读到内存里的下一个 \0 为止越界了也不知道。解决严格按返回值 n 处理printf(%.*s, n, buf)或者把 buf 构造成 std::stringstd::string data(buf, n)。记住一点网络字节流里可能有 \0它是数据的一部分而不是字符串终止符。我在代码评审里看到用strlen(buf)取长度就一定会打回。5.3 粘包半包为什么你收到的消息多一块或少一块现象客户端连续发送两条消息服务端 recv 一次收到两条或者一条消息要 recv 两次才完整。原因TCP 是字节流内核把数据按发送顺序拼在一起应用层看不到「消息」边界。解决像 2.3 节那样自定义协议头先读 4 字节长度字段再按长度读满整个消息体。这里有个小陷阱4 字节长度字段本身也可能被拆成几次 recv所以你不仅要 readn 消息体还要 readn 协议头。别假设一次 recv 就能拿到完整头部。5.4 多线程共享 fd 与误关闭close 与 shutdown 的真实区别现象在多线程服务端一个线程在 recv另一个线程 close 了同一个 fd导致连接异常断开或者 double close 崩溃。原因close 是释放 fd如果两个线程都持有这个数字第一个 close 后内核可能把这个数字分配给新连接第二个 close 就把新连接误关了。解决约定 fd 的所有权属于某一个线程单线程负责收发。需要终止对端时用 shutdown(fd, SHUT_RDWR) 而不是 closeshutdown 不会释放 fd只是禁止收发之后由持有者 close。检查你的实例代码凡是 close 出现在两个线程里都是定时炸弹。5.5 Windows 下 VSCode 配置 C 环境链接 ws2_32.lib 的坑现象在 Windows 上用 VSCode 编译网络程序代码没问题链接报undefined reference to WSAStartup或socket。原因Windows 的 socket API 在 ws2_32.dll 里编译器默认不会自动链接这个库而且使用前需要先调用 WSAStartup 初始化。解决在 tasks.json 的 args 里加-lws2_32或者直接用 MSVC 的 cl 工具加ws2_32.lib。代码开头别忘了#ifdef _WIN32 WSADATA wsData; WSAStartup(MAKEWORD(2, 2), wsData); #endif参数说明MAKEWORD(2,2) 表示请求使用 Winsock 2.2 版本。很多 c入门 教程只写 Linux到了 Windows 就会卡在这一步。如果你是在 VSCode 里配 C/C 环境MinGW 和 MSVC 都要确认链接库参数否则 socket 函数全部报未定义。6. 把实例升级成工程日志、优雅退出与压测验证6.1 用 spdlog 替换 printf实例里打日志用 printf 没问题但工程上我习惯用 spdlog。它按天或按大小滚动文件还能分级别过滤排查线上问题时能少敲很多 grep。基本用法是spdlog::info(client fd {} connected, fd)注意格式化占位符不要写错写错类型会编译不过。6.2 优雅退出捕获 SIGINT 与线程池停机顺序CtrlC 直接退出会丢未落盘的日志和未处理的连接。我会用一个全局 atomic 标志在 signal handler 里置为 false主循环检测到后先停止接受新连接再等正在处理请求的线程结束最后关闭日志。顺序不能反先关日志会让排错信息全丢。6.3 压测验证从 nc 到自写并发客户端验证服务端能不能扛住先用nc -vz 127.0.0.1 9000测端口再写一个多线程客户端每个线程建 100 个连接同时收发。最简单的方式是统计每秒完成回显的请求数。如果发现 qps 上不去先看 CPU 占用再查是不是有线程在锁里等待。这套验证方法比看实例里的图更实际我每次改完连接池参数都靠它来兜底。最后说一句我的习惯任何网络程序上线前都先让它在测试环境跑 20 分钟用脚本模拟断连、半包、超时这些故障跑不挂再考虑发布。这个习惯帮我挡住了十几次线上事故希望对你也一样有用。本文还有配套的精品资源点击获取

相关新闻

AI运维系统落地实践:大模型在32G内存服务器上的闭环设计

AI运维系统落地实践:大模型在32G内存服务器上的闭环设计

简介:本资源是一份面向制造业数字化转型从业者、IT运维架构师及AI赋能工业场景实践者的专业方案PPT,聚焦解决系统孤岛、运维响应滞后、故障定位低效与知识复用困难等核心痛点。文件为单个1.12MB的PPT课件,完整呈现AI大模型驱动运维监控平台的…

2026/10/5 3:05:50 阅读更多 →
零极点追踪与附加零点设计:提升控制系统自适应整定能力的工程实践

零极点追踪与附加零点设计:提升控制系统自适应整定能力的工程实践

做过控制工程现场调试的朋友,应该都碰见过这种糟心事:一套系统在实验室里调得好好的,电流环、速度环参数都背得滚瓜烂熟,结果一换工况,或者机器跑热了,响应就完全变样。我之前在一套直流电机驱动系统上就遇…

2026/10/5 3:05:50 阅读更多 →
深入理解多进程服务器中accept()返回EINTR的机制与处理方案

深入理解多进程服务器中accept()返回EINTR的机制与处理方案

在写多进程 TCP 服务器的时候,accept()返回EINTR恐怕是新手和老手都绕不过去的一个老熟人。很多人第一次遇到它时,一脸茫然:明明监听 socket 一切正常,客户端也确实发来了连接,为什么accept()就是不肯把连接交给我&…

2026/10/5 3:04:50 阅读更多 →

最新新闻

吃豆人AI实战:Minimax、Alpha-Beta剪枝与Expectimax完整解析

吃豆人AI实战:Minimax、Alpha-Beta剪枝与Expectimax完整解析

如果你刷过伯克利CS61B,或者看过AI入门视频,大概率见过那只黄色吃豆人在迷宫里被鬼追得满地图跑的画面。那个场景十有八九就来自CS188的Project 2: Multi-Agents。这个项目是所有CS188课程作业里最有“游戏感”的一个,任务很直接——亲手写出…

2026/10/5 3:52:15 阅读更多 →
构建真正开放的跨平台Shell工作流

构建真正开放的跨平台Shell工作流

1. OpenShell:一个被严重误读的开源项目名称,以及它真实的技术定位OpenShell 这个名字一出来,很多人第一反应是“Windows 的替代开始菜单”——没错,确实存在一个叫 Open-Shell 的经典开源项目,它基于已停更的 Classic…

2026/10/5 3:52:15 阅读更多 →
C/C++源字符集与执行字符集:乱码根源与配置指南

C/C++源字符集与执行字符集:乱码根源与配置指南

如果你写过C/C程序,大概率遇到过这种事:代码在编辑器里显示得清清楚楚,注释里的中文也一切正常,可一旦编译运行,printf打印出来的中文字符串就变成了一堆“鏂囧瓧”之类的天书。还有更诡异的,同一份源码在L…

2026/10/5 3:52:15 阅读更多 →
插件原理与排障指南:从加载失败到开发实践

插件原理与排障指南:从加载失败到开发实践

做软件这些年,我发现自己经常要在一个单词上跟别人反复解释:plugins。它不是某个产品的功能,而是一整套架构思想加工程实践。最近看到一堆相关热搜,比如“iar plugins 是干什么的”、“failed to load plugins web boot: 2 entrie…

2026/10/5 3:52:15 阅读更多 →
Petalinux工程骨架详解:从XSA到BOOT.BIN的嵌入式Linux构建

Petalinux工程骨架详解:从XSA到BOOT.BIN的嵌入式Linux构建

1. 先把 petalinux 工程骨架这块拼图摆正如果你刚接触 Zynq 这类带 FPGA 的嵌入式平台,想用 petalinux 给板卡做一套 Linux 系统,第一反应大概率是找一份教程,敲几条命令,生成 BOOT.BIN,烧进 SD 卡,完事。我…

2026/10/5 3:52:14 阅读更多 →
Java仓库管理系统课设拆解:JDBC+MySQL+Swing实战开发

Java仓库管理系统课设拆解:JDBC+MySQL+Swing实战开发

简介:基于Java的仓库管理系统项目,是一份面向计算机相关专业学生和Java Web开发者的毕业设计完整参考。项目运用Spring框架、MyBatis持久层、Servlet与JSP等主流技术,实现了用户注册登录、商品信息维护、库存出入管理、价格设置等核心业务&am…

2026/10/5 3:51:14 阅读更多 →

日新闻

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:22 阅读更多 →
AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

1. 从“plugins”这个词说起:它到底在解决什么问题如果你最近在折腾 AI 编程工具,尤其是 Cursor、Codex CLI、Claude Code 这类带 CLI 的编辑器或命令行助手,那你大概率绕不开一个词——plugins。这个词本身不新鲜,从浏览器到 IDE…

2026/10/5 0:00:23 阅读更多 →
第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:23 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:10:22 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 3:06:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →