TCP服务端与客户端异步编程实战:epoll、asyncio与避坑指南
简介这份资源聚焦TCP协议下的异步通信实现面向具备一定网络编程基础、希望深入理解高并发网络应用开发的开发者与学习者。包内包含TCP服务端与TCP客户端两套异步通信示例代码可用于对照学习异步I/O模型、回调函数、事件循环、线程池等关键技术点帮助理解连接建立、数据收发等环节在非阻塞模式下的处理流程。资源共2个文件均为cpp源文件压缩包约2KB分别对应服务端与客户端的实现逻辑便于直接编译调试与逐行分析。目前已有189人学习下载适合作为网络编程入门到进阶的实践参考通过运行与修改代码可直观掌握异步TCP通信的工作机制与常见排错思路为构建高性能、高并发网络应用打下基础。1. 从 tc.zip 说起TCP 服务端与客户端异步到底解决什么问题很多人第一次看到tc.zip_TCP 服务端_TCP客户端异步_tcp 异步这个标题脑子里冒出来的是一堆问号TCP 服务端和客户端异步到底是一回事还是两回事我直接说结论——它讲的是一套用异步 I/O 模型同时撑起 TCP 服务端和 TCP 客户端的通信骨架核心矛盾只有一个怎么让一个线程或极少线程扛住成百上千条连接而不是一条连接开一个线程。传统阻塞式写法里accept一个连接就pthread_create一个线程几百个连接就是几百个线程上下文切换和内存开销直接把你拖垮。异步模型把「等待数据」这件事从线程身上剥离出来交给事件循环去管线程只在数据真正就绪时才干活。这套东西适合谁做即时通讯、物联网设备接入、游戏网关、内部 RPC 框架的工程师尤其是那些连接数上去了但单连接流量不大的场景。下面我按「原理选型 → 服务端落地 → 客户端落地 → 避坑 → 进阶验证」的顺序把这条链路拆开讲透。2. 异步 TCP 的模型选型epoll、IOCP 和 asyncio 到底怎么挑2.1 先搞清楚「异步」在 TCP 里指什么TCP 本身是面向连接的可靠字节流协议它不关心你用阻塞还是非阻塞去读写。所谓「TCP 异步」指的是应用层用非阻塞 socket 配合 I/O 多路复用或完成端口来管理连接而不是让线程卡在recv上死等。这里有两个容易混淆的概念I/O 多路复用select/poll/epoll/kqueue和真正的异步 I/OWindows IOCP、Linux io_uring。前者是「告诉你哪个 fd 就绪了你自己去读」后者是「你提交读请求读完内核通知你」。绝大多数所谓「TCP 异步框架」用的是前者因为跨平台成熟、生态好。选型上我一般这么分模型适用平台连接量级编程复杂度典型代表select跨平台几百低教学demoepoll (LT)Linux几千到几万中大多数服务端epoll (ET)Linux几万以上高高性能网关IOCPWindows几万中高Windows服务asyncio跨平台几千到几万低Python服务新手我建议从 epoll 水平触发LT或 Python asyncio 入手别一上来就搞边缘触发ETET 模式下漏读一次数据就卡死连接血泪经验。2.2 服务端和客户端的异步不是对称的很多人以为服务端异步和客户端异步写法一样其实侧重点完全不同。服务端的核心是「一个循环管所有连接」监听 fd 注册到 epoll新连接进来后把 connfd 也注册进去事件循环里统一分发读写。客户端的核心是「一个循环管我要发的请求和要收的响应」你可能有几十个目标地址每个地址一条连接异步客户端要能同时发起、按序或乱序收结果。服务端怕的是连接管理混乱客户端怕的是请求响应错配。所以下面我分两章写服务端讲连接生命周期管理客户端讲请求-响应配对。2.3 一个最小可跑的 epoll 服务端骨架先看代码再解释。这是 Linux 下 C 语言的 epoll LT 模式服务端核心#include sys/epoll.h #include sys/socket.h #include netinet/in.h #include unistd.h #include fcntl.h #include stdio.h #include string.h #define MAX_EVENTS 1024 #define PORT 9000 // 设置 fd 非阻塞异步模型的前提 int set_nonblocking(int fd) { int flags fcntl(fd, F_GETFL, 0); return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } int main() { int listen_fd socket(AF_INET, SOCK_STREAM, 0); set_nonblocking(listen_fd); struct sockaddr_in addr; memset(addr, 0, sizeof(addr)); addr.sin_family AF_INET; addr.sin_addr.s_addr INADDR_ANY; addr.sin_port htons(PORT); bind(listen_fd, (struct sockaddr*)addr, sizeof(addr)); listen(listen_fd, 128); int epfd epoll_create1(0); struct epoll_event ev, events[MAX_EVENTS]; ev.events EPOLLIN; // LT 模式不写 EPOLLET ev.data.fd listen_fd; epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, ev); while (1) { int n epoll_wait(epfd, events, MAX_EVENTS, -1); for (int i 0; i n; i) { int fd events[i].data.fd; if (fd listen_fd) { // 新连接accept 后立刻设为非阻塞并注册 int connfd accept(listen_fd, NULL, NULL); set_nonblocking(connfd); ev.events EPOLLIN; ev.data.fd connfd; epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, ev); } else { char buf[4096]; int len read(fd, buf, sizeof(buf)); if (len 0) { // 对端关闭或出错摘除并关闭 epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); close(fd); } else { // 业务处理这里简单回显 write(fd, buf, len); } } } } return 0; }逻辑说明epoll_create1创建事件表epoll_ctl注册监听 fdepoll_wait阻塞等待事件。新连接到来时accept拿到 connfd必须立刻设为非阻塞否则后续read可能阻塞整个事件循环。读返回 0 表示对端关闭返回 -1 且 errno 为 EAGAIN 表示暂时无数据LT 模式下一般不会走到。参数上MAX_EVENTS决定单次epoll_wait最多返回多少事件设太小会频繁唤醒设太大浪费内存1024 是常见起点。listen的 backlog 设 128 到 1024取决于你的并发建连速度。提示LT 模式下如果一次没读完数据下次epoll_wait还会通知你所以不容易丢数据ET 模式必须循环读到 EAGAIN否则事件不再触发。3. TCP 服务端异步落地连接生命周期与缓冲区管理3.1 连接对象要自己管别只存一个 fd上面那个骨架只能跑 demo真实服务端必须为每条连接维护一个结构体至少包含fd、读缓冲区、写缓冲区、连接状态、最后活跃时间。为什么需要写缓冲区因为异步write不一定一次把数据全发出去内核发送缓冲区满了会返回 EAGAIN剩下的数据你得自己缓存等 fd 可写时再发。这就是「异步写」的坑你调了 write 不代表数据发出去了。typedef struct { int fd; char rbuf[8192]; size_t rlen; char *wbuf; // 动态写缓冲 size_t wlen; size_t wpos; time_t last_active; } conn_t;参数说明rbuf固定 8KB 做读缓冲够大多数协议头解析wbuf用动态分配因为待发数据长度不可控last_active用于空闲连接清理一般 60 到 300 秒没数据就踢掉。连接数上万时每个连接固定 8KB 读缓冲就是 80MB还能接受但如果设成 64KB 就是 640MB内存直接爆炸所以读缓冲别贪大。3.2 事件循环里读写要分开处理注册事件时默认只监听EPOLLIN。当你有数据要发且一次没发完时动态加上EPOLLOUT发完后立刻改回只监听EPOLLIN。原因是如果一直监听 EPOLLOUT只要内核发送缓冲区有空位就会不断触发可写事件CPU 空转。这是新手最常见的性能翻车点。// 需要发送数据时 ev.events EPOLLIN | EPOLLOUT; ev.data.fd conn-fd; epoll_ctl(epfd, EPOLL_CTL_MOD, conn-fd, ev); // 在事件循环中处理可写 if (events[i].events EPOLLOUT) { int n write(conn-fd, conn-wbuf conn-wpos, conn-wlen - conn-wpos); if (n 0) { conn-wpos n; if (conn-wpos conn-wlen) { // 发完了摘掉 EPOLLOUT ev.events EPOLLIN; epoll_ctl(epfd, EPOLL_CTL_MOD, conn-fd, ev); conn-wpos conn-wlen 0; } } }逻辑上wpos记录已发送位置每次可写事件推进全部发完才取消 EPOLLOUT 监听。参数上write的第三个参数是剩余待发字节数不要传整个 wlen否则会重复发送已发部分。3.3 用 Python asyncio 快速验证服务端逻辑如果你不想一上来写 C用 Python asyncio 可以在几十行内验证异步服务端的行为适合先跑通协议再移植import asyncio async def handle(reader, writer): addr writer.get_extra_info(peername) print(f新连接: {addr}) try: while True: data await reader.read(4096) # 异步读不阻塞事件循环 if not data: break writer.write(data) # 写入缓冲区 await writer.drain() # 背压控制缓冲满时挂起 except ConnectionResetError: pass finally: writer.close() await writer.wait_closed() async def main(): server await asyncio.start_server(handle, 0.0.0.0, 9000) async with server: await server.serve_forever() asyncio.run(main())逻辑说明reader.read是协程没数据时让出控制权给事件循环writer.drain()是关键它实现背压——当写缓冲超过高水位时挂起当前协程防止内存无限增长。参数上read(4096)的 4096 是单次最多读多少字节start_server的 backlog 默认 100高并发场景要调大。这套代码在 Linux 上底层就是 epoll在 Windows 上是 IOCP跨平台不用改。注意asyncio 单线程跑满一个核后不会自动多核扩展生产环境要配合多进程SO_REUSEPORT或多个 worker才能吃满多核。4. TCP 客户端异步请求响应配对与超时控制4.1 异步客户端最难的是「谁是谁的响应」服务端处理连接相对独立客户端不一样你可能同时向 10 个后端发请求每个请求的响应长度和到达顺序都不确定。如果只是简单send然后recv响应会错配。常见做法是给每个请求分配一个序列号seq响应里带回 seq客户端用字典维护 seq 到回调/future 的映射。这是所有异步 RPC 框架的基本功。import asyncio class AsyncTCPClient: def __init__(self, host, port): self.host host self.port port self.seq 0 self.pending {} # seq - Future self.reader None self.writer None async def connect(self): self.reader, self.writer await asyncio.open_connection(self.host, self.port) asyncio.create_task(self._recv_loop()) # 后台收包循环 async def _recv_loop(self): while True: header await self.reader.readexactly(4) # 假设4字节长度头 length int.from_bytes(header, big) body await self.reader.readexactly(length) seq int.from_bytes(body[:4], big) fut self.pending.pop(seq, None) if fut and not fut.done(): fut.set_result(body[4:]) async def request(self, payload, timeout3.0): self.seq 1 seq self.seq fut asyncio.get_event_loop().create_future() self.pending[seq] fut msg seq.to_bytes(4, big) payload self.writer.write(len(msg).to_bytes(4, big) msg) await self.writer.drain() try: return await asyncio.wait_for(fut, timeout) # 超时控制 except asyncio.TimeoutError: self.pending.pop(seq, None) raise逻辑说明_recv_loop是独立后台任务按「长度头 包体」拆包从包体里取 seq 找到对应的 Future 并设置结果。request方法发完请求后await这个 Future谁先回来谁先完成。参数上timeout3.0是单请求超时超时后必须从pending里删掉否则内存泄漏长度头用 4 字节大端支持单包最大 4GB实际业务一般限制在几 MB。4.2 超时、重连和心跳一个都不能少异步客户端三个必备机制超时上面已做、重连连接断了要自动重连并重发未完成请求或报错、心跳长时间无数据时发 ping 保活防止中间设备静默断链。重连我一般用指数退避第一次 1 秒第二次 2 秒最多 30 秒避免服务端刚重启就被客户端打爆。async def reconnect_loop(self): backoff 1 while True: try: await self.connect() backoff 1 # 连上后重置退避 await self.writer.wait_closed() # 阻塞直到断开 except Exception: await asyncio.sleep(backoff) backoff min(backoff * 2, 30) # 指数退避上限30秒参数说明backoff初始 1 秒每次翻倍封顶 30 秒。wait_closed让协程挂起直到连接断开断开后自动进入下一轮重连。注意重连成功后pending里的旧请求要全部置为异常否则调用方永远等不到结果。4.3 客户端并发压测怎么跑写完客户端别急着上生产先用它自己压自己。用 asyncio 的gather并发发 1000 个请求看服务端和客户端表现async def bench(): client AsyncTCPClient(127.0.0.1, 9000) await client.connect() tasks [client.request(bhello, timeout5.0) for _ in range(1000)] results await asyncio.gather(*tasks, return_exceptionsTrue) ok sum(1 for r in results if not isinstance(r, Exception)) print(f成功 {ok}/1000) asyncio.run(bench())逻辑上gather并发调度 1000 个协程return_exceptionsTrue让异常也作为结果返回方便统计成功率。参数上并发数从 100 开始逐步加到 1000、5000观察 QPS 和延迟拐点。如果成功率骤降先查服务端 backlog 和文件描述符上限ulimit -n再看客户端pending字典是否泄漏。5. 异步 TCP 避坑排查那些让你加班到凌晨的细节5.1 现象连接数一上去就报「Too many open files」原因Linux 默认单进程文件描述符上限是 1024每条 TCP 连接占一个 fd加上监听 fd、日志文件等1000 连接就顶到天花板。解决ulimit -n 65535临时生效永久生效改/etc/security/limits.conf加* soft nofile 65535和* hard nofile 65535重启会话后ulimit -n确认。注意 systemd 管理的服务还要在 unit 文件里加LimitNOFILE65535否则不生效。5.2 现象客户端发了数据服务端半天收不到原因TCP 默认开启 Nagle 算法小包会攒着一起发延迟最高 40ms。异步场景下如果每次只发几十字节这个延迟很致命。解决对延迟敏感的连接设置TCP_NODELAYint flag 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, flag, sizeof(flag));Python 里writer.get_extra_info(socket).setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)。代价是网络包数量增加带宽换延迟内网服务基本无脑开。5.3 现象epoll 边缘触发模式下连接突然不再收数据原因ET 模式要求每次事件触发必须循环read直到返回 EAGAIN如果只读一次剩余数据不会再次触发事件连接就「假死」了。解决ET 模式下读循环写成while ((n read(fd, buf, sizeof(buf))) 0) { 处理 }读到n -1 errno EAGAIN才退出。我一般建议新手先用 LT性能差距在大多数业务里没那么大等真扛不住了再换 ET。5.4 现象服务端内存持续增长最后 OOM原因写缓冲区只增不减或者pending字典里的请求超时后没清理。解决给写缓冲设上限比如 1MB超过就断开慢客户端超时请求必须在except里pop掉定期用len(pending)打点监控。另外读缓冲如果按连接固定分配且设得过大上万连接也会吃光内存按需分配或设小一点。5.5 现象压测时 QPS 上不去CPU 却不高原因多半是锁竞争或系统调用过多。异步单线程模型里如果用了线程锁或者每个事件都调epoll_ctl修改监听开销会很大。解决减少epoll_ctl调用能一次注册就一次注册避免在事件循环里做阻塞操作文件 IO、数据库同步查询用strace -c看系统调用分布perf top看热点函数。6. 进阶验证用 io_uring 和连接池把异步 TCP 再推一步前面讲的 epoll 本质还是「就绪通知」每次读写都要一次系统调用。Linux 5.1 之后有了 io_uring可以批量提交读写请求减少系统调用次数在高连接高吞吐场景下比 epoll 有明显优势。我一般用liburing做验证核心思路是把 accept、read、write 都封装成 SQE 提交到环形队列内核完成后通过 CQE 通知。不过 io_uring 生态还在成熟中生产环境要评估内核版本和运维成本别为了追新把稳定性搭进去。另一个更实用的进阶方向是连接池。异步客户端如果每次请求都新建连接三次握手开销会吃掉大量性能。做法是维护一组长连接请求时从池里取空闲连接用完归还。池大小按「目标 QPS × 平均请求耗时」估算比如 10000 QPS、单请求 5ms理论上 50 个连接就够实际留 2 倍余量设 100。配合前面讲的 seq 配对一个连接上可以并发多个请求吞吐还能再上一个台阶。验证异步 TCP 方案是否达标我习惯看三个指标单连接吞吐用iperf3打底、并发连接下的 P99 延迟用wrk或自写压测客户端、CPU 和内存随连接数的增长曲线。如果连接数翻倍而 CPU 增长远低于线性说明异步模型生效了如果内存线性暴涨回去查缓冲区分配。最后说个我自己的习惯每次写完异步 TCP 代码先不接业务用tcpdump抓包确认收发字节数和预期一致再上压测。这个笨办法帮我省过至少三次「代码逻辑对但线上就是不通」的排查时间。异步 TCP 这东西原理不复杂坑全在细节里把连接生命周期、缓冲区、超时这三样管住基本就稳了。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

《创业之路》-988-AI 重塑商业、经济、社会的二十本经典著作

《创业之路》-988-AI 重塑商业、经济、社会的二十本经典著作

AI 重塑商业、经济、社会的二十本经典著作导读:20 本书划分为四大板块:商业管理(7 本)、经济学视角(5 本)、社会文明与风险治理(5 本)、本土中国视角(3 本)。…

2026/10/4 9:03:12 阅读更多 →
MySQL--批量插入一百万条数据

MySQL--批量插入一百万条数据

原文网址:MySQL--批量插入一百万条数据-CSDN博客 简介 本文介绍向MySQL批量插入一百万条数据的方法。 表结构: CREATE TABLE t_goods (id bigint NOT NULL COMMENT 主键,name varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 商品名字,descri…

2026/10/4 9:03:12 阅读更多 →
“魔兽世界”C++大作业:面向对象类设计与多态实战解析

“魔兽世界”C++大作业:面向对象类设计与多态实战解析

拿到这个题目的时候,我第一反应是挺感慨的——“魔兽世界(三):开战”几乎算得上C面向对象程序设计课程的“期末保留节目”了。作为一个每年都能在论坛和课程群里看到无数版本的老题目,它最大的价值不在于“魔兽世界”四…

2026/10/4 9:02:11 阅读更多 →

最新新闻

企业级智能学习平台:SpringBoot+Vue+MyBatis+MySQL全栈实战

企业级智能学习平台:SpringBoot+Vue+MyBatis+MySQL全栈实战

企业级智能学习平台管理系统,光看标题,就是一套典型的 SpringBoot Vue MyBatis MySQL 组合的 Java 全栈项目。这几个月我在公司内部牵头做技术培训平台升级,正好就是基于这套技术栈从零搭的,所以看到这个标题格外有感触。市面上…

2026/10/4 9:33:27 阅读更多 →
STM32 HAL库驱动42步进电机:从PWM配置到加减速排障

STM32 HAL库驱动42步进电机:从PWM配置到加减速排障

42步进电机大概是创客和工控场景里最没存在感的设备。一片几十块的DRV8825驱动板、两根信号线,网上教程铺天盖地——“接上就转,给脉冲就走”。可真把它塞进XY平台、转台或者贴标机里,事情就没那么浪漫了:低速时机身嗡嗡共振&…

2026/10/4 9:33:27 阅读更多 →
SQL10过滤年龄空值:为什么age不等于空字符串,不能代替IS NOT NULL

SQL10过滤年龄空值:为什么age不等于空字符串,不能代替IS NOT NULL

这题要剔除没有获取到年龄的用户,再输出设备 ID、性别、年龄、学校。我原来的代码用了 age ! ,牛客样例也通过了。但回头看,这个条件没有准确表达题意。 关键区别是:数据库里的 NULL,不是屏幕上看起来空白的字符串&am…

2026/10/4 9:33:27 阅读更多 →
MySQL学生成绩管理系统:从建库建表到索引优化全实践

MySQL学生成绩管理系统:从建库建表到索引优化全实践

做项目的人对“管理系统”三个字应该都不陌生,学生成绩管理系统(MySQL)更是课程设计、毕业设计里的常客。我这次做的这套系统,表面上看就是记录“哪个学生哪门课考了多少分”,但真正做下来你会发现,它几乎能…

2026/10/4 9:33:27 阅读更多 →
Win11局域网共享:Guest账户开启、排查与安全设置

Win11局域网共享:Guest账户开启、排查与安全设置

1. 局域网共享方式盘点:guest 登录到底适合哪种场景前几天有个朋友问我:Win11 怎么开 guest 账户,让局域网里那几台旧电脑直接访问自己机器上的文件夹?这个问题在 Windows 11 时代听着有点复古,但在小办公室、宿舍、家…

2026/10/4 9:33:27 阅读更多 →
GNSS接收机抗干扰算法仿真:脉冲与窄带干扰抑制Matlab实现

GNSS接收机抗干扰算法仿真:脉冲与窄带干扰抑制Matlab实现

导航接收机最怕的就是射频前端被强干扰"打蒙"。不管是GPS还是北斗,民用接收机在天线口收到的信号本来就被压在噪声底底下20多dB,一旦出现脉冲干扰或者窄带干扰,接收机内部的自动增益控制(AGC)会被强干扰拉偏…

2026/10/4 9:32:26 阅读更多 →

日新闻

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/4 1:00:58 阅读更多 →
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/4 1:00:58 阅读更多 →

周新闻

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/4 1:00:58 阅读更多 →
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/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/2 10:36:31 阅读更多 →
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/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练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/3 9:42:36 阅读更多 →