<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://xutopia.top/</id>
  <title type="text">xutopia77</title>
  <subtitle type="text">见字如面</subtitle>
  <updated>2026-06-10T00:00:00.000Z</updated>
  <author><name>xutopia77</name></author>
  <link rel="alternate" href="https://xutopia.top/"/>
  <link rel="self" href="https://xutopia.top/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.9</generator>
    <entry>
      <id>https://xutopia.top/blog/ai-skills-to-industry-break/</id>
      <title type="text">从“知”到“行”：AI技能革命与传统行业的破局之路</title>
      <published>2026-06-10T00:00:00.000Z</published>
      <updated>2026-06-10T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/ai-skills-to-industry-break/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<img src="https://imgbed.xutopia.top/image-assets/fa40e3821f912e0743791be2a94c8662.jpg" width="50%" />
# 从“知”到“行”：AI技能革命与传统行业的破局之路
当我们谈论AI时，曾经的焦点总是停留在“它知道多少”——能否背完人类所有的书籍、能否回答任何常识问题、能否生成流畅的文本。但2025年的AI产业正在经历一场静水流深的范式转移：**AI的核心竞争力已从“知识储备量”转向“技能熟练度”**。它不再满足于做一个无所不知的“百科全书”，而是开始沉下心来，像一个学徒一样，钻研各行各业最核心、最隐秘的实操手艺。这场从“知”到“行”的跨越，不仅重新定义了AI的能力边界，更将彻底重塑传统行业的生存逻辑。
<section><h2>一、AI学习重心的根本性转向：从显性知识到隐性手艺<a href="#一ai学习重心的根本性转向从显性知识到隐性手艺"><span>#</span></a></h2><p>人工智能的发展历程，本质上是一部不断拓展学习边界的历史。早期的大模型依托互联网上海量的文本数据，完成了对人类显性知识的全面吸收。它能精准复述理论定义、背诵历史事件、解释科学原理，甚至能写出逻辑严谨的论文。但这种“知道”始终停留在“是什么”和“为什么”的层面，一旦触及“怎么做”的实操环节，就常常显得力不从心。</p><p>而如今的智能体，正在攻克人类知识体系中最难以被编码的部分——<strong>隐性手艺</strong>。这是一种只可意会不可言传的经验，是行业专家经过成千上万次实践沉淀下来的直觉、技巧和流程。它不是写在教科书上的公式，而是建筑设计师调整外立面弧度时对光影的微妙把握；不是三维建模软件的操作手册，而是拓扑布线时为了后续渲染更流畅而做出的取舍；不是财务报表的模板，而是老会计在核对账目时一眼就能发现异常的敏锐。</p><p>今天的AI，正在系统性地学习这些“过程性知识”。在建筑设计领域，它不再只是生成一张好看的效果图，而是能完整走完从概念方案、平面布局、结构计算到施工图绘制的全流程，甚至能模拟不同施工方案的成本和工期；在视觉创作领域，它不再只是根据文字生成图片，而是能熟练操控Photoshop、Blender等专业软件，精准调整图层、参数、材质和光影，还原设计师反复修改的打磨过程；在财务办公领域，它能自动完成发票识别、凭证录入、对账结账到税务申报的完整闭环，还能根据历史数据预判财务风险。</p><p>这种学习重心的转变，标志着AI真正从“辅助工具”进化为“执行主体”。过去，AI只能帮人类“省脑子”；现在，它开始帮人类“省手脚”。它不再需要人类把每一个步骤拆解成指令，而是能自己理解任务目标，自主规划执行路径，甚至能在遇到问题时主动调整策略。</p></section>
<section><h2>二、技术落地的铁证：当AI学会操控专业软件<a href="#二技术落地的铁证当ai学会操控专业软件"><span>#</span></a></h2><p>这场技能革命不是纸上谈兵，而是已经在多个行业落地生根，产生了实实在在的生产力提升。其中最具标志性的现象，就是<strong>AI对专业软件的原生操控能力</strong>。</p><p>过去，AI与专业软件的结合，大多停留在“插件式”的辅助层面——比如在设计软件里加一个AI生成素材的按钮，在办公软件里加一个AI写文案的功能。但现在的智能体，已经能像人类员工一样，直接打开软件、点击菜单、拖动滑块、输入参数，完成一整套复杂的操作。</p><p>在三维建模领域，新一代AI智能体可以仅凭一段文字描述，就在Blender中自动完成从基础建模、拓扑优化、UV展开、材质贴图到灯光渲染的全流程。它不仅能生成符合要求的模型，还能输出可编辑的工程文件，保留所有的图层和参数，方便人类设计师后续修改。在建筑行业，AI已经能根据地块条件和设计要求，自动生成多套符合国家规范的建筑方案，并且能直接导出CAD施工图和BIM模型，大大缩短了设计周期。在工业设计领域，AI能操控SolidWorks等软件完成产品的结构设计和力学仿真，甚至能根据生产工艺的要求自动优化设计方案。</p><p>这些案例有力地证明了：<strong>当AI掌握了行业的实操方法和专业技法后，其工作能力会呈现出指数级的提升</strong>。它不仅能完成人类能做的工作，还能以更快的速度、更低的成本、更稳定的质量完成重复性的劳动。这使得人类专家可以从繁琐的执行性工作中解放出来，专注于更具创造性和战略性的任务。</p></section>
<section><h2>三、FDE：连接AI与传统行业的关键桥梁<a href="#三fde连接ai与传统行业的关键桥梁"><span>#</span></a></h2><p>AI技能革命的背后，有一个容易被忽视但至关重要的角色——<strong>前线部署工程师（Frontline Deployment Engineer，FDE）</strong>。他们是连接通用大模型与垂直行业的桥梁，是AI学会隐性手艺的“师傅”。</p><p>FDE的工作本质，是<strong>把行业专家的大脑“数字化”</strong>。他们深入传统行业的一线，与最资深的专家并肩工作，观察他们的操作流程，拆解他们的决策逻辑，提炼他们的经验技巧。然后，他们把这些模糊的、碎片化的隐性知识，转化为标准化的、可复用的技能模块——包括详细的工作流、关键的参数阈值、常见的问题解决方案以及异常情况的处理机制。这些技能模块会被用来微调大模型，或者构建成专属的智能体，让AI能够快速掌握一个行业的核心能力。</p><p>与传统的数据标注员不同，FDE需要同时具备AI技术素养和行业认知能力。他们不仅要懂大模型的原理和prompt工程，还要懂建筑设计的规范、三维建模的技巧、财务核算的规则。他们不是简单地把专家的操作记录下来，而是要对这些操作进行抽象和提炼，找出其中的共性和规律，让AI能够举一反三。</p><p>更重要的是，FDE的工作是一个持续迭代的过程。他们会收集AI在实际工作中遇到的问题，反馈给行业专家，然后不断优化技能模块，让AI的能力越来越强。这种“专家经验-FDE转化-AI落地-反馈优化”的闭环，是AI吃透垂直领域的核心动力。可以说，<strong>未来哪个行业拥有更多优秀的FDE，哪个行业的AI落地速度就会更快，效果就会更好</strong>。</p></section>
<section><h2>四、传统行业的破局之道：从固守壁垒到开放共生<a href="#四传统行业的破局之道从固守壁垒到开放共生"><span>#</span></a></h2><p>AI技能革命的到来，给传统行业带来了前所未有的挑战，也带来了千载难逢的机遇。很多传统企业的第一反应是筑起高墙，把自己的内部经验和流程封闭起来，试图以此阻挡AI的冲击。但这种做法无异于螳臂当车。</p><p>因为AI的发展速度远远超过了大多数人的想象。今天你引以为傲的核心经验，明天可能就会被AI学会并超越。固守数据壁垒，不仅无法阻挡AI的发展，反而会让自己与时代脱节。当你的竞争对手已经在用AI把效率提升几倍的时候，你还在用传统的方式慢慢打磨，最终只会被市场淘汰。</p><p>对于传统企业来说，更明智的做法是<strong>顺势而为，主动拥抱AI</strong>。你不必花费巨资去自研通用大模型——那是科技巨头的战场。你的核心竞争力，不在于拥有一个大模型，而在于拥有别人没有的行业经验和流程。你要做的，是把这些隐性的、分散在员工大脑里的经验，转化为可训练、可复用的过程资产。</p><p>具体来说，企业可以按照以下步骤进行转型：</p><ol>
<li><strong>梳理核心流程</strong>：对企业内部的所有业务流程进行全面梳理，找出哪些环节是重复性高、标准化程度高的，哪些环节是依赖专家经验的。</li>
<li><strong>提炼隐性知识</strong>：组织资深专家和FDE一起，把核心环节的操作步骤、决策逻辑、经验技巧提炼出来，形成标准化的技能库。</li>
<li><strong>对接AI能力</strong>：基于通用大模型，结合自己的技能库，打造专属的企业智能体，让AI成为每个员工的“数字助手”。</li>
<li><strong>优化组织架构</strong>：调整企业的组织架构和工作模式，让人类员工和AI智能体能够高效协作。人类负责创意、决策和沟通，AI负责执行、计算和数据处理。</li>
</ol><p>通过这种方式，企业不仅能大幅提升运营效率，降低成本，还能把自己的核心经验沉淀为企业的数字资产，实现可持续发展。</p></section>
<section><h2>结语：技能时代，人与AI的共生未来<a href="#结语技能时代人与ai的共生未来"><span>#</span></a></h2><p>从“知识型AI”到“技能型AI”，我们正在见证人工智能发展史上的一个重要里程碑。这场革命的最终目标，不是用AI取代人类，而是让AI成为人类能力的延伸。它将把人类从繁琐的体力劳动和重复性的脑力劳动中解放出来，让我们有更多的时间和精力去追求更有价值的事情——创造、探索、关爱。</p><p>对于传统行业来说，这是一个最好的时代，也是一个最坏的时代。那些能够主动拥抱变化，把自己的经验转化为AI能力的企业，将迎来指数级的增长；而那些固守陈规，拒绝改变的企业，终将被时代的浪潮所淹没。未来的竞争，不再是人与人之间的竞争，而是“人+AI”与“人+AI”之间的竞争。谁能更好地驾驭AI的技能，谁就能在未来的市场中占据先机。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/ai-era-software-development-paradigm/</id>
      <title type="text">AI新时代软件开发范式：从传统研发到LLM原生工程体系的革命性变革</title>
      <published>2026-04-20T00:00:00.000Z</published>
      <updated>2026-04-20T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/ai-era-software-development-paradigm/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🚀 AI新时代软件开发范式：从传统研发到LLM原生工程体系的革命性变革<a href="#-ai新时代软件开发范式从传统研发到llm原生工程体系的革命性变革"><span>#</span></a></h1><section><h2>📖 引言：AI时代的软件开发革命<a href="#-引言ai时代的软件开发革命"><span>#</span></a></h2><p>随着大语言模型（LLM）技术的飞速发展，软件开发行业正经历着一场前所未有的范式革命。传统的软件开发模式——从需求分析、设计、编码、测试到部署——正在被以AI为核心生产力的新一代研发范式所重塑。这场变革不仅仅是工具的升级，更是整个软件工程体系的重构。</p><p>面向<strong>LLM原生软件工程体系</strong>，我们重构了传统瀑布式研发的核心链路，形成了以AI为核心生产力的新一代研发范式。这不仅仅是效率的提升，更是软件开发思维的根本性转变。</p></section><section><h2>🎯 LLM原生软件工程体系的五大核心阶段<a href="#-llm原生软件工程体系的五大核心阶段"><span>#</span></a></h2><section><h3>1. 📋 需求锚定阶段：从模糊想法到精准定义<a href="#1--需求锚定阶段从模糊想法到精准定义"><span>#</span></a></h3><p><strong>⚠️ 传统模式痛点</strong>：需求文档往往模糊不清，开发过程中频繁变更，导致项目延期和质量问题。</p><p><strong>✨ AI时代解决方案</strong>：</p><ul>
<li>📄 产品端输出PRD（Product Requirements Document），完成业务价值与产品形态的顶层定义</li>
<li>🤖 AI辅助需求分析，通过自然语言处理技术识别需求中的模糊点和潜在冲突</li>
<li>📊 智能需求优先级排序，基于业务价值和实现难度自动推荐开发顺序</li>
<li>🔍 需求可行性预评估，AI快速判断技术实现难度和风险点</li>
</ul><p><strong>💡 核心价值</strong>：确保需求的清晰性、完整性和可实现性，为后续开发奠定坚实基础。</p></section><section><h3>2. 🔧 规格拆解阶段：从业务需求到技术规格<a href="#2--规格拆解阶段从业务需求到技术规格"><span>#</span></a></h3><p><strong>⚠️ 传统模式痛点</strong>：需求到设计的转化依赖个人经验，规格文档质量参差不齐，开发理解偏差大。</p><p><strong>✨ AI时代解决方案</strong>：</p><ul>
<li>📝 将非结构化业务需求转化为标准化FS（Functional Specification）功能规格文档</li>
<li>⚙️ 完成功能边界、输入输出、异常处理、验收标准的原子化定义</li>
<li>🎯 为LLM代码生成提供精准的Prompt工程底座</li>
<li>🏗️ AI自动生成接口定义、数据模型、业务流程图等技术规格</li>
<li>🚨 智能识别技术债务和潜在架构问题</li>
</ul><p><strong>💡 核心价值</strong>：建立标准化的技术规格体系，为AI代码生成提供高质量输入。</p></section><section><h3>3. 💻 代码生成阶段：从手工编码到智能生成<a href="#3--代码生成阶段从手工编码到智能生成"><span>#</span></a></h3><p><strong>⚠️ 传统模式痛点</strong>：编码效率低，重复劳动多，代码质量依赖个人能力，开发周期长。</p><p><strong>✨ AI时代解决方案</strong>：</p><ul>
<li>🚀 基于功能规格说明书，通过代码大模型实现端到端的业务代码自动化生成</li>
<li>🧪 自动生成单元测试用例，提升测试覆盖率</li>
<li>🔧 AI辅助代码优化，自动重构低质量代码</li>
<li>⚡ 智能代码补全和生成，大幅提升编码效率</li>
<li>🌐 支持多种编程语言和框架的代码生成</li>
</ul><p><strong>💡 核心价值</strong>：完成研发环节的核心生产力替代，实现编码环节的自动化和智能化。</p></section><section><h3>4. 🔍 智能评审阶段：从人工审查到AI增强<a href="#4--智能评审阶段从人工审查到ai增强"><span>#</span></a></h3><p><strong>⚠️ 传统模式痛点</strong>：代码评审依赖人工，效率低、覆盖不全，容易漏掉潜在问题。</p><p><strong>✨ AI时代解决方案</strong>：</p><ul>
<li>🤖 通过AI增强型代码评审体系，结合静态代码扫描、安全合规检测</li>
<li>✅ 逻辑一致性校验，确保代码实现与规格文档的一致性</li>
<li>🐛 自动识别代码异味、潜在bug、性能问题</li>
<li>💬 智能生成评审意见和改进建议</li>
<li>📋 完成全量代码的自动化复核，替代传统人工评审的核心流程</li>
</ul><p><strong>💡 核心价值</strong>：保障代码质量与可维护性，提升评审效率和覆盖度。</p></section><section><h3>5. 🚀 集成交付阶段：从手动部署到自动化流水线<a href="#5--集成交付阶段从手动部署到自动化流水线"><span>#</span></a></h3><p><strong>⚠️ 传统模式痛点</strong>：部署过程复杂，容易出错，回滚困难，交付周期长。</p><p><strong>✨ AI时代解决方案</strong>：</p><ul>
<li>🔄 完成评审的代码通过标准化CI/CD流水线</li>
<li>📦 自动合入代码仓库主干分支，纳入版本迭代体系</li>
<li>🧪 AI辅助测试用例生成和执行</li>
<li>📊 智能监控和告警，自动识别生产环境问题</li>
<li>✅ 完成从需求到代码的全链路闭环</li>
</ul><p><strong>💡 核心价值</strong>：实现持续集成和持续交付，缩短交付周期，提升部署可靠性。</p></section></section><section><h2>🌟 AI新时代开发范式的核心优势<a href="#-ai新时代开发范式的核心优势"><span>#</span></a></h2><section><h3>1. 📈 效率革命性提升<a href="#1--效率革命性提升"><span>#</span></a></h3><ul>
<li><strong>开发效率提升3-5倍</strong>：AI代码生成大幅减少手工编码时间</li>
<li><strong>需求到交付周期缩短60%以上</strong>：自动化流程减少等待和重复工作</li>
<li><strong>代码质量显著提升</strong>：AI评审发现更多潜在问题</li>
</ul></section><section><h3>2. 🛡️ 质量保障全面强化<a href="#2-️-质量保障全面强化"><span>#</span></a></h3><ul>
<li><strong>测试覆盖率提升</strong>：自动生成测试用例，覆盖更多边界情况</li>
<li><strong>代码一致性增强</strong>：标准化规格确保代码风格统一</li>
<li><strong>安全性提升</strong>：AI安全扫描提前发现安全漏洞</li>
</ul></section><section><h3>3. 👥 团队协作优化升级<a href="#3--团队协作优化升级"><span>#</span></a></h3><ul>
<li><strong>沟通成本降低</strong>：标准化文档减少理解偏差</li>
<li><strong>知识沉淀加速</strong>：AI辅助生成文档和注释</li>
<li><strong>新人上手更快</strong>：标准化流程降低学习曲线</li>
</ul></section><section><h3>4. 💡 创新能力显著增强<a href="#4--创新能力显著增强"><span>#</span></a></h3><ul>
<li><strong>快速原型验证</strong>：AI快速生成原型代码，加速创新迭代</li>
<li><strong>技术债务减少</strong>：AI自动识别和重构低质量代码</li>
<li><strong>架构优化建议</strong>：AI分析代码库，提供架构改进建议</li>
</ul></section></section><section><h2>📊 实际应用案例<a href="#-实际应用案例"><span>#</span></a></h2><section><h3>案例1：🛒 电商平台后台管理系统重构<a href="#案例1-电商平台后台管理系统重构"><span>#</span></a></h3><p><strong>传统模式</strong>：6个月开发周期，10人团队，多次延期</p><p><strong>AI新模式</strong>：</p><ul>
<li>📋 需求分析：AI辅助PRD生成，2天完成</li>
<li>🔧 规格拆解：自动生成FS文档，1周完成</li>
<li>💻 代码生成：AI生成80%核心代码，3周完成</li>
<li>🔍 智能评审：AI评审覆盖率100%，1周完成</li>
<li>🚀 集成交付：自动化CI/CD，持续交付</li>
</ul><p><strong>🎯 结果</strong>：2个月完成开发，团队规模缩减至4人，质量提升30%</p></section><section><h3>案例2：💰 金融风控系统开发<a href="#案例2-金融风控系统开发"><span>#</span></a></h3><p><strong>传统模式</strong>：复杂业务逻辑，开发难度大，测试周期长</p><p><strong>AI新模式</strong>：</p><ul>
<li>🧠 AI辅助复杂业务逻辑理解和规格定义</li>
<li>🛡️ 自动生成符合金融合规要求的代码</li>
<li>🔒 AI安全扫描确保代码安全性</li>
<li>🧪 自动生成测试用例，覆盖各种风控场景</li>
</ul><p><strong>🎯 结果</strong>：开发周期缩短50%，代码质量显著提升，合规性100%</p></section></section><section><h2>⚠️ 面临的挑战与应对策略<a href="#️-面临的挑战与应对策略"><span>#</span></a></h2><section><h3>1. 🤔 AI生成代码的可信度问题<a href="#1--ai生成代码的可信度问题"><span>#</span></a></h3><p><strong>挑战</strong>：AI可能生成有缺陷或不安全的代码</p><p><strong>应对</strong>：</p><ul>
<li>✅ 建立完善的AI代码评审体系</li>
<li>👤 人工关键环节把关</li>
<li>🔄 持续优化AI模型和Prompt工程</li>
</ul></section><section><h3>2. 🎓 团队技能转型压力<a href="#2--团队技能转型压力"><span>#</span></a></h3><p><strong>挑战</strong>：传统开发者需要学习AI工具和新的工作方式</p><p><strong>应对</strong>：</p><ul>
<li>📚 系统化培训AI工具使用</li>
<li>📖 建立AI辅助开发的最佳实践</li>
<li>🚀 渐进式引入AI工具，降低转型阻力</li>
</ul></section><section><h3>3. 🔐 数据安全和隐私保护<a href="#3--数据安全和隐私保护"><span>#</span></a></h3><p><strong>挑战</strong>：代码和数据可能涉及敏感信息</p><p><strong>应对</strong>：</p><ul>
<li>🏢 使用私有化部署的AI模型</li>
<li>🛡️ 建立数据脱敏和访问控制机制</li>
<li>⚖️ 符合相关法律法规要求</li>
</ul></section><section><h3>4. 🔄 AI模型的持续优化<a href="#4--ai模型的持续优化"><span>#</span></a></h3><p><strong>挑战</strong>：AI模型需要不断训练和优化以适应业务需求</p><p><strong>应对</strong>：</p><ul>
<li>📊 建立模型反馈和优化机制</li>
<li>🎯 收集和使用高质量训练数据</li>
<li>📈 定期评估和更新模型性能</li>
</ul></section></section><section><h2>🔮 未来展望<a href="#-未来展望"><span>#</span></a></h2><section><h3>1. 🌟 AI原生开发将成为主流<a href="#1--ai原生开发将成为主流"><span>#</span></a></h3><p>未来3-5年，AI原生软件开发将成为行业标准，不采用AI的开发团队将失去竞争力。</p></section><section><h3>2. 👨‍💻 开发者角色重新定义<a href="#2--开发者角色重新定义"><span>#</span></a></h3><p>开发者将从”编码者”转变为”AI系统架构师”和”质量把关者”，更专注于业务逻辑和创新。</p></section><section><h3>3. 🎓 软件工程教育体系重构<a href="#3--软件工程教育体系重构"><span>#</span></a></h3><p>传统的编程教育将转向AI辅助开发教育，培养学生的AI思维和系统设计能力。</p></section><section><h3>4. 🛠️ 开发工具生态全面升级<a href="#4-️-开发工具生态全面升级"><span>#</span></a></h3><p>IDE、代码仓库、项目管理等工具将深度集成AI能力，形成完整的AI原生开发生态。</p></section></section><section><h2>🎯 结语<a href="#-结语"><span>#</span></a></h2><p>AI新时代的软件开发范式，不仅仅是技术的升级，更是思维方式的革命。从需求锚定到集成交付，AI正在重塑软件开发的每一个环节。拥抱这场变革，主动学习和实践AI原生开发模式，将成为每个软件开发者和团队的必然选择。</p><p>在这个AI驱动的时代，我们不仅要掌握工具的使用，更要理解背后的原理和思维模式。只有这样，才能真正发挥AI的潜力，创造出更优秀的软件产品，推动整个行业的进步。</p><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/d945d0b15a8efc458a368422c4af6315.jpg" alt="AI新时代软件开发范式流程图" /><figcaption>AI新时代软件开发范式流程图</figcaption></figure><p></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/first-principles-thinking/</id>
      <title type="text">🧠第一性原理：从赚钱到系统复制的底层思维框架</title>
      <published>2026-04-20T00:00:00.000Z</published>
      <updated>2026-04-20T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/first-principles-thinking/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p><em>第一性原理：从赚钱到系统复制的底层思维框架</em></p>
<p>第一性原理是一种思考方式，它要求我们回到事物的最基本事实，从最根本的真理出发，重新构建认知和解决方案。这种思维方式让我们跳出传统框架和经验主义的束缚，找到问题的本质。</p>
<ul>
<li>
<p>赚钱的第一性原理，是创造价值，不是出卖时间。</p>
</li>
<li>
<p>创造价值的第一性原理，是解决问题，不是提供劳动。</p>
</li>
<li>
<p>解决问题的第一性原理，是识别真需求，不是自我感动。</p>
</li>
<li>
<p>识别真需求的第一性原理，是实践反馈，不是闭门造车。</p>
</li>
<li>
<p>实践反馈的第一性原理，是快速迭代，不是仔细打磨。</p>
</li>
<li>
<p>快速迭代的第一性原理，是验证模型，不是为了交付。</p>
</li>
<li>
<p>验证模型的第一性原理，是为了系统复制，而不是个人苦干。</p>
</li>
<li>
<p>系统复制的第一性原理，是杠杆思维，不是线性积累。</p>
</li>
</ul>
<hr />
<blockquote><p>在不确定性中找到确定性，在复杂中找到简单，在变化中找到不变。</p></blockquote>
<hr />
<p></p><figure><img src="https://imgbed.xutopia.top/image-assets/c5c041fd17b04f7d5aefc3734a8f60f9.jpg" alt="1776609030380" /><figcaption>1776609030380</figcaption></figure><p></p>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/nodejs-notes/</id>
      <title type="text">🟢 Node.js 完整笔记</title>
      <published>2026-04-20T00:00:00.000Z</published>
      <updated>2026-04-20T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/nodejs-notes/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>Node.js 笔记<a href="#nodejs-笔记"><span>#</span></a></h1><section><h2>目录<a href="#目录"><span>#</span></a></h2><ul>
<li><a href="#nodejs-%E7%AE%80%E4%BB%8B">Node.js 简介</a></li>
<li><a href="#%E5%AE%89%E8%A3%85-nodejs">安装 Node.js</a></li>
<li><a href="#%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7-nvm">版本管理工具 nvm</a></li>
<li><a href="#%E5%8C%85%E7%AE%A1%E7%90%86%E5%99%A8">包管理器</a></li>
<li><a href="#npm-%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4">npm 常用命令</a></li>
<li><a href="#packagejson-%E8%AF%A6%E8%A7%A3">package.json 详解</a></li>
<li><a href="#%E6%A8%A1%E5%9D%97%E7%B3%BB%E7%BB%9F">模块系统</a></li>
<li><a href="#%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97">核心模块</a></li>
<li><a href="#%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE">环境配置</a></li>
<li><a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98">常见问题</a></li>
</ul><hr /></section><section><h2>Node.js 简介<a href="#nodejs-简介"><span>#</span></a></h2><p>Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境，允许在服务器端执行 JavaScript 代码。</p><p><strong>特点：</strong></p><ul>
<li>事件驱动、非阻塞 I/O 模型</li>
<li>轻量高效，适合数据密集型实时应用</li>
<li>npm 是全球最大的开源库生态系统</li>
</ul><p><strong>版本说明：</strong></p><ul>
<li><strong>LTS</strong> (Long Term Support)：长期稳定版，推荐生产环境使用</li>
<li><strong>Current</strong>：最新特性版，包含最新功能但可能不稳定</li>
</ul><hr /></section><section><h2>安装 Node.js<a href="#安装-nodejs"><span>#</span></a></h2><section><h3>Windows 系统<a href="#windows-系统"><span>#</span></a></h3><p><strong>方式一：官网下载安装包</strong></p><ol>
<li>访问 <a href="https://nodejs.org/" target="_blank">Node.js 官网</a></li>
<li>下载 Windows Installer (.msi)</li>
<li>运行安装程序，一路 Next 即可</li>
</ol><p><strong>方式二：使用 Chocolatey</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 Chocolatey (需管理员权限)</span></div></div><div><div><div>2</div></div><div><span>iwr</span><span> </span><span>https://chocolatey.org/install.ps1</span><span> </span><span>-UseBasicParsing</span><span> | </span><span>iex</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装 Node.js</span></div></div><div><div><div>5</div></div><div><span>choco</span><span> </span><span>install</span><span> </span><span>nodejs</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>方式三：使用 Winget</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>winget</span><span> </span><span>install</span><span> </span><span>OpenJS.NodeJS</span></div></div><div><div><div>2</div></div><div><span>winget</span><span> </span><span>install</span><span> </span><span>OpenJS.NodeJS.LTS</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>macOS 系统<a href="#macos-系统"><span>#</span></a></h3><p><strong>方式一：官网下载安装包</strong></p><ol>
<li>访问 <a href="https://nodejs.org/" target="_blank">Node.js 官网</a></li>
<li>下载 macOS Installer (.pkg)</li>
<li>运行安装程序</li>
</ol><p><strong>方式二：使用 Homebrew</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 Homebrew (若未安装)</span></div></div><div><div><div>2</div></div><div><span>/bin/bash</span><span> </span><span>-c</span><span> </span><span>"$(</span><span>curl</span><span> </span><span>-fsSL</span><span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装 Node.js</span></div></div><div><div><div>5</div></div><div><span>brew</span><span> </span><span>install</span><span> </span><span>node</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 安装 LTS 版本</span></div></div><div><div><div>8</div></div><div><span>brew</span><span> </span><span>install</span><span> </span><span>node@lts</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>方式三：使用 nvm</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 nvm (见下文 nvm 章节)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Linux 系统<a href="#linux-系统"><span>#</span></a></h3><section><h4>Ubuntu / Debian<a href="#ubuntu--debian"><span>#</span></a></h4><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 更新系统包</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>update</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>upgrade</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 移除旧版本(若有)</span></div></div><div><div><div>6</div></div><div><span>sudo</span><span> </span><span>apt</span><span> </span><span>remove</span><span> </span><span>nodejs</span><span> </span><span>npm</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 添加 NodeSource 仓库 (以 LTS 版本为例)</span></div></div><div><div><div>9</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://deb.nodesource.com/setup_lts.x</span><span> | </span><span>sudo</span><span> </span><span>-E</span><span> </span><span>bash</span><span> </span><span>-</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># 安装 Node.js</span></div></div><div><div><div>12</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nodejs</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 验证安装</span></div></div><div><div><div>15</div></div><div><span>node</span><span> </span><span>-v</span></div></div><div><div><div>16</div></div><div><span>npm</span><span> </span><span>-v</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>CentOS / RHEL / Fedora<a href="#centos--rhel--fedora"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 添加 NodeSource 仓库</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://rpm.nodesource.com/setup_lts.x</span><span> | </span><span>sudo</span><span> </span><span>bash</span><span> </span><span>-</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装 Node.js</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>yum</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nodejs</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 或使用 dnf</span></div></div><div><div><div>8</div></div><div><span>sudo</span><span> </span><span>dnf</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nodejs</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>Arch Linux<a href="#arch-linux"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>pacman</span><span> </span><span>-S</span><span> </span><span>nodejs</span><span> </span><span>npm</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>Alpine Linux<a href="#alpine-linux"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>apk</span><span> </span><span>add</span><span> </span><span>nodejs</span><span> </span><span>npm</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>Docker 环境中使用<a href="#docker-环境中使用"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 拉取官方镜像</span></div></div><div><div><div>2</div></div><div><span>docker</span><span> </span><span>pull</span><span> </span><span>node:latest</span></div></div><div><div><div>3</div></div><div><span>docker</span><span> </span><span>pull</span><span> </span><span>node:lts</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 运行交互式容器</span></div></div><div><div><div>6</div></div><div><span>docker</span><span> </span><span>run</span><span> </span><span>-it</span><span> </span><span>node:latest</span><span> </span><span>bash</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 运行特定版本</span></div></div><div><div><div>9</div></div><div><span>docker</span><span> </span><span>run</span><span> </span><span>-it</span><span> </span><span>node:18</span><span> </span><span>bash</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>版本管理工具 nvm<a href="#版本管理工具-nvm"><span>#</span></a></h2><p>nvm (Node Version Manager) 允许在同一台机器上管理多个 Node.js 版本。</p><section><h3>安装 nvm<a href="#安装-nvm"><span>#</span></a></h3><p><strong>Linux / macOS</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-o-</span><span> </span><span>https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh</span><span> | </span><span>bash</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或使用 wget：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>wget</span><span> </span><span>-qO-</span><span> </span><span>https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh</span><span> | </span><span>bash</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>Windows</strong>
Windows 系统使用 <a href="https://github.com/coreybutler/nvm-windows" target="_blank">nvm-windows</a>：</p><ol>
<li>下载安装包：<a href="https://github.com/coreybutler/nvm-windows/releases" target="_blank">https://github.com/coreybutler/nvm-windows/releases</a></li>
<li>运行安装程序</li>
</ol></section><section><h3>nvm 常用命令<a href="#nvm-常用命令"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 nvm-windows 后验证</span></div></div><div><div><div>2</div></div><div><span>nvm</span><span> </span><span>version</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 查看可用版本</span></div></div><div><div><div>5</div></div><div><span>nvm</span><span> </span><span>list</span><span> </span><span>available</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 安装最新稳定版</span></div></div><div><div><div>8</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>lts</span></div></div><div><div><div>9</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>--lts</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># 安装最新版本</span></div></div><div><div><div>12</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>latest</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 安装指定版本</span></div></div><div><div><div>15</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>18.17.0</span></div></div><div><div><div>16</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>20</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span># 查看已安装版本</span></div></div><div><div><div>19</div></div><div><span>nvm</span><span> </span><span>list</span></div></div><div><div><div>20</div></div><div><span>nvm</span><span> </span><span>ls</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span># 切换使用某个版本</span></div></div><div><div><div>23</div></div><div><span>nvm</span><span> </span><span>use</span><span> </span><span>18.17.0</span></div></div><div><div><div>24</div></div><div><span>nvm</span><span> </span><span>use</span><span> </span><span>lts</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span># 设置默认版本</span></div></div><div><div><div>27</div></div><div><span>nvm</span><span> </span><span>alias</span><span> </span><span>default</span><span> </span><span>18.17.0</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span># 卸载指定版本</span></div></div><div><div><div>30</div></div><div><span>nvm</span><span> </span><span>uninstall</span><span> </span><span>16.20.0</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span># 查看当前版本</span></div></div><div><div><div>33</div></div><div><span>nvm</span><span> </span><span>current</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span># 允许在特定版本上运行命令</span></div></div><div><div><div>36</div></div><div><span>nvm</span><span> </span><span>run</span><span> </span><span>18.17.0</span><span> </span><span>--version</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>nvm 高级配置<a href="#nvm-高级配置"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 设置镜像 (解决国内下载慢的问题)</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>NVM_NODEJS_ORG_MIRROR</span><span>=</span><span>https</span><span>://</span><span>npmmirror</span><span>.</span><span>com</span><span>/</span><span>mirrors</span><span>/</span><span>node</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 开启/关闭 node 版本自动切换</span></div></div><div><div><div>5</div></div><div><span>echo</span><span> </span><span>'autoload -U add-zsh-hook'</span><span> &gt;&gt; </span><span>~/.zshrc</span></div></div><div><div><div>6</div></div><div><span>load-nvmrc</span><span>() { </span><span>...</span><span> </span><span>}</span><span>  </span><span># 可配置自动读取 .nvmrc 文件</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>包管理器<a href="#包管理器"><span>#</span></a></h2><section><h3>npm (Node Package Manager)<a href="#npm-node-package-manager"><span>#</span></a></h3><p>npm 是 Node.js 默认的包管理器，集成在 Node.js 安装包中。</p></section><section><h3>pnpm (Performant npm)<a href="#pnpm-performant-npm"><span>#</span></a></h3><p>pnpm 是一个快速、节省空间的包管理器，使用硬链接避免重复存储。</p><p><strong>安装 pnpm</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用 npm 安装</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>pnpm</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用 corepack 启用 (Node.js 16+)</span></div></div><div><div><div>5</div></div><div><span>corepack</span><span> </span><span>enable</span></div></div><div><div><div>6</div></div><div><span>corepack</span><span> </span><span>prepare</span><span> </span><span>pnpm@latest</span><span> </span><span>--activate</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 使用脚本安装</span></div></div><div><div><div>9</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://get.pnpm.io/install.sh</span><span> | </span><span>sh</span><span> </span><span>-</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>pnpm 常用命令</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>install</span></div></div><div><div><div>2</div></div><div><span>pnpm</span><span> </span><span>add</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>3</div></div><div><span>pnpm</span><span> </span><span>add</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-D</span><span>          </span><span># 开发依赖</span></div></div><div><div><div>4</div></div><div><span>pnpm</span><span> </span><span>add</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-g</span><span>           </span><span># 全局安装</span></div></div><div><div><div>5</div></div><div><span>pnpm</span><span> </span><span>remove</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>6</div></div><div><span>pnpm</span><span> </span><span>update</span></div></div><div><div><div>7</div></div><div><span>pnpm</span><span> </span><span>outdated</span></div></div><div><div><div>8</div></div><div><span>pnpm</span><span> </span><span>list</span></div></div><div><div><div>9</div></div><div><span>pnpm</span><span> </span><span>list</span><span> </span><span>-g</span></div></div><div><div><div>10</div></div><div><span>pnpm</span><span> </span><span>run</span><span> </span><span>&lt;script&gt;</span></div></div><div><div><div>11</div></div><div><span>pnpm</span><span> </span><span>init</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>yarn<a href="#yarn"><span>#</span></a></h3><p><strong>安装 yarn</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用 npm 安装</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>yarn</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用 corepack 启用</span></div></div><div><div><div>5</div></div><div><span>corepack</span><span> </span><span>enable</span></div></div><div><div><div>6</div></div><div><span>corepack</span><span> </span><span>prepare</span><span> </span><span>yarn@stable</span><span> </span><span>--activate</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>yarn 常用命令</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>yarn</span><span> </span><span>install</span></div></div><div><div><div>2</div></div><div><span>yarn</span><span> </span><span>add</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>3</div></div><div><span>yarn</span><span> </span><span>add</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--dev</span></div></div><div><div><div>4</div></div><div><span>yarn</span><span> </span><span>add</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-g</span></div></div><div><div><div>5</div></div><div><span>yarn</span><span> </span><span>remove</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>6</div></div><div><span>yarn</span><span> </span><span>upgrade</span></div></div><div><div><div>7</div></div><div><span>yarn</span><span> </span><span>upgrade-interactive</span></div></div><div><div><div>8</div></div><div><span>yarn</span><span> </span><span>list</span></div></div><div><div><div>9</div></div><div><span>yarn</span><span> </span><span>run</span><span> </span><span>&lt;script&gt;</span></div></div><div><div><div>10</div></div><div><span>yarn</span><span> </span><span>init</span></div></div><div><div><div>11</div></div><div><span>yarn</span><span> </span><span>info</span><span> </span><span>&lt;package&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>npm 常用命令<a href="#npm-常用命令"><span>#</span></a></h2><section><h3>基础命令<a href="#基础命令"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看 npm 版本</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>-v</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>--version</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 查看帮助</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>help</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>--help</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 查看命令列表</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>commands</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>package.json 相关<a href="#packagejson-相关"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 初始化项目 (创建 package.json)</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>init</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>init</span><span> </span><span>-y</span><span>                   </span><span># 跳过问答，全部默认</span></div></div><div><div><div>4</div></div><div><span>npm</span><span> </span><span>init</span><span> </span><span>--yes</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 设置初始值</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>init</span><span> </span><span>--scope=username</span><span>     </span><span># 设置 scope 用户名</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>init</span><span> </span><span>--yes</span><span> </span><span>--scope=username</span><span> </span><span>--author-name=</span><span>"Your Name"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>安装依赖<a href="#安装依赖"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装项目所有依赖</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>--legacy-peer-deps</span><span>  </span><span># 忽略 peer dependencies 冲突</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 安装指定包</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;@&lt;version&gt;</span><span>    </span><span># 指定版本</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;@latest</span><span>       </span><span># 最新版本</span></div></div><div><div><div>9</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;@"&gt;=1.0.0 &lt;2.0.0"</span><span>  </span><span># 版本范围</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># 安装并保存到 dependencies (生产依赖)</span></div></div><div><div><div>12</div></div><div><span>npm</span><span> </span><span>install</span><span> &lt;</span><span>package&gt;</span><span> </span><span>--save</span></div></div><div><div><div>13</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-S</span></div></div><div><div><div>14</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--save-prod</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 安装并保存到 devDependencies (开发依赖)</span></div></div><div><div><div>17</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--save-dev</span></div></div><div><div><div>18</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-D</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span># 安装全局包</span></div></div><div><div><div>21</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-g</span></div></div><div><div><div>22</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>--global</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span># 从本地安装</span></div></div><div><div><div>25</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>./path/to/package</span></div></div><div><div><div>26</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>/path/to/package.tgz</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span># 安装 Git URL</span></div></div><div><div><div>29</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>git://github.com/user/project.git</span></div></div><div><div><div>30</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>git+ssh://git@github.com:user/project.git</span></div></div><div><div><div>31</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>git+https://github.com/user/project.git</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span># 安装特定 tag</span></div></div><div><div><div>34</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;@beta</span></div></div><div><div><div>35</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;@rc</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>更新依赖<a href="#更新依赖"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看可更新的包</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>outdated</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 更新所有包</span></div></div><div><div><div>5</div></div><div><span>npm</span><span> </span><span>update</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 更新指定包</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>update</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 检查包最新版本</span></div></div><div><div><div>11</div></div><div><span>npm</span><span> </span><span>outdated</span><span> </span><span>&lt;package&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>卸载依赖<a href="#卸载依赖"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 卸载指定包</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>uninstall</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>remove</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>4</div></div><div><span>npm</span><span> </span><span>rm</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 卸载并从 package.json 移除</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>uninstall</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--save</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>uninstall</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-S</span></div></div><div><div><div>9</div></div><div><span>npm</span><span> </span><span>uninstall</span><span> </span><span>&lt;package&gt;</span><span> </span><span>--save-dev</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>uninstall</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-D</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 卸载全局包</span></div></div><div><div><div>13</div></div><div><span>npm</span><span> </span><span>uninstall</span><span> </span><span>&lt;package&gt;</span><span> </span><span>-g</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>运行脚本<a href="#运行脚本"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 运行 package.json 中定义的脚本</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>&lt;script&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 常用预设脚本</span></div></div><div><div><div>5</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>start</span><span>          </span><span># 启动项目</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>dev</span><span>            </span><span># 开发模式</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>build</span><span>          </span><span># 构建项目</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>test</span><span>           </span><span># 运行测试</span></div></div><div><div><div>9</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>lint</span><span>           </span><span># 代码检查</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>format</span><span>        </span><span># 代码格式化</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 直接执行 bin 命令</span></div></div><div><div><div>13</div></div><div><span>npx</span><span> </span><span>&lt;command&gt;</span></div></div><div><div><div>14</div></div><div><span>npx</span><span> </span><span>create-react-app</span><span> </span><span>my-app</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>包搜索与管理<a href="#包搜索与管理"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 在 npm 仓库搜索包</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>search</span><span> </span><span>&lt;keyword&gt;</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>s</span><span> </span><span>&lt;keyword&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 查看包信息</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>v</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 查看特定信息</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;</span><span> </span><span>version</span></div></div><div><div><div>11</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;</span><span> </span><span>versions</span></div></div><div><div><div>12</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;</span><span> </span><span>dependencies</span></div></div><div><div><div>13</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;</span><span> </span><span>repository.url</span></div></div><div><div><div>14</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;engines</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 查看包在仓库中的状态</span></div></div><div><div><div>17</div></div><div><span>npm</span><span> </span><span>pack</span><span> </span><span>&lt;package&gt;</span><span>    </span><span># 下载包的 tarball</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>查看已安装的包<a href="#查看已安装的包"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看当前项目依赖树</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>list</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>ls</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 查看全局包</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>list</span><span> </span><span>-g</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>ls</span><span> </span><span>-g</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>list</span><span> </span><span>-g</span><span> </span><span>--depth=0</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 查看包的所有版本</span></div></div><div><div><div>11</div></div><div><span>npm</span><span> </span><span>view</span><span> </span><span>&lt;package&gt;</span><span> </span><span>versions</span><span> </span><span>--json</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>缓存管理<a href="#缓存管理"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看缓存</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>ls</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>ls</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 清理缓存</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>clean</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>clean</span><span> </span><span>--force</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 验证缓存</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>verify</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>npm 配置<a href="#npm-配置"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看配置</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>list</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>list</span><span> </span><span>-l</span><span>          </span><span># 查看所有配置</span></div></div><div><div><div>4</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>get</span><span> </span><span>&lt;key&gt;</span></div></div><div><div><div>5</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>list</span><span> </span><span>--json</span><span>      </span><span># JSON 格式输出</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 设置配置</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>&lt;key&gt;</span><span> </span><span>&lt;value&gt;</span></div></div><div><div><div>9</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npmjs.org</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>&lt;key&gt;</span><span> </span><span>&lt;value&gt;</span><span> </span><span>--location=user</span><span>     </span><span># 用户配置</span></div></div><div><div><div>11</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>&lt;key&gt;</span><span> </span><span>&lt;value&gt;</span><span> </span><span>--location=project</span><span>   </span><span># 项目配置</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 删除配置</span></div></div><div><div><div>14</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>delete</span><span> </span><span>&lt;key&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 编辑配置文件</span></div></div><div><div><div>17</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>edit</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>package.json 详解<a href="#packagejson-详解"><span>#</span></a></h2><p>package.json 是 Node.js 项目的核心配置文件。</p><section><h3>基本结构<a href="#基本结构"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"name"</span><span>: </span><span>"my-project"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"version"</span><span>: </span><span>"1.0.0"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"description"</span><span>: </span><span>"项目描述"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>  </span><span>"main"</span><span>: </span><span>"index.js"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>  </span><span>"scripts"</span><span>: {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>"start"</span><span>: </span><span>"node index.js"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>"dev"</span><span>: </span><span>"nodemon index.js"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"build"</span><span>: </span><span>"webpack --mode production"</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>"test"</span><span>: </span><span>"jest"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>    </span><span>"lint"</span><span>: </span><span>"eslint ."</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span>  </span><span>"keywords"</span><span>: [</span><span>"nodejs"</span><span>, </span><span>"javascript"</span><span>],</span></div></div><div><div><div>14</div></div><div><span>  </span><span>"author"</span><span>: </span><span>"Your Name &lt;email@example.com&gt;"</span><span>,</span></div></div><div><div><div>15</div></div><div><span>  </span><span>"license"</span><span>: </span><span>"MIT"</span><span>,</span></div></div><div><div><div>16</div></div><div><span>  </span><span>"repository"</span><span>: {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>"type"</span><span>: </span><span>"git"</span><span>,</span></div></div><div><div><div>18</div></div><div><span>    </span><span>"url"</span><span>: </span><span>"https://github.com/username/project.git"</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>20</div></div><div><span>  </span><span>"bugs"</span><span>: {</span></div></div><div><div><div>21</div></div><div><span>    </span><span>"url"</span><span>: </span><span>"https://github.com/username/project/issues"</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>23</div></div><div><span>  </span><span>"homepage"</span><span>: </span><span>"https://github.com/username/project#readme"</span><span>,</span></div></div><div><div><div>24</div></div><div><span>  </span><span>"dependencies"</span><span>: {},</span></div></div><div><div><div>25</div></div><div><span>  </span><span>"devDependencies"</span><span>: {}</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>字段说明<a href="#字段说明"><span>#</span></a></h3>

<table><thead><tr><th>字段</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>name</code></td><td>项目名称</td><td><code>"my-project"</code></td></tr><tr><td><code>version</code></td><td>版本号</td><td><code>"1.0.0"</code></td></tr><tr><td><code>description</code></td><td>项目描述</td><td><code>"A cool project"</code></td></tr><tr><td><code>main</code></td><td>入口文件</td><td><code>"src/index.js"</code></td></tr><tr><td><code>type</code></td><td>模块类型</td><td><code>"module"</code> 或 <code>"commonjs"</code></td></tr><tr><td><code>scripts</code></td><td>命令别名</td><td><code>{"start": "node index.js"}</code></td></tr><tr><td><code>keywords</code></td><td>关键字</td><td><code>["web", "nodejs"]</code></td></tr><tr><td><code>author</code></td><td>作者</td><td><code>"Name &lt;email&gt;"</code></td></tr><tr><td><code>license</code></td><td>许可证</td><td><code>"MIT"</code></td></tr><tr><td><code>repository</code></td><td>仓库地址</td><td><code>{type: "git", url: "..."}</code></td></tr><tr><td><code>engines</code></td><td>Node 版本要求</td><td><code>{"node": "&gt;=16"}</code></td></tr><tr><td><code>os</code></td><td>支持的操作系统</td><td><code>["darwin", "linux"]</code></td></tr><tr><td><code>cpu</code></td><td>支持的 CPU 架构</td><td><code>["x64", "arm64"]</code></td></tr></tbody></table></section><section><h3>dependencies vs devDependencies<a href="#dependencies-vs-devdependencies"><span>#</span></a></h3><p><strong>dependencies</strong> - 生产环境依赖</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>"dependencies"</span><span>: {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"express"</span><span>: </span><span>"^4.18.2"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"axios"</span><span>: </span><span>"^1.6.0"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"dotenv"</span><span>: </span><span>"^16.3.1"</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>devDependencies</strong> - 开发环境依赖</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>"devDependencies"</span><span>: {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"webpack"</span><span>: </span><span>"^5.88.0"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"jest"</span><span>: </span><span>"^29.6.0"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"eslint"</span><span>: </span><span>"^8.45.0"</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>版本号语义化 (SemVer)<a href="#版本号语义化-semver"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>major.minor.patch</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>^     ^    ^</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>|     |    +-- patch: 修复 bug，完全向下兼容</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>|     +------- minor: 新功能，向后兼容</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>+------------- major: 破坏性更新，不向下兼容</span></div></div></code></pre><div><div></div><div></div></div></figure></div>

<table><thead><tr><th>符号</th><th>含义</th><th>示例</th></tr></thead><tbody><tr><td><code>^</code></td><td>允许 minor 和 patch 更新</td><td><code>^1.2.3</code> → <code>1.x.x</code></td></tr><tr><td><code>~</code></td><td>允许 patch 更新</td><td><code>~1.2.3</code> → <code>1.2.x</code></td></tr><tr><td><code>*</code></td><td>最新版本</td><td><code>*</code></td></tr><tr><td><code>&gt;=</code></td><td>大于等于</td><td><code>&gt;=1.0.0</code></td></tr><tr><td><code>1.0.0 - 2.0.0</code></td><td>版本范围</td><td><code>1.0.0 - 2.0.0</code></td></tr><tr><td><code>latest</code></td><td>最新标签版本</td><td><code>latest</code></td></tr></tbody></table></section><section><h3>scripts 变量<a href="#scripts-变量"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"scripts"</span><span>: {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>"build"</span><span>: </span><span>"webpack --progress --config webpack.config.js"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"postinstall"</span><span>: </span><span>"if [ -f ./scripts/postinstall.js ]; then node ./scripts/postinstall.js; fi"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>"prebuild"</span><span>: </span><span>"rimraf dist"</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>常用生命周期钩子：</p><ul>
<li><code>preinstall</code> - 安装前</li>
<li><code>postinstall</code> - 安装后</li>
<li><code>preuninstall</code> - 卸载前</li>
<li><code>postuninstall</code> - 卸载后</li>
<li><code>prepublish</code> - 发布前</li>
<li><code>preversion</code> - 版本更新前</li>
<li><code>postversion</code> - 版本更新后</li>
<li><code>pretest</code> / <code>posttest</code> - 测试前后</li>
<li><code>prestop</code> / <code>poststop</code> - 停止前后</li>
<li><code>prestart</code> / <code>poststart</code> - 启动前后</li>
</ul><hr /></section></section><section><h2>模块系统<a href="#模块系统"><span>#</span></a></h2><section><h3>CommonJS (CJS)<a href="#commonjs-cjs"><span>#</span></a></h3><p>Node.js 默认的模块系统，使用 <code>require()</code> 导入和 <code>module.exports</code> 导出。</p><div><div><div><figure><figcaption><span>module.exports</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>add</span><span> </span><span>=</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span><span>;</span></span></div></div><div><div><div>2</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span><span> </span><span>add</span><span>;</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 导出方式 2: module.exports = {}</span></div></div><div><div><div>5</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>add</span><span>,</span></div></div><div><div><div>7</div></div><div><span>  </span><span>subtract</span><span>:</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>-</span><span><span> </span><span>b</span></span></div></div><div><div><div>8</div></div><div><span>};</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 导出方式 3: exports.alias</span></div></div><div><div><div>11</div></div><div><span>exports</span><span>.</span><span>add</span><span> </span><span>=</span><span><span> </span><span>add</span><span>;</span></span></div></div><div><div><div>12</div></div><div><span>exports</span><span>.</span><span>subtract</span><span> </span><span>=</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>-</span><span><span> </span><span>b</span><span>;</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 导入</span></div></div><div><div><div>15</div></div><div><span>const</span><span> </span><span>add</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'./math'</span><span>);</span></div></div><div><div><div>16</div></div><div><span>const</span><span> { </span><span>add</span><span>, </span><span>subtract</span><span> } </span><span>=</span><span> </span><span>require</span><span>(</span><span>'./math'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>ES Modules (ESM)<a href="#es-modules-esm"><span>#</span></a></h3><p>ES6 引入的模块系统，使用 <code>import</code> 导入和 <code>export</code> 导出。</p><p><strong>注意：</strong> 需要在 package.json 中设置 <code>"type": "module"</code>，或使用 <code>.mjs</code> 扩展名。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 导出方式 1: export</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>add</span><span> </span><span>=</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>subtract</span><span> </span><span>=</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>-</span><span><span> </span><span>b</span><span>;</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 导出方式 2: export default</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>add</span><span> </span><span>=</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>add</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 导出方式 3: export { }</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>add</span><span> </span><span>=</span><span><span> (</span><span>a</span><span>, </span><span>b</span><span>) </span></span><span>=&gt;</span><span><span> </span><span>a</span><span> </span></span><span>+</span><span><span> </span><span>b</span><span>;</span></span></div></div><div><div><div>11</div></div><div><span>export</span><span> { </span><span>add</span><span> };</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 导入</span></div></div><div><div><div>14</div></div><div><span>import</span><span> </span><span>add</span><span> </span><span>from</span><span> </span><span>'./math.js'</span><span>;</span></div></div><div><div><div>15</div></div><div><span>import</span><span> { </span><span>add</span><span>, </span><span>subtract</span><span> } </span><span>from</span><span> </span><span>'./math.js'</span><span>;</span></div></div><div><div><div>16</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>math</span><span> </span><span>from</span><span> </span><span>'./math.js'</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>模块解析<a href="#模块解析"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 相对路径</span></div></div><div><div><div>2</div></div><div><span>require</span><span>(</span><span>'./utils'</span><span>);           </span><span>// ./utils.js</span></div></div><div><div><div>3</div></div><div><span>require</span><span>(</span><span>'./utils.js'</span><span>);       </span><span>// 明确扩展名</span></div></div><div><div><div>4</div></div><div><span>require</span><span>(</span><span>'./'</span><span>);                </span><span>// 查找 ./index.js</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 绝对路径</span></div></div><div><div><div>7</div></div><div><span>require</span><span>(</span><span>'/usr/local/utils'</span><span>);  </span><span>// 绝对路径</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 模块路径 (node_modules)</span></div></div><div><div><div>10</div></div><div><span>require</span><span>(</span><span>'express'</span><span>);           </span><span>// 查找 node_modules/express</span></div></div><div><div><div>11</div></div><div><span>require</span><span>(</span><span>'@babel/core'</span><span>);       </span><span>// 查找 node_modules/@babel/core</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 目录作为模块</span></div></div><div><div><div>14</div></div><div><span>require</span><span>(</span><span>'./dir'</span><span>);             </span><span>// 查找 ./dir/index.js 或 ./dir/package.json 的 main 字段</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>require() 缓存机制<a href="#require-缓存机制"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 模块只会被加载一次，之后从缓存返回</span></div></div><div><div><div>2</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>require</span><span>.</span></span><span>cache</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 清除特定模块缓存</span></div></div><div><div><div>5</div></div><div><span>delete</span><span><span> </span><span>require</span><span>.</span></span><span>cache</span><span><span>[</span><span>require</span><span>.</span></span><span>resolve</span><span>(</span><span>'./module'</span><span>)];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>核心模块<a href="#核心模块"><span>#</span></a></h2><p>Node.js 内置的核心模块，无需额外安装即可使用。</p><section><h3>path - 路径操作<a href="#path---路径操作"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>path</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'path'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 路径拼接</span></div></div><div><div><div>4</div></div><div><span><span>path</span><span>.</span></span><span>join</span><span>(</span><span>'/foo'</span><span>, </span><span>'bar'</span><span>, </span><span>'baz/asdf'</span><span>);</span></div></div><div><div><div>5</div></div><div><span>// → '/foo/bar/baz/asdf'</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 解析为绝对路径</span></div></div><div><div><div>8</div></div><div><span><span>path</span><span>.</span></span><span>resolve</span><span>(</span><span>'index.js'</span><span>);</span></div></div><div><div><div>9</div></div><div><span>// → '/Users/me/project/index.js'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>path</span><span>.</span></span><span>resolve</span><span>(</span><span>'/foo'</span><span>, </span><span>'./bar'</span><span>);</span></div></div><div><div><div>12</div></div><div><span>// → '/foo/bar'</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 获取路径信息</span></div></div><div><div><div>15</div></div><div><span><span>path</span><span>.</span></span><span>basename</span><span>(</span><span>'/foo/bar/index.js'</span><span>);  </span><span>// → 'index.js'</span></div></div><div><div><div>16</div></div><div><span><span>path</span><span>.</span></span><span>dirname</span><span>(</span><span>'/foo/bar/index.js'</span><span>);   </span><span>// → '/foo/bar'</span></div></div><div><div><div>17</div></div><div><span><span>path</span><span>.</span></span><span>extname</span><span>(</span><span>'/foo/bar/index.js'</span><span>);   </span><span>// → '.js'</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 判断路径类型</span></div></div><div><div><div>20</div></div><div><span><span>path</span><span>.</span></span><span>isAbsolute</span><span>(</span><span>'/foo/bar'</span><span>);   </span><span>// → true</span></div></div><div><div><div>21</div></div><div><span><span>path</span><span>.</span></span><span>isAbsolute</span><span>(</span><span>'./foo/bar'</span><span>);  </span><span>// → false</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 解析路径</span></div></div><div><div><div>24</div></div><div><span><span>path</span><span>.</span></span><span>parse</span><span>(</span><span>'/foo/bar/index.js'</span><span>);</span></div></div><div><div><div>25</div></div><div><span>// → { root: '/', dir: '/foo/bar', base: 'index.js', ext: '.js', name: 'index' }</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// 路径格式化</span></div></div><div><div><div>28</div></div><div><span><span>path</span><span>.</span></span><span>format</span><span>({</span></div></div><div><div><div>29</div></div><div><span>  </span><span>dir</span><span>:</span><span> </span><span>'/foo/bar'</span><span>,</span></div></div><div><div><div>30</div></div><div><span>  </span><span>name</span><span>:</span><span> </span><span>'index'</span><span>,</span></div></div><div><div><div>31</div></div><div><span>  </span><span>ext</span><span>:</span><span> </span><span>'.js'</span></div></div><div><div><div>32</div></div><div><span>});</span></div></div><div><div><div>33</div></div><div><span>// → '/foo/bar/index.js'</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>// 跨平台路径分隔符</span></div></div><div><div><div>36</div></div><div><span><span>path</span><span>.</span></span><span>sep</span><span>;           </span><span>// Linux/macOS: '/', Windows: '\\'</span></div></div><div><div><div>37</div></div><div><span><span>path</span><span>.</span></span><span>delimiter</span><span>;     </span><span>// Linux/macOS: ':', Windows: ';'</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>fs - 文件系统<a href="#fs---文件系统"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>fs</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'fs'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>fsPromises</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'fs'</span><span>).</span><span>promises</span><span>;  </span><span>// Promise 风格</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 同步读取</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>content</span><span> </span><span>=</span><span><span> </span><span>fs</span><span>.</span></span><span>readFileSync</span><span>(</span><span>'./file.txt'</span><span>, </span><span>'utf8'</span><span>);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 异步回调风格</span></div></div><div><div><div>8</div></div><div><span><span>fs</span><span>.</span></span><span>readFile</span><span>(</span><span>'./file.txt'</span><span>, </span><span>'utf8'</span><span><span>, (</span><span>err</span><span>, </span><span>data</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>9</div></div><div><span>  </span><span>if</span><span><span> (</span><span>err</span><span>) </span></span><span>throw</span><span><span> </span><span>err</span><span>;</span></span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>data</span><span>);</span></span></div></div><div><div><div>11</div></div><div><span>});</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// Promise 风格 (Node.js 10+)</span></div></div><div><div><div>14</div></div><div><span>(</span><span>async</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span>  </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>fsPromises</span><span>.</span></span><span>readFile</span><span>(</span><span>'./file.txt'</span><span>, </span><span>'utf8'</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>data</span><span>);</span></span></div></div><div><div><div>17</div></div><div><span>})();</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 同步方法列表</span></div></div><div><div><div>20</div></div><div><span><span>fs</span><span>.</span></span><span>readFileSync</span><span>()</span></div></div><div><div><div>21</div></div><div><span><span>fs</span><span>.</span></span><span>writeFileSync</span><span>()</span></div></div><div><div><div>22</div></div><div><span><span>fs</span><span>.</span></span><span>appendFileSync</span><span>()</span></div></div><div><div><div>23</div></div><div><span><span>fs</span><span>.</span></span><span>mkdirSync</span><span>()</span></div></div><div><div><div>24</div></div><div><span><span>fs</span><span>.</span></span><span>rmdirSync</span><span>()</span></div></div><div><div><div>25</div></div><div><span><span>fs</span><span>.</span></span><span>readdirSync</span><span>()</span></div></div><div><div><div>26</div></div><div><span><span>fs</span><span>.</span></span><span>statSync</span><span>()</span></div></div><div><div><div>27</div></div><div><span><span>fs</span><span>.</span></span><span>existsSync</span><span>()</span></div></div><div><div><div>28</div></div><div><span><span>fs</span><span>.</span></span><span>copyFileSync</span><span>()</span></div></div><div><div><div>29</div></div><div><span><span>fs</span><span>.</span></span><span>unlinkSync</span><span>()</span></div></div><div><div><div>30</div></div><div><span><span>fs</span><span>.</span></span><span>renameSync</span><span>()</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>// 异步方法列表</span></div></div><div><div><div>33</div></div><div><span><span>fs</span><span>.</span></span><span>readFile</span><span>()</span></div></div><div><div><div>34</div></div><div><span><span>fs</span><span>.</span></span><span>writeFile</span><span>()</span></div></div><div><div><div>35</div></div><div><span><span>fs</span><span>.</span></span><span>appendFile</span><span>()</span></div></div><div><div><div>36</div></div><div><span><span>fs</span><span>.</span></span><span>mkdir</span><span>()</span></div></div><div><div><div>37</div></div><div><span><span>fs</span><span>.</span></span><span>rmdir</span><span>()</span></div></div><div><div><div>38</div></div><div><span><span>fs</span><span>.</span></span><span>readdir</span><span>()</span></div></div><div><div><div>39</div></div><div><span><span>fs</span><span>.</span></span><span>stat</span><span>()</span></div></div><div><div><div>40</div></div><div><span><span>fs</span><span>.</span></span><span>access</span><span>()</span></div></div><div><div><div>41</div></div><div><span><span>fs</span><span>.</span></span><span>copyFile</span><span>()</span></div></div><div><div><div>42</div></div><div><span><span>fs</span><span>.</span></span><span>unlink</span><span>()</span></div></div><div><div><div>43</div></div><div><span><span>fs</span><span>.</span></span><span>rename</span><span>()</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>// Promise 方法 (fs.promises)</span></div></div><div><div><div>46</div></div><div><span><span>fsPromises</span><span>.</span></span><span>readFile</span><span>()</span></div></div><div><div><div>47</div></div><div><span><span>fsPromises</span><span>.</span></span><span>writeFile</span><span>()</span></div></div><div><div><div>48</div></div><div><span><span>fsPromises</span><span>.</span></span><span>mkdir</span><span>()</span></div></div><div><div><div>49</div></div><div><span><span>fsPromises</span><span>.</span></span><span>readdir</span><span>()</span></div></div><div><div><div>50</div></div><div><span><span>fsPromises</span><span>.</span></span><span>stat</span><span>()</span></div></div><div><div><div>51</div></div><div><span><span>fsPromises</span><span>.</span></span><span>access</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>os - 操作系统信息<a href="#os---操作系统信息"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>os</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'os'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>os</span><span>.</span></span><span>platform</span><span>();      </span><span>// 'linux', 'darwin', 'win32'</span></div></div><div><div><div>4</div></div><div><span><span>os</span><span>.</span></span><span>arch</span><span>();           </span><span>// 'x64', 'arm64'</span></div></div><div><div><div>5</div></div><div><span><span>os</span><span>.</span></span><span>release</span><span>();        </span><span>// 操作系统版本</span></div></div><div><div><div>6</div></div><div><span><span>os</span><span>.</span></span><span>type</span><span>();           </span><span>// 'Linux', 'Darwin', 'Windows_NT'</span></div></div><div><div><div>7</div></div><div><span><span>os</span><span>.</span></span><span>homedir</span><span>();        </span><span>// 用户主目录</span></div></div><div><div><div>8</div></div><div><span><span>os</span><span>.</span></span><span>tmpdir</span><span>();         </span><span>// 临时文件目录</span></div></div><div><div><div>9</div></div><div><span><span>os</span><span>.</span></span><span>hostname</span><span>();       </span><span>// 主机名</span></div></div><div><div><div>10</div></div><div><span><span>os</span><span>.</span></span><span>EOL</span><span>;              </span><span>// 换行符 '\n' (Linux/macOS) 或 '\r\n' (Windows)</span></div></div><div><div><div>11</div></div><div><span><span>os</span><span>.</span></span><span>cpus</span><span>();           </span><span>// CPU 信息</span></div></div><div><div><div>12</div></div><div><span><span>os</span><span>.</span></span><span>totalmem</span><span>();       </span><span>// 总内存 (bytes)</span></div></div><div><div><div>13</div></div><div><span><span>os</span><span>.</span></span><span>freemem</span><span>();        </span><span>// 空闲内存 (bytes)</span></div></div><div><div><div>14</div></div><div><span><span>os</span><span>.</span></span><span>networkInterfaces</span><span>();  </span><span>// 网络接口</span></div></div><div><div><div>15</div></div><div><span><span>os</span><span>.</span></span><span>uptime</span><span>();         </span><span>// 系统运行时间 (秒)</span></div></div><div><div><div>16</div></div><div><span><span>os</span><span>.</span></span><span>loadavg</span><span>();        </span><span>// 平均负载 (Linux/macOS)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>util - 工具函数<a href="#util---工具函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>util</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'util'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> { </span><span>promisify</span><span> } </span><span>=</span><span><span> </span><span>util</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span>const</span><span> { </span><span>callbackify</span><span> } </span><span>=</span><span><span> </span><span>util</span><span>;</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 将回调函数转为 Promise</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>fsReadFile</span><span> </span><span>=</span><span> </span><span>promisify</span><span><span>(</span><span>fs</span><span>.</span></span><span>readFile</span><span>);</span></div></div><div><div><div>7</div></div><div><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>fsReadFile</span><span>(</span><span>'./file.txt'</span><span>, </span><span>'utf8'</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 将 async 函数转为回调风格</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>asyncFn</span><span> </span><span>=</span><span> </span><span>async</span><span> () </span><span>=&gt;</span><span> {};</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>callbackFn</span><span> </span><span>=</span><span> </span><span>callbackify</span><span><span>(</span><span>asyncFn</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 格式化字符串</span></div></div><div><div><div>14</div></div><div><span><span>util</span><span>.</span></span><span>format</span><span>(</span><span>'%s:%d'</span><span>, </span><span>'hello'</span><span>, </span><span>123</span><span>);  </span><span>// 'hello:123'</span></div></div><div><div><div>15</div></div><div><span><span>util</span><span>.</span></span><span>format</span><span>(</span><span>'%s:%s'</span><span>, </span><span>'hello'</span><span>);       </span><span>// 'hello:%s'</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// inspect 对象查看</span></div></div><div><div><div>18</div></div><div><span><span>util</span><span>.</span></span><span>inspect</span><span>({ </span><span>a</span><span>:</span><span> </span><span>1</span><span>, </span><span>b</span><span>:</span><span> { </span><span>c</span><span>:</span><span> </span><span>2</span><span> } });</span></div></div><div><div><div>19</div></div><div><span>// → "{ a: 1, b: { c: 2 } }"</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>// 类型检查</span></div></div><div><div><div>22</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isPromise</span><span><span>(</span><span>p</span><span>);           </span></span><span>// 是否为 Promise</span></div></div><div><div><div>23</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isArray</span><span><span>(</span><span>arr</span><span>);           </span></span><span>// 是否为数组</span></div></div><div><div><div>24</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isDate</span><span><span>(</span><span>date</span><span>);           </span></span><span>// 是否为日期</span></div></div><div><div><div>25</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isRegExp</span><span><span>(</span><span>regex</span><span>);        </span></span><span>// 是否为正则</span></div></div><div><div><div>26</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isMap</span><span><span>(</span><span>map</span><span>);             </span></span><span>// 是否为 Map</span></div></div><div><div><div>27</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isSet</span><span><span>(</span><span>set</span><span>);             </span></span><span>// 是否为 Set</span></div></div><div><div><div>28</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isAsyncFunction</span><span><span>(</span><span>fn</span><span>);    </span></span><span>// 是否为 async 函数</span></div></div><div><div><div>29</div></div><div><span><span>util</span><span>.</span></span><span>types</span><span>.</span><span>isGeneratorFunction</span><span><span>(</span><span>fn</span><span>);</span></span><span>// 是否为生成器函数</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>events - 事件触发器<a href="#events---事件触发器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>EventEmitter</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'events'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>class</span><span> </span><span>MyEmitter</span><span> </span><span>extends</span><span> </span><span>EventEmitter</span><span> {}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>emitter</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>MyEmitter</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 监听事件</span></div></div><div><div><div>8</div></div><div><span><span>emitter</span><span>.</span></span><span>on</span><span>(</span><span>'event'</span><span><span>, (</span><span>arg1</span><span>, </span><span>arg2</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'事件触发'</span><span><span>, </span><span>arg1</span><span>, </span><span>arg2</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span>});</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// 只触发一次的监听器</span></div></div><div><div><div>13</div></div><div><span><span>emitter</span><span>.</span></span><span>once</span><span>(</span><span>'event'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'只触发一次'</span><span>);</span></div></div><div><div><div>15</div></div><div><span>});</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// 发送事件</span></div></div><div><div><div>18</div></div><div><span><span>emitter</span><span>.</span></span><span>emit</span><span>(</span><span>'event'</span><span>, </span><span>'参数1'</span><span>, </span><span>'参数2'</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 移除监听器</span></div></div><div><div><div>21</div></div><div><span><span>emitter</span><span>.</span></span><span>off</span><span>(</span><span>'event'</span><span><span>, </span><span>listenerFn</span><span>);</span></span></div></div><div><div><div>22</div></div><div><span><span>emitter</span><span>.</span></span><span>removeListener</span><span>(</span><span>'event'</span><span><span>, </span><span>listenerFn</span><span>);</span></span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>// 移除所有监听器</span></div></div><div><div><div>25</div></div><div><span><span>emitter</span><span>.</span></span><span>removeAllListeners</span><span>();</span></div></div><div><div><div>26</div></div><div><span><span>emitter</span><span>.</span></span><span>removeAllListeners</span><span>(</span><span>'event'</span><span>);</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>// 查看监听器数量</span></div></div><div><div><div>29</div></div><div><span><span>emitter</span><span>.</span></span><span>listenerCount</span><span>(</span><span>'event'</span><span>);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>// 查看所有监听器</span></div></div><div><div><div>32</div></div><div><span><span>emitter</span><span>.</span></span><span>listeners</span><span>(</span><span>'event'</span><span>);</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>// 错误事件</span></div></div><div><div><div>35</div></div><div><span><span>emitter</span><span>.</span></span><span>on</span><span>(</span><span>'error'</span><span><span>, (</span><span>err</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>36</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'错误:'</span><span><span>, </span><span>err</span><span>);</span></span></div></div><div><div><div>37</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>crypto - 加密模块<a href="#crypto---加密模块"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>crypto</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'crypto'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 创建哈希</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>hash</span><span> </span><span>=</span><span><span> </span><span>crypto</span><span>.</span></span><span>createHash</span><span>(</span><span>'sha256'</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>hash</span><span>.</span></span><span>update</span><span>(</span><span>'hello'</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>hash</span><span>.</span></span><span>digest</span><span>(</span><span>'hex'</span><span>));</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 创建 HMAC</span></div></div><div><div><div>9</div></div><div><span>const</span><span> </span><span>hmac</span><span> </span><span>=</span><span><span> </span><span>crypto</span><span>.</span></span><span>createHmac</span><span>(</span><span>'sha256'</span><span>, </span><span>'secret-key'</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>hmac</span><span>.</span></span><span>update</span><span>(</span><span>'hello'</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>hmac</span><span>.</span></span><span>digest</span><span>(</span><span>'hex'</span><span>));</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 加密</span></div></div><div><div><div>14</div></div><div><span>const</span><span> </span><span>cipher</span><span> </span><span>=</span><span><span> </span><span>crypto</span><span>.</span></span><span>createCipher</span><span>(</span><span>'aes192'</span><span>, </span><span>'password'</span><span>);</span></div></div><div><div><div>15</div></div><div><span>let</span><span><span> </span><span>encrypted</span><span> </span></span><span>=</span><span><span> </span><span>cipher</span><span>.</span></span><span>update</span><span>(</span><span>'hello'</span><span>, </span><span>'utf8'</span><span>, </span><span>'hex'</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>encrypted</span><span> </span></span><span>+=</span><span><span> </span><span>cipher</span><span>.</span></span><span>final</span><span>(</span><span>'hex'</span><span>);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>// 解密</span></div></div><div><div><div>19</div></div><div><span>const</span><span> </span><span>decipher</span><span> </span><span>=</span><span><span> </span><span>crypto</span><span>.</span></span><span>createDecipher</span><span>(</span><span>'aes192'</span><span>, </span><span>'password'</span><span>);</span></div></div><div><div><div>20</div></div><div><span>let</span><span><span> </span><span>decrypted</span><span> </span></span><span>=</span><span><span> </span><span>decipher</span><span>.</span></span><span>update</span><span><span>(</span><span>encrypted</span><span>, </span></span><span>'hex'</span><span>, </span><span>'utf8'</span><span>);</span></div></div><div><div><div>21</div></div><div><span><span>decrypted</span><span> </span></span><span>+=</span><span><span> </span><span>decipher</span><span>.</span></span><span>final</span><span>(</span><span>'utf8'</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 随机数据</span></div></div><div><div><div>24</div></div><div><span><span>crypto</span><span>.</span></span><span>randomBytes</span><span>(</span><span>16</span><span>);            </span><span>// 16 字节随机数据</span></div></div><div><div><div>25</div></div><div><span><span>crypto</span><span>.</span></span><span>randomUUID</span><span>();               </span><span>// UUID v4</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// 生成密钥</span></div></div><div><div><div>28</div></div><div><span><span>crypto</span><span>.</span></span><span>generateKey</span><span>(</span><span>'aes'</span><span>, { </span><span>length</span><span>:</span><span> </span><span>256</span><span> });</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>// diffie-hellman 密钥交换</span></div></div><div><div><div>31</div></div><div><span>const</span><span> </span><span>dh</span><span> </span><span>=</span><span><span> </span><span>crypto</span><span>.</span></span><span>createDiffieHellman</span><span>(</span><span>2048</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>buffer - 二进制数据<a href="#buffer---二进制数据"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>buffer</span><span> </span><span>=</span><span><span> </span><span>Buffer</span><span>.</span></span><span>alloc</span><span>(</span><span>10</span><span>);       </span><span>// 创建 10 字节缓冲区</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>buffer</span><span> </span><span>=</span><span><span> </span><span>Buffer</span><span>.</span></span><span>from</span><span>(</span><span>'hello'</span><span>);   </span><span>// 从字符串创建</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>buffer</span><span> </span><span>=</span><span><span> </span><span>Buffer</span><span>.</span></span><span>from</span><span>([</span><span>1</span><span>, </span><span>2</span><span>, </span><span>3</span><span>]);  </span><span>// 从数组创建</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 编码转换</span></div></div><div><div><div>6</div></div><div><span><span>buffer</span><span>.</span></span><span>toString</span><span>(</span><span>'utf8'</span><span>);</span></div></div><div><div><div>7</div></div><div><span><span>buffer</span><span>.</span></span><span>toString</span><span>(</span><span>'hex'</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>buffer</span><span>.</span></span><span>toString</span><span>(</span><span>'base64'</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 写入</span></div></div><div><div><div>11</div></div><div><span><span>buffer</span><span>.</span></span><span>write</span><span>(</span><span>'hello'</span><span>, </span><span>0</span><span>, </span><span>5</span><span>, </span><span>'utf8'</span><span>);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 拼接</span></div></div><div><div><div>14</div></div><div><span><span>Buffer</span><span>.</span></span><span>concat</span><span><span>([</span><span>buf1</span><span>, </span><span>buf2</span><span>, </span><span>buf3</span><span>]);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 复制</span></div></div><div><div><div>17</div></div><div><span><span>buf</span><span>.</span></span><span>copy</span><span><span>(</span><span>targetBuf</span><span>, </span><span>targetStart</span><span>, </span><span>sourceStart</span><span>, </span><span>sourceEnd</span><span>);</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 比较</span></div></div><div><div><div>20</div></div><div><span><span>buffer</span><span>.</span></span><span>compare</span><span><span>(</span><span>otherBuffer</span><span>);</span></span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>// 切片</span></div></div><div><div><div>23</div></div><div><span><span>buffer</span><span>.</span></span><span>slice</span><span>(</span><span>0</span><span>, </span><span>5</span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>// 长度</span></div></div><div><div><div>26</div></div><div><span><span>buffer</span><span>.</span></span><span>length</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>stream - 流<a href="#stream---流"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> { </span><span>Readable</span><span>, </span><span>Writable</span><span>, </span><span>Transform</span><span>, </span><span>Duplex</span><span>, </span><span>pipeline</span><span> } </span><span>=</span><span> </span><span>require</span><span>(</span><span>'stream'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>fs</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'fs'</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 读取流</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>readStream</span><span> </span><span>=</span><span><span> </span><span>fs</span><span>.</span></span><span>createReadStream</span><span>(</span><span>'./file.txt'</span><span>);</span></div></div><div><div><div>6</div></div><div><span><span>readStream</span><span>.</span></span><span>on</span><span>(</span><span>'data'</span><span><span>, </span><span>chunk</span><span> </span></span><span>=&gt;</span><span> { </span><span>/* 处理数据块 */</span><span> });</span></div></div><div><div><div>7</div></div><div><span><span>readStream</span><span>.</span></span><span>on</span><span>(</span><span>'end'</span><span>, () </span><span>=&gt;</span><span> { </span><span>/* 读取完成 */</span><span> });</span></div></div><div><div><div>8</div></div><div><span><span>readStream</span><span>.</span></span><span>on</span><span>(</span><span>'error'</span><span><span>, </span><span>err</span><span> </span></span><span>=&gt;</span><span> { </span><span>/* 错误处理 */</span><span> });</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 写入流</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>writeStream</span><span> </span><span>=</span><span><span> </span><span>fs</span><span>.</span></span><span>createWriteStream</span><span>(</span><span>'./output.txt'</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>writeStream</span><span>.</span></span><span>write</span><span>(</span><span>'hello'</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>writeStream</span><span>.</span></span><span>end</span><span>();</span></div></div><div><div><div>14</div></div><div><span><span>writeStream</span><span>.</span></span><span>on</span><span>(</span><span>'finish'</span><span>, () </span><span>=&gt;</span><span> { </span><span>/* 写入完成 */</span><span> });</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 管道 (pipe)</span></div></div><div><div><div>17</div></div><div><span><span>readStream</span><span>.</span></span><span>pipe</span><span><span>(</span><span>writeStream</span><span>);</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// pipeline (更好的错误处理)</span></div></div><div><div><div>20</div></div><div><span>pipeline</span><span>(</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>fs</span><span>.</span><span>createReadStream</span><span>(</span><span>'./file.txt'</span><span>),</span></div></div><div><div><div>22</div></div><div><span>  </span><span>gzip</span><span>(),</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>fs</span><span>.</span><span>createWriteStream</span><span>(</span><span>'./file.txt.gz'</span><span>),</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>(</span><span>err</span><span>) </span><span>=&gt;</span><span> { </span><span>if</span><span><span> (</span><span>err</span><span>) </span><span>console</span><span>.</span></span><span>error</span><span>(</span><span>'失败:'</span><span><span>, </span><span>err</span><span>); }</span></span></div></div><div><div><div>25</div></div><div><span>);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// Transform 流</span></div></div><div><div><div>28</div></div><div><span>const</span><span> </span><span>upperCase</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Transform</span><span>({</span></div></div><div><div><div>29</div></div><div><span>  </span><span>transform</span><span><span>(</span><span>chunk</span><span>, </span><span>encoding</span><span>, </span><span>callback</span><span>) {</span></span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>push</span><span><span>(</span><span>chunk</span><span>.</span></span><span>toString</span><span>().</span><span>toUpperCase</span><span>());</span></div></div><div><div><div>31</div></div><div><span>    </span><span>callback</span><span>();</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>33</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>http / https - HTTP 服务器<a href="#http--https---http-服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>http</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'http'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>https</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'https'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>fs</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'fs'</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 创建 HTTP 服务器</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>server</span><span> </span><span>=</span><span><span> </span><span>http</span><span>.</span></span><span>createServer</span><span><span>((</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>statusCode</span><span> </span><span>=</span><span> </span><span>200</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/plain'</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>end</span><span>(</span><span>'Hello World'</span><span>);</span></div></div><div><div><div>10</div></div><div><span>});</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>server</span><span>.</span></span><span>listen</span><span>(</span><span>3000</span><span>, </span><span>'127.0.0.1'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'服务器运行在 http://127.0.0.1:3000'</span><span>);</span></div></div><div><div><div>14</div></div><div><span>});</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// HTTP 请求</span></div></div><div><div><div>17</div></div><div><span><span>http</span><span>.</span></span><span>get</span><span>(</span><span>'http://api.example.com/data'</span><span><span>, (</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span>  </span><span>let</span><span><span> </span><span>data</span><span> </span></span><span>=</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>on</span><span>(</span><span>'data'</span><span><span>, </span><span>chunk</span><span> </span></span><span>=&gt;</span><span><span> { </span><span>data</span><span> </span></span><span>+=</span><span><span> </span><span>chunk</span><span>; });</span></span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>on</span><span>(</span><span>'end'</span><span>, () </span><span>=&gt;</span><span><span> { </span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>data</span><span>); });</span></span></div></div><div><div><div>21</div></div><div><span>});</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// HTTPS 请求</span></div></div><div><div><div>24</div></div><div><span>const</span><span> </span><span>options</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>25</div></div><div><span>  </span><span>hostname</span><span>:</span><span> </span><span>'api.example.com'</span><span>,</span></div></div><div><div><div>26</div></div><div><span>  </span><span>port</span><span>:</span><span> </span><span>443</span><span>,</span></div></div><div><div><div>27</div></div><div><span>  </span><span>path</span><span>:</span><span> </span><span>'/data'</span><span>,</span></div></div><div><div><div>28</div></div><div><span>  </span><span>method</span><span>:</span><span> </span><span>'GET'</span></div></div><div><div><div>29</div></div><div><span>};</span></div></div><div><div><div>30</div></div><div><span><span>https</span><span>.</span></span><span>request</span><span><span>(</span><span>options</span><span>, (</span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>  </span><span>// 处理响应</span></div></div><div><div><div>32</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>url - URL 解析<a href="#url---url-解析"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>url</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'url'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 解析 URL</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>myURL</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URL</span><span>(</span><span>'https://user:pass@host.com:8080/path?query=value#hash'</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>href</span><span>);          </span><span>// 完整 URL</span></div></div><div><div><div>7</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>protocol</span><span>);      </span><span>// 'https:'</span></div></div><div><div><div>8</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>host</span><span>);           </span><span>// 'host.com:8080'</span></div></div><div><div><div>9</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>hostname</span><span>);       </span><span>// 'host.com'</span></div></div><div><div><div>10</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>port</span><span>);          </span><span>// '8080'</span></div></div><div><div><div>11</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>pathname</span><span>);       </span><span>// '/path'</span></div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>search</span><span>);         </span><span>// '?query=value'</span></div></div><div><div><div>13</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>searchParams</span><span>);  </span><span>// URLSearchParams</span></div></div><div><div><div>14</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>hash</span><span>);          </span><span>// '#hash'</span></div></div><div><div><div>15</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>username</span><span>);      </span><span>// 'user'</span></div></div><div><div><div>16</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>myURL</span><span>.</span></span><span>password</span><span>);      </span><span>// 'pass'</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>// URLSearchParams</span></div></div><div><div><div>19</div></div><div><span>const</span><span> </span><span>params</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>URLSearchParams</span><span><span>(</span><span>myURL</span><span>.</span></span><span>search</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>params</span><span>.</span></span><span>get</span><span>(</span><span>'query'</span><span>);              </span><span>// 'value'</span></div></div><div><div><div>21</div></div><div><span><span>params</span><span>.</span></span><span>set</span><span>(</span><span>'query'</span><span>, </span><span>'newvalue'</span><span>);</span></div></div><div><div><div>22</div></div><div><span><span>params</span><span>.</span></span><span>append</span><span>(</span><span>'page'</span><span>, </span><span>'1'</span><span>);</span></div></div><div><div><div>23</div></div><div><span><span>params</span><span>.</span></span><span>has</span><span>(</span><span>'query'</span><span>);              </span><span>// true</span></div></div><div><div><div>24</div></div><div><span><span>params</span><span>.</span></span><span>delete</span><span>(</span><span>'query'</span><span>);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>// 旧式 url.parse (已废弃)</span></div></div><div><div><div>27</div></div><div><span><span>url</span><span>.</span></span><span>parse</span><span>(</span><span>'https://example.com/path?query=value'</span><span>, </span><span>true</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>querystring - 查询字符串<a href="#querystring---查询字符串"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>querystring</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'querystring'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 解析查询字符串</span></div></div><div><div><div>4</div></div><div><span><span>querystring</span><span>.</span></span><span>parse</span><span>(</span><span>'name=test&amp;age=18'</span><span>);</span></div></div><div><div><div>5</div></div><div><span>// → { name: 'test', age: '18' }</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 构建查询字符串</span></div></div><div><div><div>8</div></div><div><span><span>querystring</span><span>.</span></span><span>stringify</span><span>({ </span><span>name</span><span>:</span><span> </span><span>'test'</span><span>, </span><span>age</span><span>:</span><span> </span><span>18</span><span> });</span></div></div><div><div><div>9</div></div><div><span>// → 'name=test&amp;age=18'</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 编码</span></div></div><div><div><div>12</div></div><div><span><span>querystring</span><span>.</span></span><span>escape</span><span>(</span><span>'name=test&amp;age=18'</span><span>);</span></div></div><div><div><div>13</div></div><div><span>// → 'name%3Dtest%26age%3D18'</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 解码</span></div></div><div><div><div>16</div></div><div><span><span>querystring</span><span>.</span></span><span>unescape</span><span>(</span><span>'name%3Dtest'</span><span>);</span></div></div><div><div><div>17</div></div><div><span>// → 'name=test'</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>zlib - 压缩<a href="#zlib---压缩"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>zlib</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'zlib'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// gzip 压缩</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>gzip</span><span> </span><span>=</span><span><span> </span><span>zlib</span><span>.</span></span><span>createGzip</span><span>();</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>inp</span><span> </span><span>=</span><span><span> </span><span>fs</span><span>.</span></span><span>createReadStream</span><span>(</span><span>'./file.txt'</span><span>);</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>out</span><span> </span><span>=</span><span><span> </span><span>fs</span><span>.</span></span><span>createWriteStream</span><span>(</span><span>'./file.txt.gz'</span><span>);</span></div></div><div><div><div>7</div></div><div><span><span>inp</span><span>.</span></span><span>pipe</span><span><span>(</span><span>gzip</span><span>).</span></span><span>pipe</span><span><span>(</span><span>out</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 压缩数据</span></div></div><div><div><div>10</div></div><div><span><span>zlib</span><span>.</span></span><span>gzipSync</span><span><span>(</span><span>Buffer</span><span>.</span></span><span>from</span><span>(</span><span>'hello'</span><span>));</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// 解压数据</span></div></div><div><div><div>13</div></div><div><span><span>zlib</span><span>.</span></span><span>unzipSync</span><span><span>(</span><span>Buffer</span><span>.</span></span><span>from</span><span><span>(</span><span>gzipData</span><span>));</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 其他压缩格式</span></div></div><div><div><div>16</div></div><div><span><span>zlib</span><span>.</span></span><span>deflateSync</span><span>();   </span><span>// deflate</span></div></div><div><div><div>17</div></div><div><span><span>zlib</span><span>.</span></span><span>deflateRawSync</span><span>(); </span><span>// raw deflate</span></div></div><div><div><div>18</div></div><div><span><span>zlib</span><span>.</span></span><span>brotli</span><span>*</span><span>();       </span><span>// brotli 压缩</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>net - TCP 服务器<a href="#net---tcp-服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>net</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'net'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 创建 TCP 服务器</span></div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>server</span><span> </span><span>=</span><span><span> </span><span>net</span><span>.</span></span><span>createServer</span><span><span>((</span><span>socket</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'客户端连接:'</span><span><span>, </span><span>socket</span><span>.</span></span><span>remoteAddress</span><span>);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>socket</span><span>.</span><span>on</span><span>(</span><span>'data'</span><span><span>, (</span><span>data</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'收到数据:'</span><span><span>, </span><span>data</span><span>.</span></span><span>toString</span><span>());</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>socket</span><span>.</span><span>write</span><span>(</span><span>'服务器收到'</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>socket</span><span>.</span><span>on</span><span>(</span><span>'end'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'客户端断开'</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>socket</span><span>.</span><span>write</span><span>(</span><span>'欢迎连接'</span><span>);</span></div></div><div><div><div>17</div></div><div><span>});</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>server</span><span>.</span></span><span>listen</span><span>(</span><span>8080</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'TCP 服务器监听 8080 端口'</span><span>);</span></div></div><div><div><div>21</div></div><div><span>});</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 创建 TCP 客户端</span></div></div><div><div><div>24</div></div><div><span>const</span><span> </span><span>client</span><span> </span><span>=</span><span><span> </span><span>net</span><span>.</span></span><span>createConnection</span><span>(</span><span>8080</span><span>, </span><span>'127.0.0.1'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'已连接到服务器'</span><span>);</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>client</span><span>.</span><span>write</span><span>(</span><span>'Hello Server'</span><span>);</span></div></div><div><div><div>27</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>dns - 域名解析<a href="#dns---域名解析"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>dns</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'dns'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>dnsPromises</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'dns'</span><span>).</span><span>promises</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 解析域名</span></div></div><div><div><div>5</div></div><div><span><span>dns</span><span>.</span></span><span>resolve4</span><span>(</span><span>'example.com'</span><span><span>, (</span><span>err</span><span>, </span><span>addresses</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span><span> (</span><span>err</span><span>) </span></span><span>throw</span><span><span> </span><span>err</span><span>;</span></span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'IPv4 地址:'</span><span><span>, </span><span>addresses</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span>});</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>dns</span><span>.</span></span><span>resolve6</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>dns</span><span>.</span></span><span>resolveMx</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>dns</span><span>.</span></span><span>resolveTxt</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>dns</span><span>.</span></span><span>resolveNs</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>dns</span><span>.</span></span><span>resolveCname</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>15</div></div><div><span><span>dns</span><span>.</span></span><span>resolveSoa</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>dns</span><span>.</span></span><span>resolveSrv</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>17</div></div><div><span><span>dns</span><span>.</span></span><span>reverse</span><span>(</span><span>'93.184.216.34'</span><span>);</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// Promise 风格</span></div></div><div><div><div>20</div></div><div><span>(</span><span>async</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>21</div></div><div><span>  </span><span>const</span><span> </span><span>addresses</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>dnsPromises</span><span>.</span></span><span>resolve4</span><span>(</span><span>'example.com'</span><span>);</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>addresses</span><span>);</span></span></div></div><div><div><div>23</div></div><div><span>})();</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>// 反向解析</span></div></div><div><div><div>26</div></div><div><span><span>dns</span><span>.</span></span><span>reverse</span><span>(</span><span>'93.184.216.34'</span><span>);</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>// 查找</span></div></div><div><div><div>29</div></div><div><span><span>dns</span><span>.</span></span><span>lookup</span><span>(</span><span>'example.com'</span><span><span>, (</span><span>err</span><span>, </span><span>address</span><span>, </span><span>family</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'地址:'</span><span><span>, </span><span>address</span><span>, </span></span><span>'类型:'</span><span><span>, </span><span>family</span><span>); </span></span><span>// family: 4 或 6</span></div></div><div><div><div>31</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>process - 进程信息<a href="#process---进程信息"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 进程信息</span></div></div><div><div><div>2</div></div><div><span><span>process</span><span>.</span></span><span>version</span><span>;           </span><span>// Node.js 版本</span></div></div><div><div><div>3</div></div><div><span><span>process</span><span>.</span></span><span>versions</span><span>;          </span><span>// 依赖库版本</span></div></div><div><div><div>4</div></div><div><span><span>process</span><span>.</span></span><span>platform</span><span>;          </span><span>// 平台 'linux', 'darwin', 'win32'</span></div></div><div><div><div>5</div></div><div><span><span>process</span><span>.</span></span><span>arch</span><span>;              </span><span>// 架构 'x64', 'arm64'</span></div></div><div><div><div>6</div></div><div><span><span>process</span><span>.</span></span><span>title</span><span>;             </span><span>// 进程标题</span></div></div><div><div><div>7</div></div><div><span><span>process</span><span>.</span></span><span>pid</span><span>;               </span><span>// 进程 ID</span></div></div><div><div><div>8</div></div><div><span><span>process</span><span>.</span></span><span>ppid</span><span>;              </span><span>// 父进程 ID</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 内存使用</span></div></div><div><div><div>11</div></div><div><span><span>process</span><span>.</span></span><span>memoryUsage</span><span>();</span></div></div><div><div><div>12</div></div><div><span>// { rss: 23MB, heapTotal: 10MB, heapUsed: 7MB, external: 1MB }</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>process</span><span>.</span></span><span>memoryUsage</span><span>().</span><span>heapUsed</span><span>;  </span><span>// 堆内存使用量</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// CPU 使用</span></div></div><div><div><div>17</div></div><div><span><span>process</span><span>.</span></span><span>cpuUsage</span><span>();</span></div></div><div><div><div>18</div></div><div><span>// { user: 10000, system: 5000 }</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 运行时间</span></div></div><div><div><div>21</div></div><div><span><span>process</span><span>.</span></span><span>uptime</span><span>();          </span><span>// 进程运行秒数</span></div></div><div><div><div>22</div></div><div><span><span>process</span><span>.</span></span><span>hrtime</span><span>.</span><span>bigint</span><span>();   </span><span>// 高精度时间 (纳秒)</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>// 环境变量</span></div></div><div><div><div>25</div></div><div><span><span>process</span><span>.</span></span><span>env</span><span>;               </span><span>// 环境变量对象</span></div></div><div><div><div>26</div></div><div><span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>NODE_ENV</span><span>;     </span><span>// 'development', 'production'</span></div></div><div><div><div>27</div></div><div><span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>PATH</span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>HOME</span><span>;</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>// 命令行参数</span></div></div><div><div><div>31</div></div><div><span><span>process</span><span>.</span></span><span>argv</span><span>;             </span><span>// ['node', 'script.js', 'arg1', 'arg2']</span></div></div><div><div><div>32</div></div><div><span><span>process</span><span>.</span></span><span>execArgv</span><span>;         </span><span>// Node 特殊参数</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>// 工作目录</span></div></div><div><div><div>35</div></div><div><span><span>process</span><span>.</span></span><span>cwd</span><span>();             </span><span>// 当前工作目录</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>// 用户信息</span></div></div><div><div><div>38</div></div><div><span><span>process</span><span>.</span></span><span>getuid</span><span>();          </span><span>// 用户 ID</span></div></div><div><div><div>39</div></div><div><span><span>process</span><span>.</span></span><span>getgid</span><span>();          </span><span>// 组 ID</span></div></div><div><div><div>40</div></div><div><span><span>process</span><span>.</span></span><span>groups</span><span>;            </span><span>// 附加组 ID</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>// 信号</span></div></div><div><div><div>43</div></div><div><span><span>process</span><span>.</span></span><span>on</span><span>(</span><span>'SIGINT'</span><span>, () </span><span>=&gt;</span><span> { </span><span>/* 处理 Ctrl+C */</span><span> });</span></div></div><div><div><div>44</div></div><div><span><span>process</span><span>.</span></span><span>on</span><span>(</span><span>'SIGTERM'</span><span>, () </span><span>=&gt;</span><span> { </span><span>/* 处理终止信号 */</span><span> });</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>// 退出</span></div></div><div><div><div>47</div></div><div><span><span>process</span><span>.</span></span><span>exit</span><span>(</span><span>0</span><span>);           </span><span>// 正常退出</span></div></div><div><div><div>48</div></div><div><span><span>process</span><span>.</span></span><span>exit</span><span>(</span><span>1</span><span>);           </span><span>// 异常退出</span></div></div><div><div><div>49</div></div><div><span><span>process</span><span>.</span></span><span>kill</span><span><span>(</span><span>pid</span><span>, </span></span><span>'SIGTERM'</span><span>);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>// 下一轮事件循环</span></div></div><div><div><div>52</div></div><div><span><span>process</span><span>.</span></span><span>nextTick</span><span>(() </span><span>=&gt;</span><span> { </span><span>/* 异步执行 */</span><span> });</span></div></div><div><div><div>53</div></div><div><span><span>process</span><span>.</span></span><span>setImmediate</span><span>(() </span><span>=&gt;</span><span> { </span><span>/* 立即执行 */</span><span> });</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>// 错误传播</span></div></div><div><div><div>56</div></div><div><span><span>process</span><span>.</span></span><span>on</span><span>(</span><span>'uncaughtException'</span><span><span>, (</span><span>err</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>57</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'未捕获异常:'</span><span><span>, </span><span>err</span><span>);</span></span></div></div><div><div><div>58</div></div><div><span>});</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span><span>process</span><span>.</span></span><span>on</span><span>(</span><span>'unhandledRejection'</span><span><span>, (</span><span>reason</span><span>, </span><span>promise</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>61</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'未处理的 Promise 拒绝:'</span><span><span>, </span><span>reason</span><span>);</span></span></div></div><div><div><div>62</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>环境配置<a href="#环境配置"><span>#</span></a></h2><section><h3>npm 镜像源配置<a href="#npm-镜像源配置"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看当前镜像</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>get</span><span> </span><span>registry</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 设置淘宝镜像</span></div></div><div><div><div>5</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npmmirror.com</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 设置官方镜像</span></div></div><div><div><div>8</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npmjs.org</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 还原默认镜像</span></div></div><div><div><div>11</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npmjs.org</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>nrm - 镜像源管理器<a href="#nrm---镜像源管理器"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 nrm</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>nrm</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 查看可用镜像</span></div></div><div><div><div>5</div></div><div><span>nrm</span><span> </span><span>ls</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 切换镜像</span></div></div><div><div><div>8</div></div><div><span>nrm</span><span> </span><span>use</span><span> </span><span>taobao</span></div></div><div><div><div>9</div></div><div><span>nrm</span><span> </span><span>use</span><span> </span><span>npm</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># 添加镜像</span></div></div><div><div><div>12</div></div><div><span>nrm</span><span> </span><span>add</span><span> </span><span>&lt;name&gt;</span><span> </span><span>&lt;url&gt;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 删除镜像</span></div></div><div><div><div>15</div></div><div><span>nrm</span><span> </span><span>del</span><span> </span><span>&lt;name&gt;</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span># 测试镜像速度</span></div></div><div><div><div>18</div></div><div><span>nrm</span><span> </span><span>test</span></div></div><div><div><div>19</div></div><div><span>nrm</span><span> </span><span>test</span><span> </span><span>taobao</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span># 查看当前使用的镜像</span></div></div><div><div><div>22</div></div><div><span>nrm</span><span> </span><span>current</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>cnpm - 淘宝镜像客户端<a href="#cnpm---淘宝镜像客户端"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 cnpm</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>cnpm</span><span> </span><span>--registry=https://registry.npmmirror.com</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用 cnpm 代替 npm</span></div></div><div><div><div>5</div></div><div><span>cnpm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span></div></div><div><div><div>6</div></div><div><span>cnpm</span><span> </span><span>install</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 或使用 npx 直接运行 cnpm</span></div></div><div><div><div>9</div></div><div><span>npx</span><span> </span><span>cnpm</span><span> </span><span>install</span><span> </span><span>&lt;package&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>.npmrc 配置文件<a href="#npmrc-配置文件"><span>#</span></a></h3><p>在用户主目录或项目根目录创建 <code>.npmrc</code> 文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 项目级 .npmrc</span></div></div><div><div><div>2</div></div><div><span>registry</span><span><span>=</span><span>https://registry.npmmirror.com</span></span></div></div><div><div><div>3</div></div><div><span>prefix</span><span><span>=</span><span>C:\Users\username\.npm-global</span></span></div></div><div><div><div>4</div></div><div><span>cache</span><span><span>=</span><span>C:\Users\username\.npm-cache</span></span></div></div><div><div><div>5</div></div><div><span>tmp</span><span><span>=</span><span>C:\Users\username\.npm-tmp</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 用户级 .npmrc (~/.npmrc)</span></div></div><div><div><div>2</div></div><div><span>registry</span><span><span>=</span><span>https://registry.npmmirror.com</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>n 环境管理 (Node 版本切换)<a href="#n-环境管理-node-版本切换"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 全局安装 n</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>n</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装稳定版</span></div></div><div><div><div>5</div></div><div><span>n</span><span> </span><span>stable</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 安装最新版</span></div></div><div><div><div>8</div></div><div><span>n</span><span> </span><span>latest</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 安装 LTS 版</span></div></div><div><div><div>11</div></div><div><span>n</span><span> </span><span>lts</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 安装指定版本</span></div></div><div><div><div>14</div></div><div><span>n</span><span> </span><span>18.17.0</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 切换版本</span></div></div><div><div><div>17</div></div><div><span>n</span><span> </span><span>16.20.0</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span># 列出已安装版本</span></div></div><div><div><div>20</div></div><div><span>n</span><span> </span><span>list</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span># 显示可安装版本</span></div></div><div><div><div>23</div></div><div><span>n</span><span> </span><span>list</span><span> </span><span>--all</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span># 移除版本</span></div></div><div><div><div>26</div></div><div><span>n</span><span> </span><span>rm</span><span> </span><span>14.20.0</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span># 查看当前版本</span></div></div><div><div><div>29</div></div><div><span>n</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span># 使用指定版本执行脚本</span></div></div><div><div><div>32</div></div><div><span>n</span><span> </span><span>use</span><span> </span><span>18.17.0</span><span> </span><span>node</span><span> </span><span>script.js</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>常见问题<a href="#常见问题"><span>#</span></a></h2><section><h3>安装失败<a href="#安装失败"><span>#</span></a></h3><p><strong>问题：npm 安装报错 EACCES (权限拒绝)</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 解决方案：修改 npm 全局目录权限</span></div></div><div><div><div>2</div></div><div><span>mkdir</span><span> </span><span>~/.npm-global</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>prefix</span><span> </span><span>'~/.npm-global'</span></div></div><div><div><div>4</div></div><div><span>echo</span><span> </span><span>'export PATH=~/.npm-global/bin:$PATH'</span><span> &gt;&gt; </span><span>~/.bashrc</span></div></div><div><div><div>5</div></div><div><span>source</span><span> </span><span>~/.bashrc</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>问题：安装卡住不动</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 清理缓存后重试</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>cache</span><span> </span><span>clean</span><span> </span><span>--force</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>install</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 或使用淘宝镜像</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>--registry=https://registry.npmmirror.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>版本问题<a href="#版本问题"><span>#</span></a></h3><p><strong>Node.js 和 npm 版本不匹配</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看版本对应关系</span></div></div><div><div><div>2</div></div><div><span># https://nodejs.org/zh-cn/download/releases/</span></div></div><div><div><div>3</div></div><div><span># https://nodejs.org/en/download/releases/</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 升级 npm</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>npm</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 升级 Node.js (使用 nvm)</span></div></div><div><div><div>9</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>lts</span></div></div><div><div><div>10</div></div><div><span>nvm</span><span> </span><span>use</span><span> </span><span>lts</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>权限问题<a href="#权限问题"><span>#</span></a></h3><p><strong>问题：全局安装报 EACCES</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 方法 1：使用 nvm 管理 Node</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-o-</span><span> </span><span>https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh</span><span> | </span><span>bash</span></div></div><div><div><div>3</div></div><div><span>nvm</span><span> </span><span>install</span><span> </span><span>--lts</span></div></div><div><div><div>4</div></div><div><span>nvm</span><span> </span><span>use</span><span> </span><span>--lts</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 方法 2：修复 npm 目录权限</span></div></div><div><div><div>7</div></div><div><span>sudo</span><span> </span><span>chown</span><span> </span><span>-R</span><span> $(</span><span>whoami</span><span>) </span><span>~/.npm</span></div></div><div><div><div>8</div></div><div><span>sudo</span><span> </span><span>chown</span><span> </span><span>-R</span><span> $(</span><span>whoami</span><span>) </span><span>/usr/local/lib/node_modules</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>网络问题<a href="#网络问题"><span>#</span></a></h3><p><strong>问题：下载慢或超时</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 设置国内镜像</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>registry</span><span> </span><span>https://registry.npmmirror.com</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 或使用代理</span></div></div><div><div><div>5</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>proxy</span><span> </span><span>http://proxy.company.com:8080</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>https-proxy</span><span> </span><span>http://proxy.company.com:8080</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 取消代理</span></div></div><div><div><div>9</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>delete</span><span> </span><span>proxy</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>config</span><span> </span><span>delete</span><span> </span><span>https-proxy</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>其他问题<a href="#其他问题"><span>#</span></a></h3><p><strong>Windows 下找不到 node_modules/.bin</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 确保在项目根目录执行</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>project-dir</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>install</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># Windows 下可能需要使用 Git Bash 或 WSL</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>删除 node_modules</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用 rimraf (推荐)</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>rimraf</span></div></div><div><div><div>3</div></div><div><span>rimraf</span><span> </span><span>node_modules</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 或手动删除</span></div></div><div><div><div>6</div></div><div><span>rm</span><span> </span><span>-rf</span><span> </span><span>node_modules</span><span>      </span><span># Linux/macOS</span></div></div><div><div><div>7</div></div><div><span>rd</span><span> </span><span>/s</span><span> </span><span>/q</span><span> </span><span>node_modules</span><span>     </span><span># Windows CMD</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>npm link 使用</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 在开发包目录创建链接</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>package-dir</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>link</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 在项目中使用链接</span></div></div><div><div><div>6</div></div><div><span>cd</span><span> </span><span>project-dir</span></div></div><div><div><div>7</div></div><div><span>npm</span><span> </span><span>link</span><span> </span><span>package-name</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 取消链接</span></div></div><div><div><div>10</div></div><div><span>npm</span><span> </span><span>unlink</span><span> </span><span>package-name</span></div></div><div><div><div>11</div></div><div><span>npm</span><span> </span><span>unlink</span><span> </span><span>-g</span><span> </span><span>package-name</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>相关资源<a href="#相关资源"><span>#</span></a></h2><ul>
<li><a href="https://nodejs.org/" target="_blank">Node.js 官网</a></li>
<li><a href="https://www.npmjs.com/" target="_blank">npm 官网</a></li>
<li><a href="https://nodejs.cn/" target="_blank">Node.js 中文文档</a></li>
<li><a href="https://www.npmjs.cn/" target="_blank">npm 中文文档</a></li>
<li><a href="https://github.com/coreybutler/nvm-windows/releases" target="_blank">nvm-windows releases</a></li>
<li><a href="https://nodejs.org/zh-cn/download/releases/" target="_blank">Node.js 版本列表</a></li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/sse-technology/</id>
      <title type="text">🔄 Server-Sent Events (SSE) 技术详解</title>
      <published>2026-04-20T00:00:00.000Z</published>
      <updated>2026-04-20T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/sse-technology/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>Server-Sent Events (SSE) 技术详解<a href="#server-sent-events-sse-技术详解"><span>#</span></a></h1><section><h2>目录<a href="#目录"><span>#</span></a></h2><ul>
<li><a href="#sse-%E7%AE%80%E4%BB%8B">SSE 简介</a></li>
<li><a href="#sse-vs-websocket-vs-long-polling">SSE vs WebSocket vs Long Polling</a></li>
<li><a href="#sse-%E5%8D%8F%E8%AE%AE%E6%A0%BC%E5%BC%8F">SSE 协议格式</a></li>
<li><a href="#eventsource-api">EventSource API</a></li>
<li><a href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%AE%9E%E7%8E%B0">服务端实现</a></li>
<li><a href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7">浏览器兼容性</a></li>
<li><a href="#%E9%87%8D%E8%BF%9E%E6%9C%BA%E5%88%B6">重连机制</a></li>
<li><a href="#%E5%AD%97%E6%AE%B5%E8%AF%B4%E6%98%8E">字段说明</a></li>
<li><a href="#%E5%AE%89%E5%85%A8%E8%80%83%E8%99%91">安全考虑</a></li>
<li><a href="#%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF">实际应用场景</a></li>
<li><a href="#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">性能优化</a></li>
<li><a href="#%E5%AE%8C%E6%95%B4%E7%A4%BA%E4%BE%8B">完整示例</a></li>
<li><a href="#%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7">调试工具</a></li>
<li><a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98">常见问题</a></li>
</ul><hr /></section><section><h2>SSE 简介<a href="#sse-简介"><span>#</span></a></h2><p>Server-Sent Events (SSE) 是一种基于 HTTP 的单向通信技术，允许服务器主动向浏览器推送数据。SSE 是 HTML5 规范的一部分，提供了一种简单的方式来获取服务器的实时更新。</p><section><h3>核心特点<a href="#核心特点"><span>#</span></a></h3><ul>
<li><strong>单向通信</strong>: 服务器 → 客户端，客户端不能向服务器发送数据</li>
<li><strong>基于 HTTP/1.1</strong>: 使用 HTTP 协议，兼容现有网络架构</li>
<li><strong>自动重连</strong>: 浏览器自动维护连接并在断线时重连</li>
<li><strong>文本协议</strong>: 数据格式简单，是 UTF-8 编码的文本</li>
<li><strong>轻量级</strong>: 相比 WebSocket 协议更简单，开销更小</li>
</ul></section><section><h3>工作原理<a href="#工作原理"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────┐                    ┌─────────┐                    ┌─────────┐</span></div></div><div><div><div>2</div></div><div><span>│ Browser │ ── HTTP Request ──▶│ Server  │                    │         │</span></div></div><div><div><div>3</div></div><div><span>│         │◀─ Keep-Alive ──────│         │◀── Data Stream ────┤  Data   │</span></div></div><div><div><div>4</div></div><div><span>│         │◀─ text/event-stream│         │                    │  Source │</span></div></div><div><div><div>5</div></div><div><span>│         │                    │         │                    │         │</span></div></div><div><div><div>6</div></div><div><span>│         │    (持久连接)        │         │                    │         │</span></div></div><div><div><div>7</div></div><div><span>└─────────┘                    └─────────┘                    └─────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>客户端通过 <code>EventSource</code> API 发起 HTTP 请求到服务器</li>
<li>服务器保持连接打开，不断发送事件</li>
<li>客户端接收事件并触发相应的 JavaScript 回调</li>
<li>连接断开时，浏览器自动尝试重连</li>
</ol><hr /></section></section><section><h2>SSE vs WebSocket vs Long Polling<a href="#sse-vs-websocket-vs-long-polling"><span>#</span></a></h2><section><h3>对比表<a href="#对比表"><span>#</span></a></h3>

<table><thead><tr><th>特性</th><th>SSE</th><th>WebSocket</th><th>Long Polling</th></tr></thead><tbody><tr><td><strong>通信方向</strong></td><td>单向 (服务器→客户端)</td><td>全双工 (双向)</td><td>半双工 (伪双向)</td></tr><tr><td><strong>协议</strong></td><td>HTTP/1.1</td><td>WebSocket (ws/wss)</td><td>HTTP</td></tr><tr><td><strong>连接数限制</strong></td><td>受浏览器同源连接限制</td><td>相同</td><td>每次轮询都是新连接</td></tr><tr><td><strong>数据格式</strong></td><td>UTF-8 文本</td><td>二进制/文本</td><td>任意</td></tr><tr><td><strong>自动重连</strong></td><td>内置支持</td><td>需要手动实现</td><td>无 (轮询间隔)</td></tr><tr><td><strong>CORS</strong></td><td>需要服务器配置</td><td>需要 WebSocket 服务器</td><td>需要服务器配置</td></tr><tr><td><strong>防火墙/代理</strong></td><td>友好</td><td>可能被阻止</td><td>友好</td></tr><tr><td><strong>复杂度</strong></td><td>低</td><td>中</td><td>低</td></tr><tr><td><strong>服务器资源</strong></td><td>较低 (持久连接)</td><td>较高 (全双工)</td><td>较高 (频繁建连)</td></tr><tr><td><strong>适合场景</strong></td><td>实时通知、推送更新</td><td>实时游戏、聊天</td><td>简单轮询需求</td></tr></tbody></table></section><section><h3>如何选择<a href="#如何选择"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>选择 SSE 的场景:</span></div></div><div><div><div>2</div></div><div><span>├── 只需要服务器向客户端推送数据</span></div></div><div><div><div>3</div></div><div><span>├── 需要通过 HTTP/HTTPS 传输</span></div></div><div><div><div>4</div></div><div><span>├── 需要自动重连功能</span></div></div><div><div><div>5</div></div><div><span>└── 需要兼容不支持 WebSocket 的环境</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>选择 WebSocket 的场景:</span></div></div><div><div><div>8</div></div><div><span>├── 需要双向通信</span></div></div><div><div><div>9</div></div><div><span>├── 传输延迟要求极低</span></div></div><div><div><div>10</div></div><div><span>├── 需要传输二进制数据</span></div></div><div><div><div>11</div></div><div><span>├── 高频数据交换</span></div></div><div><div><div>12</div></div><div><span>└── 实时游戏、在线协作、交易系统</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>选择 Long Polling 的场景:</span></div></div><div><div><div>15</div></div><div><span>├── 现有系统改造困难</span></div></div><div><div><div>16</div></div><div><span>├── 服务器不支持长连接</span></div></div><div><div><div>17</div></div><div><span>└── 数据更新频率很低</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>SSE 协议格式<a href="#sse-协议格式"><span>#</span></a></h2><section><h3>基本格式<a href="#基本格式"><span>#</span></a></h3><p>SSE 数据以 <code>field: value</code> 的形式传输，以两个换行符 <code>\n\n</code> 结束一个事件。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>field: value\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>字段说明<a href="#字段说明"><span>#</span></a></h3>

<table><thead><tr><th>字段名</th><th>说明</th></tr></thead><tbody><tr><td><code>event</code></td><td>事件类型，客户端可据此区分不同事件</td></tr><tr><td><code>data</code></td><td>事件数据，可以有多行</td></tr><tr><td><code>id</code></td><td>事件 ID，客户端发送 <code>Last-Event-ID</code> 用于断线重连</td></tr><tr><td><code>retry</code></td><td>重连时间（毫秒），覆盖默认重连时间</td></tr><tr><td><code>:</code></td><td>注释，以冒号开头，用于心跳或传输注释</td></tr></tbody></table></section><section><h3>单行事件示例<a href="#单行事件示例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>data: {"message": "Hello World"}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>多行数据<a href="#多行数据"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>data: {"status": "ok",</span></div></div><div><div><div>2</div></div><div><span>data: "message": "Processing complete"</span></div></div><div><div><div>3</div></div><div><span>data: }\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>带事件类型<a href="#带事件类型"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event: user-join\n</span></div></div><div><div><div>2</div></div><div><span>data: {"username": "Alice"}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>带事件 ID (用于重连)<a href="#带事件-id-用于重连"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>id: 123\n</span></div></div><div><div><div>2</div></div><div><span>data: {"message": "First event"}\n\n</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>id: 124\n</span></div></div><div><div><div>5</div></div><div><span>data: {"message": "Second event"}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>指定重连时间<a href="#指定重连时间"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>retry: 10000\n</span></div></div><div><div><div>2</div></div><div><span>data: {"message": "Set retry to 10 seconds"}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>注释行 (心跳)<a href="#注释行-心跳"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>: This is a comment\n</span></div></div><div><div><div>2</div></div><div><span>data: ping\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>完整事件示例<a href="#完整事件示例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event: notification\n</span></div></div><div><div><div>2</div></div><div><span>id: evt-001\n</span></div></div><div><div><div>3</div></div><div><span>retry: 5000\n</span></div></div><div><div><div>4</div></div><div><span>data: {"title": "New Message", "body": "You have a new notification", "timestamp": 1699000000}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>EventSource API<a href="#eventsource-api"><span>#</span></a></h2><section><h3>基本用法<a href="#基本用法"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建 EventSource 连接</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/events'</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 监听默认事件 (message)</span></div></div><div><div><div>5</div></div><div><span><span>eventSource</span><span>.</span></span><span>onmessage</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>event</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Received:'</span><span><span>, </span><span>event</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>7</div></div><div><span>};</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 监听自定义事件</span></div></div><div><div><div>10</div></div><div><span><span>eventSource</span><span>.</span></span><span>addEventListener</span><span>(</span><span>'notification'</span><span>, </span><span>function</span><span><span>(</span><span>event</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>  </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>event</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Notification:'</span><span><span>, </span><span>data</span><span>);</span></span></div></div><div><div><div>13</div></div><div><span>});</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 监听连接打开</span></div></div><div><div><div>16</div></div><div><span><span>eventSource</span><span>.</span></span><span>onopen</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>event</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Connection opened'</span><span>);</span></div></div><div><div><div>18</div></div><div><span>};</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 监听错误</span></div></div><div><div><div>21</div></div><div><span><span>eventSource</span><span>.</span></span><span>onerror</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>event</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>  </span><span>if</span><span><span> (</span><span>event</span><span>.</span></span><span>readyState</span><span> </span><span>===</span><span><span> </span><span>EventSource</span><span>.</span></span><span>CONNECTING</span><span>) {</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Reconnecting...'</span><span>);</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>event</span><span>.</span></span><span>readyState</span><span> </span><span>===</span><span><span> </span><span>EventSource</span><span>.</span></span><span>CLOSED</span><span>) {</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Connection closed'</span><span>);</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Error:'</span><span><span>, </span><span>event</span><span>);</span></span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>EventSource 属性<a href="#eventsource-属性"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/events'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>eventSource</span><span>.</span></span><span>url</span><span>);          </span><span>// 连接 URL</span></div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>eventSource</span><span>.</span></span><span>readyState</span><span>);  </span><span>// 连接状态</span></div></div><div><div><div>5</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>eventSource</span><span>.</span></span><span>withCredentials</span><span>); </span><span>// 是否携带凭证</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>readyState 状态<a href="#readystate-状态"><span>#</span></a></h3>

<table><thead><tr><th>常量</th><th>值</th><th>说明</th></tr></thead><tbody><tr><td><code>EventSource.CONNECTING</code></td><td>0</td><td>连接中或重连中</td></tr><tr><td><code>EventSource.OPEN</code></td><td>1</td><td>连接已打开</td></tr><tr><td><code>EventSource.CLOSED</code></td><td>2</td><td>连接已关闭</td></tr></tbody></table><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>switch</span><span><span> (</span><span>eventSource</span><span>.</span></span><span>readyState</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>case</span><span><span> </span><span>EventSource</span><span>.</span></span><span>CONNECTING</span><span>:</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Connecting...'</span><span>);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>break</span><span>;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>case</span><span><span> </span><span>EventSource</span><span>.</span></span><span>OPEN</span><span>:</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Connected'</span><span>);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>break</span><span>;</span></div></div><div><div><div>8</div></div><div><span>  </span><span>case</span><span><span> </span><span>EventSource</span><span>.</span></span><span>CLOSED</span><span>:</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Disconnected'</span><span>);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>break</span><span>;</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>关闭连接<a href="#关闭连接"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 手动关闭连接</span></div></div><div><div><div>2</div></div><div><span><span>eventSource</span><span>.</span></span><span>close</span><span>();</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>eventSource</span><span>.</span></span><span>readyState</span><span>); </span><span>// EventSource.CLOSED</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>携带凭证 (CORS)<a href="#携带凭证-cors"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// withCredentials 设置为 true 时，会携带 cookie、Authorization header 等</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/events'</span><span>, {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>withCredentials</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>4</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>断线重连与 Last-Event-ID<a href="#断线重连与-last-event-id"><span>#</span></a></h3><p>服务器可发送 <code>id</code> 字段标记事件 ID。当连接断开重连时，浏览器会自动在请求头中携带 <code>Last-Event-ID</code>，服务器据此可以继续推送未发送的事件。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 服务器端发送带 ID 的事件</span></div></div><div><div><div>2</div></div><div><span>// id: 123\n</span></div></div><div><div><div>3</div></div><div><span>// data: {"message": "Event 123"}\n\n</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 客户端断开后重连时，HTTP 请求头会包含:</span></div></div><div><div><div>6</div></div><div><span>// Last-Event-ID: 123</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 服务器收到后可以发送 124 开始的后续事件</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>服务端实现<a href="#服务端实现"><span>#</span></a></h2><section><h3>Node.js + Express<a href="#nodejs--express"><span>#</span></a></h3><div><div><div><figure><figcaption><span>server-express.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>express</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'express'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>app</span><span> </span><span>=</span><span> </span><span>express</span><span>();</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// SSE 路由</span></div></div><div><div><div>5</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/sse'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>// 设置 SSE 相关 headers</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Cache-Control'</span><span>, </span><span>'no-cache'</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Connection'</span><span>, </span><span>'keep-alive'</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Access-Control-Allow-Origin'</span><span>, </span><span>'*'</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>  </span><span>// 发送初始连接消息</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>'event: connected</span><span>\n</span><span>'</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>'data: {"status": "connected"}</span><span>\n\n</span><span>'</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>// 定时发送数据</span></div></div><div><div><div>17</div></div><div><span>  </span><span>const</span><span> </span><span>intervalId</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>timestamp</span><span>:</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>(),</span></div></div><div><div><div>20</div></div><div><span>      </span><span>random</span><span>:</span><span><span> </span><span>Math</span><span>.</span></span><span>random</span><span>().</span><span>toFixed</span><span>(</span><span>4</span><span>)</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: heartbeat</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>}, </span><span>5000</span><span>);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>  </span><span>// 模拟事件推送</span></div></div><div><div><div>28</div></div><div><span>  </span><span>const</span><span> </span><span>eventId</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>29</div></div><div><span>    </span><span>const</span><span> </span><span>messages</span><span> </span><span>=</span><span> [</span><span>'Hello!'</span><span>, </span><span>'New notification'</span><span>, </span><span>'System update'</span><span>];</span></div></div><div><div><div>30</div></div><div><span>    </span><span>const</span><span> </span><span>message</span><span> </span><span>=</span><span><span> </span><span>messages</span><span>[</span><span>Math</span><span>.</span></span><span>floor</span><span><span>(</span><span>Math</span><span>.</span></span><span>random</span><span>() </span><span>*</span><span><span> </span><span>messages</span><span>.</span></span><span>length</span><span>)];</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: notification</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`id: </span><span>${</span><span>Date</span><span>.</span><span>now</span><span>()</span><span>}</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: {"message": "</span><span>${</span><span>message</span><span>}</span><span>"}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>35</div></div><div><span><span>  </span></span><span>}, </span><span>15000</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>  </span><span>// 客户端断开连接时清理</span></div></div><div><div><div>38</div></div><div><span><span>  </span></span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>39</div></div><div><span>    </span><span>clearInterval</span><span><span>(</span><span>intervalId</span><span>);</span></span></div></div><div><div><div>40</div></div><div><span>    </span><span>clearInterval</span><span><span>(</span><span>eventId</span><span>);</span></span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Client disconnected'</span><span>);</span></div></div><div><div><div>42</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>43</div></div><div><span>});</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span><span>app</span><span>.</span></span><span>listen</span><span>(</span><span>3000</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>46</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Server running on http://localhost:3000'</span><span>);</span></div></div><div><div><div>47</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Node.js + 原生 HTTP<a href="#nodejs--原生-http"><span>#</span></a></h3><div><div><div><figure><figcaption><span>server-http.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>http</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'http'</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>server</span><span> </span><span>=</span><span><span> </span><span>http</span><span>.</span></span><span>createServer</span><span><span>((</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span><span> (</span><span>req</span><span>.</span></span><span>url</span><span> </span><span>===</span><span> </span><span>'/api/events'</span><span>) {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>writeHead</span><span>(</span><span>200</span><span>, {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>'Content-Type'</span><span>:</span><span> </span><span>'text/event-stream'</span><span>,</span></div></div><div><div><div>7</div></div><div><span>      </span><span>'Cache-Control'</span><span>:</span><span> </span><span>'no-cache'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>      </span><span>'Connection'</span><span>:</span><span> </span><span>'keep-alive'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>'Access-Control-Allow-Origin'</span><span>:</span><span> </span><span>'*'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>      </span><span>'X-Accel-Buffering'</span><span>:</span><span> </span><span>'no'</span><span> </span><span>// 禁用 Nginx 缓冲</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>sendEvent</span><span> </span><span>=</span><span><span> (</span><span>eventName</span><span>, </span><span>data</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: </span><span>${</span><span>eventName</span><span>}</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>const</span><span> </span><span>heartbeat</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`: heartbeat</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}, </span><span>30000</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> </span><span>pushData</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>23</div></div><div><span>      </span><span>sendEvent</span><span>(</span><span>'update'</span><span>, {</span></div></div><div><div><div>24</div></div><div><span>        </span><span>time</span><span>:</span><span> </span><span>new</span><span> </span><span>Date</span><span>().</span><span>toISOString</span><span>(),</span></div></div><div><div><div>25</div></div><div><span>        </span><span>value</span><span>:</span><span><span> </span><span>Math</span><span>.</span></span><span>random</span><span>()</span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>});</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>}, </span><span>2000</span><span>);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>30</div></div><div><span>      </span><span>clearInterval</span><span><span>(</span><span>heartbeat</span><span>);</span></span></div></div><div><div><div>31</div></div><div><span>      </span><span>clearInterval</span><span><span>(</span><span>pushData</span><span>);</span></span></div></div><div><div><div>32</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Connection closed'</span><span>);</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>writeHead</span><span>(</span><span>404</span><span>);</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>end</span><span>(</span><span>'Not Found'</span><span>);</span></div></div><div><div><div>37</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span>});</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span><span>server</span><span>.</span></span><span>listen</span><span>(</span><span>8080</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'SSE Server running on http://localhost:8080'</span><span>);</span></div></div><div><div><div>42</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Node.js + Koa<a href="#nodejs--koa"><span>#</span></a></h3><div><div><div><figure><figcaption><span>server-koa.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>Koa</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'koa'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>Router</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'@koa/router'</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>app</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Koa</span><span>();</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Router</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>router</span><span>.</span></span><span>get</span><span>(</span><span>'/api/sse'</span><span>, </span><span>async</span><span><span> (</span><span>ctx</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>ctx</span><span>.</span><span>set</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>ctx</span><span>.</span><span>set</span><span>(</span><span>'Cache-Control'</span><span>, </span><span>'no-cache'</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>ctx</span><span>.</span><span>set</span><span>(</span><span>'Connection'</span><span>, </span><span>'keep-alive'</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>ctx</span><span>.</span><span>set</span><span>(</span><span>'Access-Control-Allow-Origin'</span><span>, </span><span>'*'</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>ctx</span><span>.</span><span>set</span><span>(</span><span>'X-Accel-Buffering'</span><span>, </span><span>'no'</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>const</span><span> </span><span>encoder</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>TextEncoder</span><span>();</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>const</span><span> </span><span>send</span><span> </span><span>=</span><span><span> (</span><span>eventName</span><span>, </span><span>data</span><span>, </span><span>eventId</span><span> </span></span><span>=</span><span> </span><span>null</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>let</span><span><span> </span><span>message</span><span> </span></span><span>=</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span><span> (</span><span>eventId</span><span>) </span><span>message</span><span> </span></span><span>+=</span><span> </span><span>`id: </span><span>${</span><span>eventId</span><span>}</span><span>\n</span><span>`</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>message</span><span> </span><span>+=</span><span> </span><span>`event: </span><span>${</span><span>eventName</span><span>}</span><span>\n</span><span>`</span><span>;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>message</span><span> </span><span>+=</span><span> </span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>ctx</span><span>.</span><span>res</span><span>.</span><span>write</span><span><span>(</span><span>message</span><span>);</span></span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>  </span><span>send</span><span>(</span><span>'connected'</span><span>, { </span><span>status</span><span>:</span><span> </span><span>'connected'</span><span> });</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>  </span><span>const</span><span> </span><span>interval</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span>    </span><span>send</span><span>(</span><span>'heartbeat'</span><span>, { </span><span>timestamp</span><span>:</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>() });</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}, </span><span>5000</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>ctx</span><span>.</span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>clearInterval</span><span><span>(</span><span>interval</span><span>);</span></span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>33</div></div><div><span>});</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span><span>app</span><span>.</span></span><span>use</span><span><span>(</span><span>router</span><span>.</span></span><span>routes</span><span>());</span></div></div><div><div><div>36</div></div><div><span><span>app</span><span>.</span></span><span>listen</span><span>(</span><span>3000</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Python + Flask<a href="#python--flask"><span>#</span></a></h3><div><div><div><figure><figcaption><span>server-flask.py</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>from</span><span> flask </span><span>import</span><span> Flask, Response, request</span></div></div><div><div><div>2</div></div><div><span>import</span><span> json</span></div></div><div><div><div>3</div></div><div><span>import</span><span> time</span></div></div><div><div><div>4</div></div><div><span>import</span><span> random</span></div></div><div><div><div>5</div></div><div><span>from</span><span> datetime </span><span>import</span><span> datetime</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>app </span><span>=</span><span> </span><span>Flask</span><span>(</span></span><span>__name__</span><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>def</span><span> </span><span>generate_events</span><span>():</span></div></div><div><div><div>10</div></div><div><span>    </span><span>"""生成 SSE 事件流"""</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>event_id </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>while</span><span> </span><span>True</span><span>:</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>event_id </span><span>+=</span><span> </span><span>1</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>data </span><span>=</span><span> {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>'id'</span><span>: event_id,</span></div></div><div><div><div>17</div></div><div><span>            </span><span>'timestamp'</span><span><span>: datetime.</span><span>now</span><span>().</span><span>isoformat</span><span>(),</span></span></div></div><div><div><div>18</div></div><div><span>            </span><span>'value'</span><span>: </span><span>round</span><span><span>(random.</span><span>random</span><span>() </span><span>*</span><span> </span></span><span>100</span><span>, </span><span>2</span><span>)</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>        </span><span>yield</span><span> </span><span>f</span><span>"event: update</span><span>\n</span><span>"</span></div></div><div><div><div>22</div></div><div><span>        </span><span>yield</span><span> </span><span>f</span><span>"id: </span><span>{</span><span>event_id</span><span>}</span><span>\n</span><span>"</span></div></div><div><div><div>23</div></div><div><span>        </span><span>yield</span><span> </span><span>f</span><span>"data: </span><span>{</span><span><span>json.</span><span>dumps</span><span>(data)</span></span><span>}</span><span>\n\n</span><span>"</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>time.</span><span>sleep</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>@app</span><span>.</span><span>route</span></span><span>(</span><span>'/api/events'</span><span>)</span></div></div><div><div><div>28</div></div><div><span>def</span><span> </span><span>events</span><span>():</span></div></div><div><div><div>29</div></div><div><span>    </span><span>"""SSE 端点"""</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>last_event_id </span><span>=</span><span> request.headers.</span><span>get</span><span>(</span><span>'Last-Event-ID'</span><span>)</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>if</span><span> last_event_id:</span></div></div><div><div><div>33</div></div><div><span>        </span><span>print</span><span>(</span><span>f</span><span>"Reconnecting from event ID: </span><span>{</span><span>last_event_id</span><span>}</span><span>"</span><span>)</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>return</span><span><span> </span><span>Response</span><span>(</span></span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>generate_events</span><span>(),</span></div></div><div><div><div>37</div></div><div><span>        </span><span>mimetype</span><span>=</span><span>'text/event-stream'</span><span>,</span></div></div><div><div><div>38</div></div><div><span>        </span><span>headers</span><span><span>=</span><span>{</span></span></div></div><div><div><div>39</div></div><div><span>            </span><span>'Cache-Control'</span><span>: </span><span>'no-cache'</span><span>,</span></div></div><div><div><div>40</div></div><div><span>            </span><span>'Connection'</span><span>: </span><span>'keep-alive'</span><span>,</span></div></div><div><div><div>41</div></div><div><span>            </span><span>'Access-Control-Allow-Origin'</span><span>: </span><span>'*'</span><span>,</span></div></div><div><div><div>42</div></div><div><span>            </span><span>'X-Accel-Buffering'</span><span>: </span><span>'no'</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>if</span><span> </span><span>__name__</span><span><span> </span><span>==</span><span> </span></span><span>'__main__'</span><span>:</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>app.</span><span>run</span><span>(</span><span>host</span><span>=</span><span>'0.0.0.0'</span><span>, </span><span>port</span><span>=</span><span>5000</span><span>, </span><span>threaded</span><span>=</span><span>True</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Python + FastAPI<a href="#python--fastapi"><span>#</span></a></h3><div><div><div><figure><figcaption><span>server-fastapi.py</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>from</span><span> fastapi </span><span>import</span><span> FastAPI, Request</span></div></div><div><div><div>2</div></div><div><span>from</span><span> fastapi.responses </span><span>import</span><span> StreamingResponse</span></div></div><div><div><div>3</div></div><div><span>import</span><span> asyncio</span></div></div><div><div><div>4</div></div><div><span>import</span><span> json</span></div></div><div><div><div>5</div></div><div><span>import</span><span> random</span></div></div><div><div><div>6</div></div><div><span>from</span><span> datetime </span><span>import</span><span> datetime</span></div></div><div><div><div>7</div></div><div><span>from</span><span> typing </span><span>import</span><span> AsyncGenerator</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>app </span><span>=</span><span> </span><span>FastAPI</span><span>()</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>@app</span><span>.</span><span>get</span></span><span>(</span><span>"/api/events"</span><span>)</span></div></div><div><div><div>12</div></div><div><span>async</span><span> </span><span>def</span><span> </span><span>sse_endpoint</span><span>(</span><span>request</span><span>:</span><span> Request</span><span>):</span></div></div><div><div><div>13</div></div><div><span>    </span><span>"""SSE 端点"""</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>last_event_id </span><span>=</span><span> request.headers.</span><span>get</span><span>(</span><span>"last-event-id"</span><span>)</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>async</span><span> </span><span>def</span><span> </span><span>event_generator</span><span>() -&gt; AsyncGenerator[</span><span>str</span><span>, </span><span>None</span><span>]:</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>event_id </span><span>=</span><span> </span><span>int</span><span>(last_event_id) </span><span>if</span><span> last_event_id </span><span>else</span><span> </span><span>0</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>        </span><span>while</span><span> </span><span>True</span><span>:</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>event_id </span><span>+=</span><span> </span><span>1</span></div></div><div><div><div>21</div></div><div><span><span>            </span></span><span>data </span><span>=</span><span> {</span></div></div><div><div><div>22</div></div><div><span>                </span><span>"id"</span><span>: event_id,</span></div></div><div><div><div>23</div></div><div><span>                </span><span>"timestamp"</span><span><span>: datetime.</span><span>now</span><span>().</span><span>isoformat</span><span>(),</span></span></div></div><div><div><div>24</div></div><div><span>                </span><span>"value"</span><span>: </span><span>round</span><span><span>(random.</span><span>random</span><span>() </span><span>*</span><span> </span></span><span>100</span><span>, </span><span>2</span><span>)</span></div></div><div><div><div>25</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>            </span><span>yield</span><span> </span><span>f</span><span>"event: update</span><span>\n</span><span>"</span></div></div><div><div><div>28</div></div><div><span>            </span><span>yield</span><span> </span><span>f</span><span>"id: </span><span>{</span><span>event_id</span><span>}</span><span>\n</span><span>"</span></div></div><div><div><div>29</div></div><div><span>            </span><span>yield</span><span> </span><span>f</span><span>"data: </span><span>{</span><span><span>json.</span><span>dumps</span><span>(data)</span></span><span>}</span><span>\n\n</span><span>"</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>            </span><span>await</span><span><span> asyncio.</span><span>sleep</span><span>(</span></span><span>2</span><span>)</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>return</span><span><span> </span><span>StreamingResponse</span><span>(</span></span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>event_generator</span><span>(),</span></div></div><div><div><div>35</div></div><div><span>        </span><span>media_type</span><span>=</span><span>"text/event-stream"</span><span>,</span></div></div><div><div><div>36</div></div><div><span>        </span><span>headers</span><span><span>=</span><span>{</span></span></div></div><div><div><div>37</div></div><div><span>            </span><span>"Cache-Control"</span><span>: </span><span>"no-cache"</span><span>,</span></div></div><div><div><div>38</div></div><div><span>            </span><span>"Connection"</span><span>: </span><span>"keep-alive"</span><span>,</span></div></div><div><div><div>39</div></div><div><span>            </span><span>"Access-Control-Allow-Origin"</span><span>: </span><span>"*"</span><span>,</span></div></div><div><div><div>40</div></div><div><span>            </span><span>"X-Accel-Buffering"</span><span>: </span><span>"no"</span></div></div><div><div><div>41</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Go<a href="#go"><span>#</span></a></h3><div><div><div><figure><figcaption><span>server-go/main.go</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>package</span><span> </span><span>main</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>import</span><span> (</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"fmt"</span></div></div><div><div><div>5</div></div><div><span>  </span><span>"log"</span></div></div><div><div><div>6</div></div><div><span>  </span><span>"net/http"</span></div></div><div><div><div>7</div></div><div><span>  </span><span>"time"</span></div></div><div><div><div>8</div></div><div><span>)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>func</span><span> </span><span>sseHandler</span><span><span>(</span><span>w</span><span> </span></span><span>http</span><span>.</span><span>ResponseWriter</span><span><span>, </span><span>r</span><span> </span><span>*</span></span><span>http</span><span>.</span><span>Request</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>  </span><span>w</span><span>.</span><span>Header</span><span>().</span><span>Set</span><span>(</span><span>"Content-Type"</span><span>, </span><span>"text/event-stream"</span><span>)</span></div></div><div><div><div>12</div></div><div><span>  </span><span>w</span><span>.</span><span>Header</span><span>().</span><span>Set</span><span>(</span><span>"Cache-Control"</span><span>, </span><span>"no-cache"</span><span>)</span></div></div><div><div><div>13</div></div><div><span>  </span><span>w</span><span>.</span><span>Header</span><span>().</span><span>Set</span><span>(</span><span>"Connection"</span><span>, </span><span>"keep-alive"</span><span>)</span></div></div><div><div><div>14</div></div><div><span>  </span><span>w</span><span>.</span><span>Header</span><span>().</span><span>Set</span><span>(</span><span>"Access-Control-Allow-Origin"</span><span>, </span><span>"*"</span><span>)</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>flusher</span><span>, </span><span>ok</span><span><span> </span><span>:=</span><span> </span></span><span>w</span><span>.(</span><span>http</span><span>.</span><span>Flusher</span><span>)</span></div></div><div><div><div>17</div></div><div><span>  </span><span>if</span><span><span> </span><span>!</span></span><span>ok</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>http</span><span>.</span><span>Error</span><span>(</span><span>w</span><span>, </span><span>"SSE not supported"</span><span>, </span><span>http</span><span>.</span><span>StatusInternalServerError</span><span>)</span></div></div><div><div><div>19</div></div><div><span>    </span><span>return</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>  </span><span>eventID</span><span><span> </span><span>:=</span><span> </span></span><span>0</span></div></div><div><div><div>23</div></div><div><span>  </span><span>ticker</span><span><span> </span><span>:=</span><span> </span></span><span>time</span><span>.</span><span>NewTicker</span><span>(</span><span>2</span><span><span> </span><span>*</span><span> </span></span><span>time</span><span>.</span><span>Second</span><span>)</span></div></div><div><div><div>24</div></div><div><span>  </span><span>defer</span><span> </span><span>ticker</span><span>.</span><span>Stop</span><span>()</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>  </span><span>for</span><span> {</span></div></div><div><div><div>27</div></div><div><span>    </span><span>select</span><span> {</span></div></div><div><div><div>28</div></div><div><span>    </span><span>case</span><span><span> </span><span>&lt;-</span></span><span>r</span><span>.</span><span>Context</span><span>().</span><span>Done</span><span>():</span></div></div><div><div><div>29</div></div><div><span>      </span><span>log</span><span>.</span><span>Println</span><span>(</span><span>"Client disconnected"</span><span>)</span></div></div><div><div><div>30</div></div><div><span>      </span><span>return</span></div></div><div><div><div>31</div></div><div><span>    </span><span>case</span><span><span> </span><span>&lt;-</span></span><span>ticker</span><span>.</span><span>C</span><span>:</span></div></div><div><div><div>32</div></div><div><span>      </span><span>eventID</span><span>++</span></div></div><div><div><div>33</div></div><div><span>      </span><span>data</span><span><span> </span><span>:=</span><span> </span></span><span>map</span><span>[</span><span>string</span><span>]</span><span>interface</span><span>{}{</span></div></div><div><div><div>34</div></div><div><span>        </span><span>"id"</span><span>:        </span><span>eventID</span><span>,</span></div></div><div><div><div>35</div></div><div><span>        </span><span>"timestamp"</span><span>: </span><span>time</span><span>.</span><span>Now</span><span>().</span><span>Format</span><span>(</span><span>time</span><span>.</span><span>RFC3339</span><span>),</span></div></div><div><div><div>36</div></div><div><span>        </span><span>"value"</span><span>:     </span><span>float64</span><span>(</span><span>eventID</span><span><span>) </span><span>*</span><span> </span></span><span>1.5</span><span>,</span></div></div><div><div><div>37</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>      </span><span>fmt</span><span>.</span><span>Fprintf</span><span>(</span><span>w</span><span>, </span><span>"id: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>eventID</span><span>)</span></div></div><div><div><div>40</div></div><div><span>      </span><span>fmt</span><span>.</span><span>Fprintf</span><span>(</span><span>w</span><span>, </span><span>"event: update</span><span>\n</span><span>"</span><span>)</span></div></div><div><div><div>41</div></div><div><span>      </span><span>fmt</span><span>.</span><span>Fprintf</span><span>(</span><span>w</span><span>, </span><span>"data: </span><span>%s</span><span>\n\n</span><span>"</span><span>, </span><span>toJSON</span><span>(</span><span>data</span><span>))</span></div></div><div><div><div>42</div></div><div><span>      </span><span>flusher</span><span>.</span><span>Flush</span><span>()</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>44</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>45</div></div><div><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>func</span><span> </span><span>toJSON</span><span><span>(</span><span>data</span><span> </span></span><span>map</span><span>[</span><span>string</span><span>]</span><span>interface</span><span>{}) </span><span>string</span><span> {</span></div></div><div><div><div>48</div></div><div><span>  </span><span>return</span><span> </span><span>fmt</span><span>.</span><span>Sprintf</span><span>(</span><span>`{"id":</span><span>%d</span><span>,"timestamp":"</span><span>%s</span><span>","value":</span><span>%.2f</span><span>}`</span><span>,</span></div></div><div><div><div>49</div></div><div><span>    </span><span>data</span><span>[</span><span>"id"</span><span>], </span><span>data</span><span>[</span><span>"timestamp"</span><span>], </span><span>data</span><span>[</span><span>"value"</span><span>])</span></div></div><div><div><div>50</div></div><div><span>}</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>func</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>53</div></div><div><span>  </span><span>http</span><span>.</span><span>HandleFunc</span><span>(</span><span>"/api/events"</span><span>, </span><span>sseHandler</span><span>)</span></div></div><div><div><div>54</div></div><div><span>  </span><span>log</span><span>.</span><span>Println</span><span>(</span><span>"Server running on http://localhost:8080"</span><span>)</span></div></div><div><div><div>55</div></div><div><span>  </span><span>log</span><span>.</span><span>Fatal</span><span>(</span><span>http</span><span>.</span><span>ListenAndServe</span><span>(</span><span>":8080"</span><span>, </span><span>nil</span><span>))</span></div></div><div><div><div>56</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Java + Spring Boot<a href="#java--spring-boot"><span>#</span></a></h3><div><div><div><figure><figcaption><span>SseController.java</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>@</span><span>RestController</span></div></div><div><div><div>2</div></div><div><span>@</span><span>RequestMapping</span><span>(</span><span>"/api"</span><span>)</span></div></div><div><div><div>3</div></div><div><span>public</span><span> </span><span>class</span><span><span> </span><span>SseController</span><span> </span></span><span>{</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>@</span><span>GetMapping</span><span>(</span><span>"/events"</span><span>)</span></div></div><div><div><div>6</div></div><div><span>    </span><span>public</span><span> </span><span>SseEmitter</span><span> </span><span>streamEvents</span><span><span>()</span><span> </span><span>{</span></span></div></div><div><div><div>7</div></div><div><span>        </span><span>SseEmitter</span><span><span> </span><span>emitter</span><span> </span></span><span><span>=</span><span> </span></span><span>new</span><span> </span><span>SseEmitter</span><span>(</span><span>0L</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>        </span><span>ExecutorService</span><span><span> </span><span>executor</span><span> </span></span><span><span>=</span><span> </span></span><span>Executors</span><span>.</span><span>newCachedThreadPool</span><span>();</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>        </span><span>executor</span><span>.</span><span>execute</span><span>(() </span><span>-&gt;</span><span> {</span></div></div><div><div><div>12</div></div><div><span>            </span><span>try</span><span> {</span></div></div><div><div><div>13</div></div><div><span>                </span><span>int</span><span><span>[] </span><span>eventId</span><span> </span></span><span><span>=</span><span> {</span></span><span>0</span><span>};</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>                </span><span>while</span><span> (</span><span>true</span><span>) {</span></div></div><div><div><div>16</div></div><div><span><span>                    </span></span><span>eventId[</span><span>0</span><span>]++;</span></div></div><div><div><div>17</div></div><div><span>                    </span><span>Map</span><span>&lt;</span><span>String</span><span>, </span><span>Object</span><span><span>&gt; </span><span>data</span><span> </span></span><span><span>=</span><span> </span></span><span>new</span><span> </span><span>HashMap</span><span>&lt;&gt;();</span></div></div><div><div><div>18</div></div><div><span>                    </span><span>data</span><span>.</span><span>put</span><span>(</span><span>"id"</span><span>, eventId[</span><span>0</span><span>]);</span></div></div><div><div><div>19</div></div><div><span>                    </span><span>data</span><span>.</span><span>put</span><span>(</span><span>"timestamp"</span><span>, </span><span>Instant</span><span>.</span><span>now</span><span>().</span><span>toString</span><span>());</span></div></div><div><div><div>20</div></div><div><span>                    </span><span>data</span><span>.</span><span>put</span><span>(</span><span>"value"</span><span>, </span><span>Math</span><span>.</span><span>random</span><span><span>() </span><span>*</span><span> </span></span><span>100</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>                    </span><span>emitter</span><span>.</span><span>send</span><span>(</span><span>SseEmitter</span><span>.</span><span>event</span><span>()</span></div></div><div><div><div>23</div></div><div><span><span>                        </span></span><span>.</span><span>name</span><span>(</span><span>"update"</span><span>)</span></div></div><div><div><div>24</div></div><div><span><span>                        </span></span><span>.</span><span>id</span><span>(</span><span>String</span><span>.</span><span>valueOf</span><span>(eventId[</span><span>0</span><span>]))</span></div></div><div><div><div>25</div></div><div><span><span>                        </span></span><span>.</span><span>data</span><span>(data));</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>                    </span><span>Thread</span><span>.</span><span>sleep</span><span>(</span><span>2000</span><span>);</span></div></div><div><div><div>28</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>} </span><span>catch</span><span> (</span><span>Exception</span><span><span> </span><span>e</span><span>) {</span></span></div></div><div><div><div>30</div></div><div><span>                </span><span>emitter</span><span>.</span><span>completeWithError</span><span>(e);</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>        </span><span>emitter</span><span>.</span><span>onCompletion</span><span>(() </span><span>-&gt;</span><span> </span><span>log</span><span>.</span><span>info</span><span>(</span><span>"SSE stream completed"</span><span>));</span></div></div><div><div><div>35</div></div><div><span>        </span><span>emitter</span><span>.</span><span>onError</span><span>(e </span><span>-&gt;</span><span> </span><span>log</span><span>.</span><span>error</span><span>(</span><span>"SSE error"</span><span>, e));</span></div></div><div><div><div>36</div></div><div><span>        </span><span>emitter</span><span>.</span><span>onTimeout</span><span>(() </span><span>-&gt;</span><span> </span><span>log</span><span>.</span><span>warning</span><span>(</span><span>"SSE timeout"</span><span>));</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>        </span><span>return</span><span> emitter;</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Nginx 配置<a href="#nginx-配置"><span>#</span></a></h3><div><div><div><figure><figcaption><span>nginx.conf</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>location</span><span> /api/sse {</span></div></div><div><div><div>3</div></div><div><span>        </span><span># 禁用缓冲</span></div></div><div><div><div>4</div></div><div><span>       <span> </span></span><span>proxy_buffering </span><span>off</span><span>;</span></div></div><div><div><div>5</div></div><div><span>       <span> </span></span><span>proxy_cache </span><span>off</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>        </span><span># 禁用 gzip (SSE 需要原始流)</span></div></div><div><div><div>8</div></div><div><span>       <span> </span></span><span>gzip </span><span>off</span><span>;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>       <span> </span></span><span>proxy_pass </span><span>http://backend;</span></div></div><div><div><div>11</div></div><div><span>       <span> </span></span><span>proxy_http_version </span><span>1.1</span><span>;</span></div></div><div><div><div>12</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>Connection </span><span>''</span><span>;</span></div></div><div><div><div>13</div></div><div><span>       <span> </span></span><span>proxy_read_timeout </span><span>86400</span><span>;</span></div></div><div><div><div>14</div></div><div><span>       <span> </span></span><span>proxy_send_timeout </span><span>86400</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>浏览器兼容性<a href="#浏览器兼容性"><span>#</span></a></h2><section><h3>支持情况<a href="#支持情况"><span>#</span></a></h3>

<table><thead><tr><th>浏览器</th><th>支持版本</th><th>备注</th></tr></thead><tbody><tr><td>Chrome</td><td>6+</td><td>完整支持</td></tr><tr><td>Firefox</td><td>6+</td><td>完整支持</td></tr><tr><td>Safari</td><td>5+</td><td>完整支持</td></tr><tr><td>Edge</td><td>79+</td><td>完整支持 (Chromium)</td></tr><tr><td>IE</td><td>不支持</td><td>需要 polyfill</td></tr><tr><td>Opera</td><td>11+</td><td>完整支持</td></tr><tr><td>iOS Safari</td><td>4+</td><td>完整支持</td></tr><tr><td>Android Browser</td><td>4.4+</td><td>完整支持</td></tr></tbody></table></section><section><h3>Polyfill (兼容 IE)<a href="#polyfill-兼容-ie"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 使用 polyfill --&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>script</span><span> </span><span>src</span><span>=</span><span>"https://cdn.jsdelivr.net/npm/event-source-polyfill@1.0.31/src/event_source_polyfill.js"</span><span>&gt;&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// 自动使用 polyfill (如果原生不支持)</span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>EventSource</span><span> </span></span><span>===</span><span> </span><span>'undefined'</span><span>) {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>EventSource</span><span> </span><span>=</span><span><span> </span><span>EventSourcePolyfill</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>检测支持<a href="#检测支持"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if</span><span> (</span><span>typeof</span><span><span> </span><span>EventSource</span><span> </span></span><span>!==</span><span> </span><span>'undefined'</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// 支持 SSE</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/events'</span><span>);</span></div></div><div><div><div>4</div></div><div><span>} </span><span>else</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// 不支持，使用 Fallback</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>'SSE not supported'</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>重连机制<a href="#重连机制"><span>#</span></a></h2><section><h3>自动重连<a href="#自动重连"><span>#</span></a></h3><p>浏览器内置自动重连机制。连接断开后，浏览器会自动等待一段时间后重连，默认重连时间约 3 秒。</p></section><section><h3>自定义重连时间<a href="#自定义重连时间"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 服务器端设置重连时间 (毫秒)</span></div></div><div><div><div>2</div></div><div><span><span>retry</span><span>: </span></span><span>10000</span><span><span>\</span><span>n</span></span></div></div><div><div><div>3</div></div><div><span><span>data</span><span>: {</span></span><span>"message"</span><span>: </span><span>"Retry in 10 seconds"</span><span><span>}\</span><span>n</span><span>\</span><span>n</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>手动重连控制<a href="#手动重连控制"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>SSEClient</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>url</span><span>, </span><span>options</span><span> </span></span><span>=</span><span> {}) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>url</span><span> </span><span>=</span><span><span> </span><span>url</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>options</span><span> </span><span>=</span><span><span> </span><span>options</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>this</span><span>.</span><span>eventSource</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>reconnectAttempts</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>maxReconnectAttempts</span><span> </span><span>=</span><span><span> </span><span>options</span><span>.</span></span><span>maxReconnectAttempts</span><span> </span><span>||</span><span> </span><span>10</span><span>;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>reconnectDelay</span><span> </span><span>=</span><span><span> </span><span>options</span><span>.</span></span><span>reconnectDelay</span><span> </span><span>||</span><span> </span><span>3000</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>  </span><span>connect</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>this</span><span>.</span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>this</span><span>.</span><span>url</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>this</span><span>.</span><span>eventSource</span><span>.</span><span>onopen</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Connected'</span><span>);</span></div></div><div><div><div>16</div></div><div><span>      </span><span>this</span><span>.</span><span>reconnectAttempts</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>this</span><span>.</span><span>eventSource</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span><span> (</span><span>event</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Message:'</span><span><span>, </span><span>event</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>this</span><span>.</span><span>eventSource</span><span>.</span><span>onerror</span><span> </span><span>=</span><span><span> (</span><span>event</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>24</div></div><div><span>      </span><span>if</span><span> (</span><span>this</span><span>.</span><span>reconnectAttempts</span><span> </span><span>&lt;</span><span> </span><span>this</span><span>.</span><span>maxReconnectAttempts</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>this</span><span>.</span><span>reconnectAttempts</span><span>++</span><span>;</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Reconnecting... (</span><span>${</span><span>this</span><span>.</span><span>reconnectAttempts</span><span>}</span><span>/</span><span>${</span><span>this</span><span>.</span><span>maxReconnectAttempts</span><span>}</span><span>)`</span><span>);</span></div></div><div><div><div>27</div></div><div><span>        </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>connect</span><span>(), </span><span>this</span><span>.</span><span>reconnectDelay</span><span>);</span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Max reconnect attempts reached'</span><span>);</span></div></div><div><div><div>30</div></div><div><span>        </span><span>this</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>  </span><span>close</span><span>() {</span></div></div><div><div><div>36</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>eventSource</span><span>) {</span></div></div><div><div><div>37</div></div><div><span>      </span><span>this</span><span>.</span><span>eventSource</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>38</div></div><div><span>      </span><span>this</span><span>.</span><span>eventSource</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>重连与事件顺序<a href="#重连与事件顺序"><span>#</span></a></h3><p>服务器需要确保事件的顺序和连续性：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>EventPublisher</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>events</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>maxStoredEvents</span><span> </span><span>=</span><span> </span><span>100</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>this</span><span>.</span><span>lastEventId</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>publish</span><span><span>(</span><span>eventType</span><span>, </span><span>data</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>event</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>      </span><span>id</span><span>:</span><span> </span><span>++</span><span>this</span><span>.</span><span>lastEventId</span><span>,</span></div></div><div><div><div>11</div></div><div><span>      </span><span>type</span><span>:</span><span><span> </span><span>eventType</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span>      </span><span>data</span><span>:</span><span><span> </span><span>data</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>      </span><span>timestamp</span><span>:</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>()</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>this</span><span>.</span><span>events</span><span>.</span><span>push</span><span><span>(</span><span>event</span><span>);</span></span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>events</span><span>.</span><span>length</span><span> </span><span>&gt;</span><span> </span><span>this</span><span>.</span><span>maxStoredEvents</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>      </span><span>this</span><span>.</span><span>events</span><span>.</span><span>shift</span><span>();</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>this</span><span>.</span><span>broadcast</span><span><span>(</span><span>event</span><span>);</span></span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>  </span><span>getEventsSince</span><span><span>(</span><span>lastEventId</span><span>) {</span></span></div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>this</span><span>.</span><span>events</span><span>.</span><span>filter</span><span><span>(</span><span>e</span><span> </span></span><span>=&gt;</span><span><span> </span><span>e</span><span>.</span></span><span>id</span><span> </span><span>&gt;</span><span><span> </span><span>lastEventId</span><span>);</span></span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>  </span><span>broadcast</span><span><span>(</span><span>event</span><span>) {</span></span></div></div><div><div><div>30</div></div><div><span>    </span><span>this</span><span>.</span><span>clients</span><span>.</span><span>forEach</span><span><span>(</span><span>client</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span><span>      </span></span><span>client</span><span>.</span><span>send</span><span><span>(</span><span>event</span><span>);</span></span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>字段说明<a href="#字段说明-1"><span>#</span></a></h2><section><h3>data 字段<a href="#data-字段"><span>#</span></a></h3><p>数据内容，可以是多行。多行数据会自动用换行符连接。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>data: First line\n</span></div></div><div><div><div>2</div></div><div><span>data: Second line\n</span></div></div><div><div><div>3</div></div><div><span>data: Third line\n\n</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 解析为:</span></div></div><div><div><div>6</div></div><div><span>// "First line\nSecond line\nThird line"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>event 字段<a href="#event-字段"><span>#</span></a></h3><p>事件类型，客户端通过 <code>addEventListener</code> 监听。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event: custom-event\n</span></div></div><div><div><div>2</div></div><div><span>data: {"message": "Hello"}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>id 字段<a href="#id-字段"><span>#</span></a></h3><p>事件唯一标识，用于断线重连时恢复。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>id: 123\n</span></div></div><div><div><div>2</div></div><div><span>event: update\n</span></div></div><div><div><div>3</div></div><div><span>data: {"value": 100}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>retry 字段<a href="#retry-字段"><span>#</span></a></h3><p>指定重连时间（毫秒）。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>retry: 5000\n</span></div></div><div><div><div>2</div></div><div><span>data: {"message": "Set 5s retry"}\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>冒号开头的注释<a href="#冒号开头的注释"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>: This is a comment\n</span></div></div><div><div><div>2</div></div><div><span>data: ping\n\n</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>安全考虑<a href="#安全考虑"><span>#</span></a></h2><section><h3>CORS 配置<a href="#cors-配置"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/sse'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Access-Control-Allow-Origin'</span><span>, </span><span>'https://yourdomain.com'</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Cache-Control'</span><span>, </span><span>'no-cache'</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Connection'</span><span>, </span><span>'keep-alive'</span><span>);</span></div></div><div><div><div>6</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>认证与授权<a href="#认证与授权"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 方式 1: Cookie (withCredentials)</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/sse'</span><span>, {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>withCredentials</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>4</div></div><div><span>});</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 方式 2: 初始连接后验证</span></div></div><div><div><div>7</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/sse'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> </span><span>sessionId</span><span> </span><span>=</span><span><span> </span><span>req</span><span>.</span></span><span>cookies</span><span>.</span><span>sessionId</span><span>;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span>validateSession</span><span><span>(</span><span>sessionId</span><span>)) {</span></span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>writeHead</span><span>(</span><span>401</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>end</span><span>(</span><span>'Unauthorized'</span><span>);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>return</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>  </span><span>// ... 建立 SSE 连接</span></div></div><div><div><div>17</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>连接数限制<a href="#连接数限制"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>connections</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>();</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>MAX_CONNECTIONS_PER_IP</span><span> </span><span>=</span><span> </span><span>5</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/sse'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>ip</span><span> </span><span>=</span><span><span> </span><span>req</span><span>.</span></span><span>ip</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>if</span><span><span> (</span><span>connections</span><span>.</span></span><span>get</span><span><span>(</span><span>ip</span><span>) </span></span><span>&gt;=</span><span> </span><span>MAX_CONNECTIONS_PER_IP</span><span>) {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>writeHead</span><span>(</span><span>429</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>end</span><span>(</span><span>'Connection limit exceeded'</span><span>);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>connections</span><span>.</span><span>set</span><span><span>(</span><span>ip</span><span>, </span><span>connections</span><span>.</span></span><span>get</span><span><span>(</span><span>ip</span><span>) </span></span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>connections</span><span>.</span><span>set</span><span><span>(</span><span>ip</span><span>, </span><span>connections</span><span>.</span></span><span>get</span><span><span>(</span><span>ip</span><span>) </span></span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>18</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>速率限制<a href="#速率限制"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>rateLimiter</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>windowMs</span><span>:</span><span> </span><span>1000</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>maxEvents</span><span>:</span><span> </span><span>10</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>counts</span><span>:</span><span> {},</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>check</span><span><span>(</span><span>clientId</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>const</span><span> </span><span>now</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>8</div></div><div><span>    </span><span>const</span><span> </span><span>key</span><span> </span><span>=</span><span> </span><span>`</span><span>${</span><span>clientId</span><span>}</span><span>:</span><span>${</span><span>Math</span><span>.</span><span>floor</span><span><span>(</span><span>now</span></span><span> </span><span>/</span><span> </span><span>this</span><span>.</span><span>windowMs</span><span>)</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>this</span><span>.</span><span>counts</span><span><span>[</span><span>key</span><span>] </span></span><span>=</span><span> (</span><span>this</span><span>.</span><span>counts</span><span><span>[</span><span>key</span><span>] </span></span><span>||</span><span> </span><span>0</span><span>) </span><span>+</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span> </span><span>this</span><span>.</span><span>counts</span><span><span>[</span><span>key</span><span>] </span></span><span>&lt;=</span><span> </span><span>this</span><span>.</span><span>maxEvents</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>实际应用场景<a href="#实际应用场景"><span>#</span></a></h2><section><h3>1. 实时通知系统<a href="#1-实时通知系统"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 服务器: 通知推送服务</span></div></div><div><div><div>2</div></div><div><span>class</span><span> </span><span>NotificationService</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>constructor</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>subscribers</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>();</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>subscribe</span><span><span>(</span><span>userId</span><span>, </span><span>client</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>this</span><span>.</span><span>subscribers</span><span>.</span><span>has</span><span><span>(</span><span>userId</span><span>)) {</span></span></div></div><div><div><div>9</div></div><div><span>      </span><span>this</span><span>.</span><span>subscribers</span><span>.</span><span>set</span><span><span>(</span><span>userId</span><span>, []);</span></span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>    </span><span>this</span><span>.</span><span>subscribers</span><span>.</span><span>get</span><span><span>(</span><span>userId</span><span>).</span></span><span>push</span><span><span>(</span><span>client</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>notify</span><span><span>(</span><span>userId</span><span>, </span><span>notification</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>const</span><span> </span><span>clients</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>subscribers</span><span>.</span><span>get</span><span><span>(</span><span>userId</span><span>);</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span><span> (</span><span>clients</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>clients</span><span>.</span><span>forEach</span><span><span>(</span><span>client</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>client</span><span>.</span><span>send</span><span>({</span></div></div><div><div><div>19</div></div><div><span>          </span><span>event</span><span>:</span><span> </span><span>'notification'</span><span>,</span></div></div><div><div><div>20</div></div><div><span>          </span><span>data</span><span>:</span><span><span> </span><span>notification</span></span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>22</div></div><div><span><span>      </span></span><span>});</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// 客户端</span></div></div><div><div><div>28</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>`/api/notifications/</span><span>${</span><span>userId</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span><span>eventSource</span><span>.</span></span><span>addEventListener</span><span>(</span><span>'notification'</span><span><span>, (</span><span>event</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>31</div></div><div><span>  </span><span>const</span><span> </span><span>notification</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>event</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>32</div></div><div><span>  </span><span>showToast</span><span><span>(</span><span>notification</span><span>.</span></span><span>title</span><span><span>, </span><span>notification</span><span>.</span></span><span>body</span><span>);</span></div></div><div><div><div>33</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 实时数据仪表盘<a href="#2-实时数据仪表盘"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 服务器: 系统监控</span></div></div><div><div><div>2</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/metrics'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>sendMetric</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>metrics</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>7</div></div><div><span>      </span><span>cpu</span><span>:</span><span><span> </span><span>os</span><span>.</span></span><span>loadavg</span><span>()[</span><span>0</span><span>],</span></div></div><div><div><div>8</div></div><div><span>      </span><span>memory</span><span>:</span><span> (</span><span>1</span><span> </span><span>-</span><span><span> </span><span>os</span><span>.</span></span><span>freemem</span><span>() </span><span>/</span><span><span> </span><span>os</span><span>.</span></span><span>totalmem</span><span>()) </span><span>*</span><span> </span><span>100</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>connections</span><span>:</span><span><span> </span><span>activeConnections</span><span>.</span></span><span>size</span><span>()</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>metrics</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>  </span><span>const</span><span> </span><span>interval</span><span> </span><span>=</span><span> </span><span>setInterval</span><span><span>(</span><span>sendMetric</span><span>, </span></span><span>1000</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>clearInterval</span><span><span>(</span><span>interval</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>19</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3. 聊天消息推送<a href="#3-聊天消息推送"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 服务器: 消息推送</span></div></div><div><div><div>2</div></div><div><span>function</span><span> </span><span>handleChatSSE</span><span><span>(</span><span>req</span><span>, </span><span>res</span><span>, </span><span>userId</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>sendMessage</span><span> </span><span>=</span><span><span> (</span><span>message</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: message</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>message</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>  </span><span>subscribe</span><span><span>(</span><span>userId</span><span>, </span><span>sendMessage</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>unsubscribe</span><span><span>(</span><span>userId</span><span>, </span><span>sendMessage</span><span>);</span></span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4. AI 流式响应<a href="#4-ai-流式响应"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 服务器: AI 流式生成</span></div></div><div><div><div>2</div></div><div><span><span>app</span><span>.</span></span><span>post</span><span>(</span><span>'/api/ai/chat'</span><span>, </span><span>async</span><span><span> (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>  </span><span>const</span><span> { </span><span>prompt</span><span> } </span><span>=</span><span><span> </span><span>req</span><span>.</span></span><span>body</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>const</span><span> </span><span>stream</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>ai</span><span>.</span></span><span>generateStream</span><span><span>(</span><span>prompt</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>for</span><span> </span><span>await</span><span> (</span><span>const</span><span> </span><span>chunk</span><span> </span><span>of</span><span><span> </span><span>stream</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span>({</span><span> </span><span>token</span><span>:</span><span> </span><span>chunk</span><span> </span><span>})</span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: done</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span>({</span><span> </span><span>completed</span><span>:</span><span> </span><span>true</span><span> </span><span>})</span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: error</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span>({</span><span> </span><span>message</span><span>:</span><span> </span><span>error</span><span>.</span><span>message</span><span> </span><span>})</span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>21</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>性能优化<a href="#性能优化"><span>#</span></a></h2><section><h3>1. 连接池管理<a href="#1-连接池管理"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>SSEConnectionPool</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>maxConnections</span><span> </span></span><span>=</span><span> </span><span>100</span><span>) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>pool</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>();</span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>maxConnections</span><span> </span><span>=</span><span><span> </span><span>maxConnections</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>get</span><span><span>(</span><span>key</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>pool</span><span>.</span><span>size</span><span> </span><span>&gt;=</span><span> </span><span>this</span><span>.</span><span>maxConnections</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>      </span><span>const</span><span> </span><span>oldest</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>findOldest</span><span>();</span></div></div><div><div><div>10</div></div><div><span>      </span><span>if</span><span><span> (</span><span>oldest</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>oldest</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>12</div></div><div><span>        </span><span>this</span><span>.</span><span>pool</span><span>.</span><span>delete</span><span><span>(</span><span>oldest</span><span>.</span></span><span>key</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>const</span><span> </span><span>connection</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>SSEConnection</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>this</span><span>.</span><span>pool</span><span>.</span><span>set</span><span><span>(</span><span>key</span><span>, </span><span>connection</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span><span> </span><span>connection</span><span>;</span></span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>remove</span><span><span>(</span><span>key</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> </span><span>connection</span><span> </span><span>=</span><span> </span><span>this</span><span>.</span><span>pool</span><span>.</span><span>get</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span><span> (</span><span>connection</span><span>) {</span></span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>connection</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>25</div></div><div><span>      </span><span>this</span><span>.</span><span>pool</span><span>.</span><span>delete</span><span><span>(</span><span>key</span><span>);</span></span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 心跳优化<a href="#2-心跳优化"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>HEARTBEAT_INTERVAL</span><span> </span><span>=</span><span> </span><span>30000</span><span>; </span><span>// 30 秒</span></div></div><div><div><div>2</div></div><div><span>let</span><span><span> </span><span>lastActivity</span><span> </span></span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>heartbeat</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>if</span><span><span> (</span><span>Date</span><span>.</span></span><span>now</span><span>() </span><span>-</span><span><span> </span><span>lastActivity</span><span> </span></span><span>&gt;=</span><span> </span><span>HEARTBEAT_INTERVAL</span><span>) {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`: heartbeat</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>lastActivity</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>}, </span><span>HEARTBEAT_INTERVAL</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>function</span><span> </span><span>onActivity</span><span>() {</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>lastActivity</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 消息批量处理<a href="#3-消息批量处理"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>MessageBuffer</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>callback</span><span>, </span><span>batchSize</span><span> </span></span><span>=</span><span> </span><span>10</span><span><span>, </span><span>flushInterval</span><span> </span></span><span>=</span><span> </span><span>100</span><span>) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>callback</span><span> </span><span>=</span><span><span> </span><span>callback</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>batchSize</span><span> </span><span>=</span><span><span> </span><span>batchSize</span><span>;</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>this</span><span>.</span><span>buffer</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>timer</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>  </span><span>push</span><span><span>(</span><span>message</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>this</span><span>.</span><span>buffer</span><span>.</span><span>push</span><span><span>(</span><span>message</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>buffer</span><span>.</span><span>length</span><span> </span><span>&gt;=</span><span> </span><span>this</span><span>.</span><span>batchSize</span><span>) {</span></div></div><div><div><div>13</div></div><div><span>      </span><span>this</span><span>.</span><span>flush</span><span>();</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>!</span><span>this</span><span>.</span><span>timer</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>      </span><span>this</span><span>.</span><span>timer</span><span> </span><span>=</span><span> </span><span>setTimeout</span><span>(() </span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>flush</span><span>(), </span><span>this</span><span>.</span><span>flushInterval</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>  </span><span>flush</span><span>() {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>buffer</span><span>.</span><span>length</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>21</div></div><div><span>      </span><span>this</span><span>.</span><span>callback</span><span>([</span><span>...</span><span>this</span><span>.</span><span>buffer</span><span>]);</span></div></div><div><div><div>22</div></div><div><span>      </span><span>this</span><span>.</span><span>buffer</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>timer</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>      </span><span>clearTimeout</span><span>(</span><span>this</span><span>.</span><span>timer</span><span>);</span></div></div><div><div><div>26</div></div><div><span>      </span><span>this</span><span>.</span><span>timer</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>完整示例<a href="#完整示例"><span>#</span></a></h2><section><h3>实时股票行情系统<a href="#实时股票行情系统"><span>#</span></a></h3><p><strong>前端页面</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!</span><span>DOCTYPE</span><span> </span><span>html</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>html</span><span> </span><span>lang</span><span>=</span><span>"zh-CN"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span>&lt;</span><span>head</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;</span><span>meta</span><span> </span><span>charset</span><span>=</span><span>"UTF-8"</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;</span><span>title</span><span>&gt;Real-time Stock Quotes&lt;/</span><span>title</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>&lt;</span><span>style</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>body</span><span> { font-family: </span><span>Arial</span><span>, </span><span>sans-serif</span><span>; margin: </span><span><span>20</span><span>px</span></span><span>; }</span></div></div><div><div><div>8</div></div><div><span>    </span><span>.stock-grid</span><span><span> { display: </span><span>grid</span><span>; grid-template-columns: </span></span><span>repeat</span><span><span>(</span><span>auto-fill</span><span>, </span></span><span>minmax</span><span>(</span><span><span>200</span><span>px</span></span><span>, </span><span><span>1</span><span>fr</span></span><span>)); gap: </span><span><span>16</span><span>px</span></span><span>; }</span></div></div><div><div><div>9</div></div><div><span>    </span><span>.stock-card</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>border: </span><span><span>1</span><span>px</span></span><span><span> </span><span>solid</span><span> </span></span><span>#</span><span>ddd</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>border-radius: </span><span><span>8</span><span>px</span></span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>padding: </span><span><span>16</span><span>px</span></span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>transition: box-shadow </span><span><span>0.3</span><span>s</span></span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>    </span><span><span>.stock-card</span><span>:hover</span></span><span> { box-shadow: </span><span>0</span><span> </span><span><span>4</span><span>px</span></span><span> </span><span><span>12</span><span>px</span></span><span> </span><span>rgba</span><span>(</span><span>0</span><span>,</span><span>0</span><span>,</span><span>0</span><span>,</span><span>0.1</span><span>); }</span></div></div><div><div><div>16</div></div><div><span>    </span><span>.stock-symbol</span><span><span> { font-weight: </span><span>bold</span><span>; font-size: </span></span><span><span>18</span><span>px</span></span><span>; }</span></div></div><div><div><div>17</div></div><div><span>    </span><span>.stock-price</span><span> { font-size: </span><span><span>24</span><span>px</span></span><span>; color: </span><span>#</span><span>333</span><span>; margin: </span><span><span>8</span><span>px</span></span><span> </span><span>0</span><span>; }</span></div></div><div><div><div>18</div></div><div><span>    </span><span>.stock-change</span><span> { font-size: </span><span><span>14</span><span>px</span></span><span>; }</span></div></div><div><div><div>19</div></div><div><span>    </span><span>.up</span><span> { color: </span><span>#</span><span>4caf50</span><span>; }</span></div></div><div><div><div>20</div></div><div><span>    </span><span>.down</span><span> { color: </span><span>#</span><span>f44336</span><span>; }</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>&lt;/</span><span>style</span><span>&gt;</span></div></div><div><div><div>22</div></div><div><span>&lt;/</span><span>head</span><span>&gt;</span></div></div><div><div><div>23</div></div><div><span>&lt;</span><span>body</span><span>&gt;</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>&lt;</span><span>h1</span><span>&gt;Real-time Stock Quotes&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"stockGrid"</span><span> </span><span>class</span><span>=</span><span>"stock-grid"</span><span>&gt;&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>&lt;</span><span>script</span><span>&gt;</span></div></div><div><div><div>28</div></div><div><span>    </span><span>const</span><span> </span><span>stocks</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>();</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/stocks/stream'</span><span>);</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>addEventListener</span><span>(</span><span>'initial-data'</span><span><span>, (</span><span>event</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>33</div></div><div><span>      </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>event</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>34</div></div><div><span><span>      </span></span><span>data</span><span>.</span><span>forEach</span><span><span>(</span><span>stock</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>stocks</span><span>.</span><span>set</span><span><span>(</span><span>stock</span><span>.</span></span><span>symbol</span><span><span>, </span><span>stock</span><span>);</span></span></div></div><div><div><div>36</div></div><div><span>        </span><span>createStockCard</span><span><span>(</span><span>stock</span><span>);</span></span></div></div><div><div><div>37</div></div><div><span><span>      </span></span><span>});</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>addEventListener</span><span>(</span><span>'stock-update'</span><span><span>, (</span><span>event</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>41</div></div><div><span>      </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>event</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>42</div></div><div><span>      </span><span>updateStockCard</span><span><span>(</span><span>data</span><span>);</span></span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>function</span><span> </span><span>updateStockCard</span><span><span>(</span><span>stock</span><span>) {</span></span></div></div><div><div><div>46</div></div><div><span>      </span><span>const</span><span> </span><span>existing</span><span> </span><span>=</span><span><span> </span><span>stocks</span><span>.</span></span><span>get</span><span><span>(</span><span>stock</span><span>.</span></span><span>symbol</span><span>);</span></div></div><div><div><div>47</div></div><div><span>      </span><span>if</span><span><span> (</span><span>existing</span><span>) {</span></span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>existing</span><span>.</span><span>price</span><span> </span><span>=</span><span><span> </span><span>stock</span><span>.</span></span><span>price</span><span>;</span></div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>existing</span><span>.</span><span>change</span><span> </span><span>=</span><span><span> </span><span>stock</span><span>.</span></span><span>change</span><span>;</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>existing</span><span>.</span><span>changePercent</span><span> </span><span>=</span><span><span> </span><span>stock</span><span>.</span></span><span>changePercent</span><span>;</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>        </span><span>const</span><span> </span><span>card</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelector</span><span>(</span><span>`[data-symbol="</span><span>${</span><span>stock</span><span>.</span><span>symbol</span><span>}</span><span>"]`</span><span>);</span></div></div><div><div><div>53</div></div><div><span>        </span><span>if</span><span><span> (</span><span>card</span><span>) {</span></span></div></div><div><div><div>54</div></div><div><span>          </span><span>const</span><span> </span><span>priceEl</span><span> </span><span>=</span><span><span> </span><span>card</span><span>.</span></span><span>querySelector</span><span>(</span><span>'.stock-price'</span><span>);</span></div></div><div><div><div>55</div></div><div><span>          </span><span>const</span><span> </span><span>changeEl</span><span> </span><span>=</span><span><span> </span><span>card</span><span>.</span></span><span>querySelector</span><span>(</span><span>'.stock-change'</span><span>);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span><span>          </span></span><span>priceEl</span><span>.</span><span>textContent</span><span> </span><span>=</span><span> </span><span>`$</span><span>${</span><span>stock</span><span>.</span><span>price</span><span>.</span><span>toFixed</span><span>(</span><span>2</span><span>)</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>58</div></div><div><span>          </span><span>const</span><span> </span><span>isUp</span><span> </span><span>=</span><span><span> </span><span>stock</span><span>.</span></span><span>change</span><span> </span><span>&gt;=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>59</div></div><div><span><span>          </span></span><span>changeEl</span><span>.</span><span>textContent</span><span> </span><span>=</span><span> </span><span>`</span><span>${</span><span>isUp</span><span> </span><span>?</span><span> </span><span>'</span><span>+</span><span>'</span><span> </span><span>:</span><span> </span><span><span>''</span><span>}${</span></span><span>stock</span><span>.</span><span>change</span><span>.</span><span>toFixed</span><span>(</span><span>2</span><span>)</span><span>}</span><span> (</span><span>${</span><span>isUp</span><span> </span><span>?</span><span> </span><span>'</span><span>+</span><span>'</span><span> </span><span>:</span><span> </span><span><span>''</span><span>}${</span></span><span>stock</span><span>.</span><span>changePercent</span><span>.</span><span>toFixed</span><span>(</span><span>2</span><span>)</span><span>}</span><span>%)`</span><span>;</span></div></div><div><div><div>60</div></div><div><span><span>          </span></span><span>changeEl</span><span>.</span><span>className</span><span> </span><span>=</span><span> </span><span>`stock-change </span><span>${</span><span>isUp</span><span> </span><span>?</span><span> </span><span>'</span><span>up</span><span>'</span><span> </span><span>:</span><span> </span><span>'</span><span>down</span><span><span>'</span><span>}</span></span><span>`</span><span>;</span></div></div><div><div><div>61</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>62</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>stocks</span><span>.</span><span>set</span><span><span>(</span><span>stock</span><span>.</span></span><span>symbol</span><span><span>, </span><span>stock</span><span>);</span></span></div></div><div><div><div>64</div></div><div><span>        </span><span>createStockCard</span><span><span>(</span><span>stock</span><span>);</span></span></div></div><div><div><div>65</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>66</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>67</div></div><div>
</div></div><div><div><div>68</div></div><div><span>    </span><span>function</span><span> </span><span>createStockCard</span><span><span>(</span><span>stock</span><span>) {</span></span></div></div><div><div><div>69</div></div><div><span>      </span><span>const</span><span> </span><span>grid</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>'stockGrid'</span><span>);</span></div></div><div><div><div>70</div></div><div><span>      </span><span>const</span><span> </span><span>isUp</span><span> </span><span>=</span><span><span> </span><span>stock</span><span>.</span></span><span>change</span><span> </span><span>&gt;=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>      </span><span>const</span><span> </span><span>card</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'div'</span><span>);</span></div></div><div><div><div>73</div></div><div><span><span>      </span></span><span>card</span><span>.</span><span>className</span><span> </span><span>=</span><span> </span><span>'stock-card'</span><span>;</span></div></div><div><div><div>74</div></div><div><span><span>      </span></span><span>card</span><span>.</span><span>dataset</span><span>.</span><span>symbol</span><span> </span><span>=</span><span><span> </span><span>stock</span><span>.</span></span><span>symbol</span><span>;</span></div></div><div><div><div>75</div></div><div><span><span>      </span></span><span>card</span><span>.</span><span>innerHTML</span><span> </span><span>=</span><span> </span><span>`</span></div></div><div><div><div>76</div></div><div><span><span>        </span></span><span>&lt;div class="stock-symbol"&gt;</span><span>${</span><span>stock</span><span>.</span><span>symbol</span><span>}</span><span>&lt;/div&gt;</span></div></div><div><div><div>77</div></div><div><span><span>        </span></span><span>&lt;div class="stock-price"&gt;$</span><span>${</span><span>stock</span><span>.</span><span>price</span><span>.</span><span>toFixed</span><span>(</span><span>2</span><span>)</span><span>}</span><span>&lt;/div&gt;</span></div></div><div><div><div>78</div></div><div><span><span>        </span></span><span>&lt;div class="stock-change </span><span>${</span><span>isUp</span><span> </span><span>?</span><span> </span><span>'</span><span>up</span><span>'</span><span> </span><span>:</span><span> </span><span>'</span><span>down</span><span><span>'</span><span>}</span></span><span>"&gt;</span></div></div><div><div><div>79</div></div><div><span>          </span><span>${</span><span>isUp</span><span> </span><span>?</span><span> </span><span>'</span><span>+</span><span>'</span><span> </span><span>:</span><span> </span><span><span>''</span><span>}${</span></span><span>stock</span><span>.</span><span>change</span><span>.</span><span>toFixed</span><span>(</span><span>2</span><span>)</span><span>}</span><span> (</span><span>${</span><span>isUp</span><span> </span><span>?</span><span> </span><span>'</span><span>+</span><span>'</span><span> </span><span>:</span><span> </span><span><span>''</span><span>}${</span></span><span>stock</span><span>.</span><span>changePercent</span><span>.</span><span>toFixed</span><span>(</span><span>2</span><span>)</span><span>}</span><span>%)</span></div></div><div><div><div>80</div></div><div><span><span>        </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>81</div></div><div><span><span>      </span></span><span>`</span><span>;</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span><span>      </span></span><span>grid</span><span>.</span><span>appendChild</span><span><span>(</span><span>card</span><span>);</span></span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span><span>    </span></span><span>window</span><span>.</span><span>addEventListener</span><span>(</span><span>'beforeunload'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>87</div></div><div><span><span>      </span></span><span>eventSource</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>88</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>89</div></div><div><span><span>  </span></span><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>90</div></div><div><span>&lt;/</span><span>body</span><span>&gt;</span></div></div><div><div><div>91</div></div><div><span>&lt;/</span><span>html</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>Node.js 服务器</strong></p><div><div><div><figure><figcaption><span>server-stock.js</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>express</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'express'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>app</span><span> </span><span>=</span><span> </span><span>express</span><span>();</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>stocks</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>symbol</span><span>:</span><span> </span><span>'AAPL'</span><span>, </span><span>basePrice</span><span>:</span><span> </span><span>175.50</span><span> },</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{ </span><span>symbol</span><span>:</span><span> </span><span>'GOOGL'</span><span>, </span><span>basePrice</span><span>:</span><span> </span><span>140.25</span><span> },</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>{ </span><span>symbol</span><span>:</span><span> </span><span>'MSFT'</span><span>, </span><span>basePrice</span><span>:</span><span> </span><span>378.90</span><span> },</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>{ </span><span>symbol</span><span>:</span><span> </span><span>'AMZN'</span><span>, </span><span>basePrice</span><span>:</span><span> </span><span>178.50</span><span> },</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>{ </span><span>symbol</span><span>:</span><span> </span><span>'TSLA'</span><span>, </span><span>basePrice</span><span>:</span><span> </span><span>242.80</span><span> },</span></div></div><div><div><div>10</div></div><div><span>];</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>const</span><span> </span><span>stockPrices</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Map</span><span>();</span></div></div><div><div><div>13</div></div><div><span><span>stocks</span><span>.</span></span><span>forEach</span><span><span>(</span><span>s</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>stockPrices</span><span>.</span><span>set</span><span><span>(</span><span>s</span><span>.</span></span><span>symbol</span><span>, {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>symbol</span><span>:</span><span><span> </span><span>s</span><span>.</span></span><span>symbol</span><span>,</span></div></div><div><div><div>16</div></div><div><span>    </span><span>price</span><span>:</span><span><span> </span><span>s</span><span>.</span></span><span>basePrice</span><span>,</span></div></div><div><div><div>17</div></div><div><span>    </span><span>change</span><span>:</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>18</div></div><div><span>    </span><span>changePercent</span><span>:</span><span> </span><span>0</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>20</div></div><div><span>});</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>const</span><span> </span><span>clients</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>();</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/stocks/stream'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Cache-Control'</span><span>, </span><span>'no-cache'</span><span>);</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Connection'</span><span>, </span><span>'keep-alive'</span><span>);</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Access-Control-Allow-Origin'</span><span>, </span><span>'*'</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>  </span><span>const</span><span> </span><span>initialData</span><span> </span><span>=</span><span><span> </span><span>Array</span><span>.</span></span><span>from</span><span><span>(</span><span>stockPrices</span><span>.</span></span><span>values</span><span>());</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: initial-data</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>initialData</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>clients</span><span>.</span><span>add</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>35</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Client connected. Total: </span><span>${</span><span>clients</span><span>.</span><span>size</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>  </span><span>const</span><span> </span><span>heartbeat</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`: heartbeat</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>39</div></div><div><span><span>  </span></span><span>}, </span><span>30000</span><span>);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span><span>  </span></span><span>req</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>42</div></div><div><span>    </span><span>clearInterval</span><span><span>(</span><span>heartbeat</span><span>);</span></span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>clients</span><span>.</span><span>delete</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Client disconnected. Total: </span><span>${</span><span>clients</span><span>.</span><span>size</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>46</div></div><div><span>});</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>function</span><span> </span><span>broadcastUpdate</span><span>() {</span></div></div><div><div><div>49</div></div><div><span>  </span><span>const</span><span> </span><span>symbols</span><span> </span><span>=</span><span><span> </span><span>Array</span><span>.</span></span><span>from</span><span><span>(</span><span>stockPrices</span><span>.</span></span><span>keys</span><span>());</span></div></div><div><div><div>50</div></div><div><span>  </span><span>const</span><span> </span><span>symbol</span><span> </span><span>=</span><span><span> </span><span>symbols</span><span>[</span><span>Math</span><span>.</span></span><span>floor</span><span><span>(</span><span>Math</span><span>.</span></span><span>random</span><span>() </span><span>*</span><span><span> </span><span>symbols</span><span>.</span></span><span>length</span><span>)];</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>  </span><span>const</span><span> </span><span>stock</span><span> </span><span>=</span><span><span> </span><span>stockPrices</span><span>.</span></span><span>get</span><span><span>(</span><span>symbol</span><span>);</span></span></div></div><div><div><div>53</div></div><div><span>  </span><span>const</span><span> </span><span>change</span><span> </span><span>=</span><span><span> (</span><span>Math</span><span>.</span></span><span>random</span><span>() </span><span>-</span><span> </span><span>0.5</span><span>) </span><span>*</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>54</div></div><div><span>  </span><span>const</span><span> </span><span>newPrice</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>max</span><span>(</span><span>1</span><span><span>, </span><span>stock</span><span>.</span></span><span>price</span><span> </span><span>+</span><span><span> </span><span>change</span><span>);</span></span></div></div><div><div><div>55</div></div><div><span>  </span><span>const</span><span> </span><span>totalChange</span><span> </span><span>=</span><span><span> </span><span>newPrice</span><span> </span></span><span>-</span><span><span> </span><span>stock</span><span>.</span></span><span>basePrice</span><span>;</span></div></div><div><div><div>56</div></div><div><span>  </span><span>const</span><span> </span><span>changePercent</span><span> </span><span>=</span><span><span> (</span><span>totalChange</span><span> </span></span><span>/</span><span><span> </span><span>stock</span><span>.</span></span><span>basePrice</span><span>) </span><span>*</span><span> </span><span>100</span><span>;</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span><span>  </span></span><span>stock</span><span>.</span><span>price</span><span> </span><span>=</span><span> </span><span>parseFloat</span><span><span>(</span><span>newPrice</span><span>.</span></span><span>toFixed</span><span>(</span><span>2</span><span>));</span></div></div><div><div><div>59</div></div><div><span><span>  </span></span><span>stock</span><span>.</span><span>change</span><span> </span><span>=</span><span> </span><span>parseFloat</span><span><span>(</span><span>totalChange</span><span>.</span></span><span>toFixed</span><span>(</span><span>2</span><span>));</span></div></div><div><div><div>60</div></div><div><span><span>  </span></span><span>stock</span><span>.</span><span>changePercent</span><span> </span><span>=</span><span> </span><span>parseFloat</span><span><span>(</span><span>changePercent</span><span>.</span></span><span>toFixed</span><span>(</span><span>2</span><span>));</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>  </span><span>const</span><span> </span><span>message</span><span> </span><span>=</span><span> </span><span>`event: stock-update</span><span>\n</span><span>data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>stock</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>;</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span><span>  </span></span><span>clients</span><span>.</span><span>forEach</span><span><span>(</span><span>client</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>client</span><span>.</span><span>write</span><span><span>(</span><span>message</span><span>);</span></span></div></div><div><div><div>66</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>67</div></div><div><span>}</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>setInterval</span><span><span>(</span><span>broadcastUpdate</span><span>, </span></span><span>1000</span><span>);</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span><span>app</span><span>.</span></span><span>listen</span><span>(</span><span>3000</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>72</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>'Stock SSE server running on http://localhost:3000'</span><span>);</span></div></div><div><div><div>73</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>调试工具<a href="#调试工具"><span>#</span></a></h2><section><h3>curl 测试<a href="#curl-测试"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 测试 SSE 端点</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-N</span><span> </span><span>-H</span><span> </span><span>"Accept: text/event-stream"</span><span> </span><span>http://localhost:3000/api/events</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 测试带 Last-Event-ID 的重连</span></div></div><div><div><div>5</div></div><div><span>curl</span><span> </span><span>-N</span><span> </span><span>-H</span><span> </span><span>"Accept: text/event-stream"</span><span> </span><span>-H</span><span> </span><span>"Last-Event-ID: 5"</span><span> </span><span>http://localhost:3000/api/events</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>浏览器开发者工具<a href="#浏览器开发者工具"><span>#</span></a></h3><ol>
<li><strong>Network 面板</strong>: 查看 SSE 连接，类型为 <code> EventSource</code></li>
<li><strong>Response 面板</strong>: 实时查看接收到的数据</li>
</ol></section><section><h3>JavaScript 调试技巧<a href="#javascript-调试技巧"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>class</span><span> </span><span>DebugEventSource</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>constructor</span><span><span>(</span><span>url</span><span>, </span><span>options</span><span> </span></span><span>=</span><span> {}) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>this</span><span>.</span><span>original</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span><span>(</span><span>url</span><span>, </span><span>options</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>this</span><span>.</span><span>debug</span><span> </span><span>=</span><span><span> </span><span>options</span><span>.</span></span><span>debug</span><span> </span><span>||</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>this</span><span>.</span><span>original</span><span>.</span><span>onopen</span><span> </span><span>=</span><span><span> (</span><span>e</span><span>) </span></span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>log</span><span>(</span><span>'OPEN'</span><span><span>, </span><span>e</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>this</span><span>.</span><span>original</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span><span> (</span><span>e</span><span>) </span></span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>log</span><span>(</span><span>'MESSAGE'</span><span><span>, </span><span>e</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>this</span><span>.</span><span>original</span><span>.</span><span>onerror</span><span> </span><span>=</span><span><span> (</span><span>e</span><span>) </span></span><span>=&gt;</span><span> </span><span>this</span><span>.</span><span>log</span><span>(</span><span>'ERROR'</span><span><span>, </span><span>e</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span> </span><span>this</span><span>.</span><span>original</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>  </span><span>log</span><span><span>(</span><span>type</span><span>, </span><span>data</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>) {</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`[SSE </span><span>${</span><span>type</span><span>}</span><span>]`</span><span><span>, </span><span>data</span><span>);</span></span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>  </span><span>addEventListener</span><span><span>(</span><span>type</span><span>, </span><span>listener</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>this</span><span>.</span><span>original</span><span>.</span><span>addEventListener</span><span><span>(</span><span>type</span><span>, (</span><span>e</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>21</div></div><div><span>      </span><span>if</span><span> (</span><span>this</span><span>.</span><span>debug</span><span>) {</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`[SSE EVENT: </span><span>${</span><span>type</span><span>}</span><span>]`</span><span><span>, </span><span>e</span><span>.</span></span><span>data</span><span>);</span></div></div><div><div><div>23</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>      </span><span>listener</span><span><span>(</span><span>e</span><span>);</span></span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>DebugEventSource</span><span>(</span><span>'/api/events'</span><span>, { </span><span>debug</span><span>:</span><span> </span><span>true</span><span> });</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>常见问题<a href="#常见问题"><span>#</span></a></h2><section><h3>Q: 为什么收到两条相同的消息？<a href="#q-为什么收到两条相同的消息"><span>#</span></a></h3><p>可能是重连导致的。检查 <code>Last-Event-ID</code> 是否正确传递，服务器需要实现去重逻辑。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>processedEvents</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>();</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>function</span><span> </span><span>sendEvent</span><span><span>(</span><span>res</span><span>, </span><span>eventId</span><span>, </span><span>data</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>if</span><span><span> (</span><span>processedEvents</span><span>.</span></span><span>has</span><span><span>(</span><span>eventId</span><span>)) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>processedEvents</span><span>.</span><span>add</span><span><span>(</span><span>eventId</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`id: </span><span>${</span><span>eventId</span><span>}</span><span>\n</span><span>`</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Q: 连接经常断开？<a href="#q-连接经常断开"><span>#</span></a></h3><p>检查网络和代理配置：</p><ol>
<li><strong>Nginx 代理</strong>: 确保 <code>proxy_buffering off;</code></li>
<li><strong>Cloudflare</strong>: SSE 可能被识别为爬虫</li>
<li><strong>AWS ALB</strong>: 默认 60 秒超时，需要配置更长超时</li>
</ol></section><section><h3>Q: 如何在 Vue/React 中使用？<a href="#q-如何在-vuereact-中使用"><span>#</span></a></h3><p><strong>Vue 3 Composition API</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>ref</span><span>, </span><span>onMounted</span><span>, </span><span>onUnmounted</span><span> } </span><span>from</span><span> </span><span>'vue'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>useSSE</span><span><span>(</span><span>url</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>null</span><span>);</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> </span><span>error</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>null</span><span>);</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>connected</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>false</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>let</span><span><span> </span><span>eventSource</span><span> </span></span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>  </span><span>onMounted</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span><span>(</span><span>url</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>onopen</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span><span> { </span><span>connected</span><span>.</span></span><span>value</span><span> </span><span>=</span><span> </span><span>true</span><span>; };</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span><span> (</span><span>event</span><span>) </span></span><span>=&gt;</span><span><span> { </span><span>data</span><span>.</span></span><span>value</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>event</span><span>.</span></span><span>data</span><span>); };</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>onerror</span><span> </span><span>=</span><span><span> (</span><span>err</span><span>) </span></span><span>=&gt;</span><span><span> { </span><span>error</span><span>.</span></span><span>value</span><span> </span><span>=</span><span><span> </span><span>err</span><span>; </span><span>connected</span><span>.</span></span><span>value</span><span> </span><span>=</span><span> </span><span>false</span><span>; };</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>  </span><span>onUnmounted</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span><span> (</span><span>eventSource</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>eventSource</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>  </span><span>return</span><span><span> { </span><span>data</span><span>, </span><span>error</span><span>, </span><span>connected</span><span> };</span></span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>React Hook</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>useState</span><span>, </span><span>useEffect</span><span>, </span><span>useRef</span><span> } </span><span>from</span><span> </span><span>'react'</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>function</span><span> </span><span>useSSE</span><span><span>(</span><span>url</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> [</span><span>data</span><span>, </span><span>setData</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>null</span><span>);</span></div></div><div><div><div>5</div></div><div><span>  </span><span>const</span><span> [</span><span>connected</span><span>, </span><span>setConnected</span><span>] </span><span>=</span><span> </span><span>useState</span><span>(</span><span>false</span><span>);</span></div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>eventSourceRef</span><span> </span><span>=</span><span> </span><span>useRef</span><span>(</span><span>null</span><span>);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>useEffect</span><span>(() </span><span>=&gt;</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span><span>(</span><span>url</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>eventSourceRef</span><span>.</span><span>current</span><span> </span><span>=</span><span><span> </span><span>eventSource</span><span>;</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>onopen</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> </span><span>setConnected</span><span>(</span><span>true</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>onmessage</span><span> </span><span>=</span><span><span> (</span><span>event</span><span>) </span></span><span>=&gt;</span><span> </span><span>setData</span><span>(</span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>event</span><span>.</span></span><span>data</span><span>));</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>eventSource</span><span>.</span><span>onerror</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> </span><span>setConnected</span><span>(</span><span>false</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>return</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>eventSource</span><span>.</span><span>close</span><span>();</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}, [</span><span>url</span><span>]);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>return</span><span><span> { </span><span>data</span><span>, </span><span>connected</span><span> };</span></span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q: SSE 与 WebSocket 同时使用？<a href="#q-sse-与-websocket-同时使用"><span>#</span></a></h3><p>可以共存，根据场景选择：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 简单推送场景用 SSE</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>eventSource</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>EventSource</span><span>(</span><span>'/api/notifications'</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 实时交互场景用 WebSocket</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>ws</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>WebSocket</span><span>(</span><span>'wss://api/ws'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Q: 如何限制最大连接数？<a href="#q-如何限制最大连接数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>MAX_CONNECTIONS</span><span> </span><span>=</span><span> </span><span>1000</span><span>;</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>activeConnections</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Set</span><span>();</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/sse'</span><span><span>, (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>if</span><span><span> (</span><span>activeConnections</span><span>.</span></span><span>size</span><span> </span><span>&gt;=</span><span> </span><span>MAX_CONNECTIONS</span><span>) {</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>writeHead</span><span>(</span><span>503</span><span>, { </span><span>'Retry-After'</span><span>:</span><span> </span><span>30</span><span> });</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>end</span><span>(</span><span>'Server busy'</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>return</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>activeConnections</span><span>.</span><span>add</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>on</span><span>(</span><span>'close'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>activeConnections</span><span>.</span><span>delete</span><span><span>(</span><span>res</span><span>);</span></span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>16</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q: 支持二进制数据？<a href="#q-支持二进制数据"><span>#</span></a></h3><p>SSE 本身是文本协议。传输二进制需要 Base64 编码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// Base64 编码</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>binaryData</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Uint8Array</span><span>([</span><span>0x00</span><span>, </span><span>0xFF</span><span>, </span><span>0x42</span><span>]);</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>base64</span><span> </span><span>=</span><span> </span><span>btoa</span><span><span>(</span><span>String</span><span>.</span></span><span>fromCharCode</span><span>(</span><span>...</span><span><span>binaryData</span><span>));</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>res</span><span>.</span></span><span>write</span><span>(</span><span>`data: </span><span>${</span><span>base64</span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>参考资源<a href="#参考资源"><span>#</span></a></h2><ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" target="_blank">MDN: Server-Sent Events</a></li>
<li><a href="https://html.spec.whatwg.org/multipage/server-sent-events.html" target="_blank">HTML5 Specification: Server-Sent Events</a></li>
<li><a href="https://www.html5rocks.com/en/tutorials/eventsource/basics/" target="_blank">SSE Demo</a></li>
<li><a href="https://caniuse.com/eventsource" target="_blank">Can I Use: EventSource</a></li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/jin-ri-tou-tiao-chuang-zuo/</id>
      <title type="text">今日头条创作</title>
      <published>2026-04-19T00:00:00.000Z</published>
      <updated>2026-04-19T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/jin-ri-tou-tiao-chuang-zuo/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>今日头条程序员/科技博主创作全攻略（2026最新）<a href="#今日头条程序员科技博主创作全攻略2026最新"><span>#</span></a></h1><p>作为软件程序员与科技人文爱好者，在今日头条创作的核心是<strong>合规+原创+垂直</strong>，守住这三点，配合平台规则与流量扶持，就能快速成长为优质创作者。以下是为你量身定制的详细指南，覆盖规则红线、新手入门与账号进阶全流程。</p><section><h2>一、核心创作规则（程序员/科技领域重点）<a href="#一核心创作规则程序员科技领域重点"><span>#</span></a></h2><section><h3>1. 账号基础规则（必知）<a href="#1-账号基础规则必知"><span>#</span></a></h3><ul>
<li><strong>实名认证</strong>：必须完成，否则无法开通创作收益与原创标签</li>
<li><strong>信用分机制</strong>（满分100）：
<ul>
<li>违规扣分，<strong>低于60分核心权益冻结</strong>，连续10天无违规自动恢复10分</li>
<li>程序员常见风险点：<strong>AI代码未标注</strong>（扣20分）、<strong>技术观点错误</strong>（扣10分）、<strong>无资质谈安全/金融技术</strong>（扣20分）</li>
</ul>
</li>
<li><strong>垂直领域</strong>：选择1-2个专注方向（如C++开发、AI应用、嵌入式系统），避免跨领域发布</li>
</ul></section><section><h3>2. 内容红线清单（程序员需特别注意）<a href="#2-内容红线清单程序员需特别注意"><span>#</span></a></h3>

<table><thead><tr><th>违规类型</th><th>处罚力度</th><th>典型案例（程序员常踩）</th></tr></thead><tbody><tr><td><strong>AI内容未标注</strong></td><td>限流+扣20分</td><td>整篇AI生成技术文章未勾选”引用AI”选项；AI生成代码未说明</td></tr><tr><td><strong>无资质专业内容</strong></td><td>限流+扣分</td><td>无网络安全资质谈渗透测试；无金融资质分析区块链投资</td></tr><tr><td><strong>代码版权问题</strong></td><td>取消原创+扣分</td><td>未经授权复制开源代码；未注明出处引用他人技术方案</td></tr><tr><td><strong>技术误导</strong></td><td>扣10-20分</td><td>发布错误技术教程；夸大AI编程能力；编造技术数据</td></tr><tr><td><strong>标题党/夸大宣传</strong></td><td>扣10分</td><td>“零基础7天精通C++”、“AI编程让程序员失业”等夸大标题</td></tr><tr><td><strong>违规导流</strong></td><td>限流+扣分</td><td>文章中引导至未认证个人博客；无广告标识推广技术课程</td></tr></tbody></table></section><section><h3>3. 原创与AI使用规范（程序员创作关键）<a href="#3-原创与ai使用规范程序员创作关键"><span>#</span></a></h3><ul>
<li><strong>原创判定标准</strong>：技术文章需包含<strong>独特见解</strong>、<strong>实际代码示例</strong>、<strong>个人经验总结</strong>，引用资料必须注明来源</li>
<li><strong>AI内容标注规则</strong>：
<ul>
<li>整篇AI生成内容必须在发布页勾选”引用AI/AI生成内容”</li>
<li>视频在前5秒添加”AI辅助创作”文字标注，图文在标题下方标注</li>
<li>仅用AI辅助代码生成/资料整理无需标注，避免误标影响流量</li>
</ul>
</li>
<li><strong>代码发布规范</strong>：
<ul>
<li>开源代码需提供GitHub链接并遵守开源协议</li>
<li>商业代码仅展示关键片段，避免泄露核心逻辑</li>
<li>AI生成代码需添加注释说明生成工具与人工修改部分</li>
</ul>
</li>
</ul></section><section><h3>4. 专业领域资质要求（程序员常见领域）<a href="#4-专业领域资质要求程序员常见领域"><span>#</span></a></h3>

<table><thead><tr><th>领域</th><th>所需资质</th><th>平台认证方式</th></tr></thead><tbody><tr><td>网络安全</td><td>CISP/CISAW等认证</td><td>创作中心→资质认证→上传证书</td></tr><tr><td>金融科技</td><td>金融从业资格证</td><td>创作中心→资质认证→上传证书</td></tr><tr><td>医疗IT</td><td>医疗相关资质</td><td>创作中心→资质认证→上传证书</td></tr><tr><td>教育技术</td><td>教师资格证</td><td>创作中心→资质认证→上传证书</td></tr><tr><td>技术培训</td><td>企业/机构认证</td><td>完成企业认证后发布培训内容</td></tr></tbody></table><hr /></section></section><section><h2>二、新手创作引导（程序员专属入门路径）<a href="#二新手创作引导程序员专属入门路径"><span>#</span></a></h2><section><h3>1. 账号开通五步走（快速启动）<a href="#1-账号开通五步走快速启动"><span>#</span></a></h3><ol>
<li><strong>下载注册</strong>：安装官方今日头条APP，用手机号注册，完善头像（技术相关）、昵称（含领域标识，如”码农阿泽”）、简介（突出技术专长）</li>
<li><strong>实名认证</strong>：我的→设置→账号与安全→实名认证（准备身份证）</li>
<li><strong>开通创作权益</strong>：我的→创作中心→创作权益，开启<strong>文章创作收益、头条首发、微头条创作收益</strong>（0粉丝即可开通）</li>
<li><strong>兴趣认证</strong>：选择”程序员/技术专家”领域，发布4条优质技术内容通过认证，增强账号垂直度</li>
<li><strong>设备准备</strong>：电脑端使用头条号后台（mp.toutiao.com）撰写技术文章，支持代码高亮与格式编辑</li>
</ol></section><section><h3>2. 7天起号计划（程序员专属，平台考核期关键）<a href="#2-7天起号计划程序员专属平台考核期关键"><span>#</span></a></h3>

<table><thead><tr><th>天数</th><th>任务清单</th><th>技术创作重点</th><th>目标</th></tr></thead><tbody><tr><td>Day1</td><td>1条300字微头条（技术热点+个人见解）</td><td>如”AI编程工具对比：Copilot vs CodeLlama”</td><td>告诉系统账号定位为技术领域</td></tr><tr><td>Day2-3</td><td>每天1条微头条+2条高质量评论</td><td>评论同领域爆款文章，提供技术补充</td><td>提升账号活跃度，建立专业形象</td></tr><tr><td>Day4</td><td>1篇800字技术文章（含代码示例）</td><td>如”C++智能指针使用误区与解决方案”</td><td>展示技术实力，申请原创标签铺垫</td></tr><tr><td>Day5</td><td>1条技术短视频（5分钟内）</td><td>演示”如何用CMake构建C++项目”</td><td>测试视频内容流量表现</td></tr><tr><td>Day6-7</td><td>每天1篇技术文章+1条微头条</td><td>技术文章深入讲解，微头条分享技术感悟</td><td>完成平台考核，提升账号权重</td></tr></tbody></table></section><section><h3>3. 内容创作技巧（程序员快速出爆款）<a href="#3-内容创作技巧程序员快速出爆款"><span>#</span></a></h3><ul>
<li><strong>选题方向（高流量技术领域）</strong>：
<ol>
<li><strong>技术实战</strong>：如”嵌入式Linux驱动开发步骤”、“WebRTC音视频通话实现”（结合代码示例）</li>
<li><strong>AI应用</strong>：如”用Ollama部署本地大模型”、“AI Agent在软件工程中的应用”（结合实际案例）</li>
<li><strong>技术对比</strong>：如”C++ vs Rust性能对比”、“libuv vs Boost.Asio异步IO库分析”（数据支撑）</li>
<li><strong>踩坑经验</strong>：如”排查C++内存泄漏的5种方法”、“Docker部署C++项目常见问题”（实用价值高）</li>
<li><strong>技术人文</strong>：如”程序员职业发展路径”、“开源社区文化分析”（吸引非技术读者）</li>
</ol>
</li>
<li><strong>标题撰写公式</strong>：
<ul>
<li>问题+解决方案：“如何解决C++多线程死锁问题？3种实用方案”</li>
<li>经验+数据：“我用libuv开发网络库的3年经验，性能提升40%”</li>
<li>趋势+见解：“2026年嵌入式AI发展趋势，程序员该如何转型？”</li>
</ul>
</li>
<li><strong>内容结构（技术文章标准）</strong>：
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>标题（25字内）→ 引言（问题背景）→ 核心技术原理 → 代码实现（带注释）→ 实战演示 → 问题排查 → 总结与展望</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li><strong>代码展示规范</strong>：
<ul>
<li>使用代码块，添加语法高亮（头条号后台支持）</li>
<li>关键代码添加详细注释，解释设计思路</li>
<li>提供完整代码GitHub链接，方便读者下载测试</li>
</ul>
</li>
</ul><hr /></section></section><section><h2>三、账号进阶指南（从技术博主到专业创作者）<a href="#三账号进阶指南从技术博主到专业创作者"><span>#</span></a></h2><section><h3>1. 原创标签申请（程序员必备权益）<a href="#1-原创标签申请程序员必备权益"><span>#</span></a></h3><ul>
<li><strong>申请条件</strong>：
<ol>
<li>账号注册满30天，近30天发布≥3篇原创技术文章，原创比例超70%</li>
<li>账号状态正常，信用分100分，无违规记录</li>
<li>技术文章包含独特见解、实际代码、个人经验，非纯理论复述</li>
</ol>
</li>
<li><strong>申请流程</strong>：创作中心→创作权益→原创标签→提交申请（24小时内审核）</li>
<li><strong>权益提升</strong>：原创文章收益提升3-5倍，获得平台优先推荐</li>
</ul></section><section><h3>2. 变现路径（程序员技术变现全攻略）<a href="#2-变现路径程序员技术变现全攻略"><span>#</span></a></h3>

<table><thead><tr><th>变现方式</th><th>开通条件</th><th>技术创作应用</th><th>收益预估</th></tr></thead><tbody><tr><td><strong>基础创作收益</strong></td><td>实名认证+0粉丝</td><td>技术文章、微头条、视频</td><td>千次阅读1-5元，爆款单篇可达千元</td></tr><tr><td><strong>原创补贴</strong></td><td>原创标签</td><td>深度技术文章、系列教程</td><td>单篇补贴50-200元，优质内容最高5倍奖励</td></tr><tr><td><strong>头条首发</strong></td><td>开通首发权益</td><td>技术文章优先在头条发布</td><td>额外10-30%收益加成</td></tr><tr><td><strong>商品卡</strong></td><td>粉丝≥1000</td><td>推荐技术书籍、开发板、工具软件</td><td>佣金率10-30%，月入可达数千元</td></tr><tr><td><strong>专栏订阅</strong></td><td>优质创作者认证</td><td>系列技术课程（如”C++并发编程实战”）</td><td>定价9.9-99元，订阅量决定收益</td></tr><tr><td><strong>直播带货</strong></td><td>粉丝≥10000</td><td>技术直播+开发工具/课程推广</td><td>单次直播收益可达万元</td></tr><tr><td><strong>技术咨询</strong></td><td>优质创作者认证</td><td>提供技术问题解答、项目指导</td><td>按次收费，单次100-500元</td></tr></tbody></table></section><section><h3>3. 账号进阶三步法（从新手到专家）<a href="#3-账号进阶三步法从新手到专家"><span>#</span></a></h3><ol>
<li>
<p><strong>深耕垂直领域</strong>：</p>
<ul>
<li>选择1个核心技术方向（如嵌入式AI、视频流媒体），成为该领域权威</li>
<li>建立系列内容（如”C++性能优化系列”、“嵌入式Linux开发系列”），增强用户粘性</li>
<li>定期发布深度内容（10分钟+视频、2000字+长文），获取平台深度内容补贴</li>
</ul>
</li>
<li>
<p><strong>数据驱动优化</strong>：</p>
<ul>
<li>创作中心数据分析：重点关注<strong>阅读完成率</strong>（技术文章需&gt;60%）、<strong>收藏/转发比</strong>（&gt;0.5%为优质）、<strong>粉丝增长</strong></li>
<li>发布时间优化：程序员读者活跃时段为<strong>21:00-23&lt;00&gt;</strong>（下班后）、<strong>12:00-13&lt;00&gt;</strong>（午休）</li>
<li>内容形式调整：技术复杂内容用<strong>视频+图文</strong>组合；入门内容用<strong>微头条+短视频</strong>快速传播</li>
</ul>
</li>
<li>
<p><strong>建立个人品牌</strong>：</p>
<ul>
<li>统一账号名称、头像、简介，强化技术形象</li>
<li>与同领域创作者互动，参与技术话题讨论，扩大影响力</li>
<li>开通知乎、GitHub等平台账号，多平台联动引流</li>
<li>定期举办技术直播，解答读者问题，增强粉丝粘性</li>
</ul>
</li>
</ol><hr /></section></section><section><h2>四、避坑指南（程序员常见错误）<a href="#四避坑指南程序员常见错误"><span>#</span></a></h2><ol>
<li><strong>忽视信用分</strong>：信用分是创作”生命线”，扣分后立即整改，连续10天无违规恢复</li>
<li><strong>跨领域乱发</strong>：今天发C++，明天发Python，后天发AI，导致账号定位模糊，流量分散</li>
<li><strong>技术内容太晦涩</strong>：用太多专业术语，缺乏通俗解释，导致阅读完成率低，推荐量受限</li>
<li><strong>不标注AI内容</strong>：用AI生成技术文章未标注，被系统判定为低质内容，影响账号权重</li>
<li><strong>忽视版权问题</strong>：复制他人代码或技术文章未注明出处，被投诉侵权，取消原创标签</li>
<li><strong>不看平台规则</strong>：未关注”头条号”官方账号，错过技术领域规则更新，导致违规</li>
</ol><hr /></section><section><h2>五、官方支持与申诉渠道<a href="#五官方支持与申诉渠道"><span>#</span></a></h2><ol>
<li><strong>创作中心</strong>：提供新手引导、权益说明、数据分析等一站式服务</li>
<li><strong>规则查询</strong>：在创作中心查看《今日头条社区规范》《内容审核标准》</li>
<li><strong>申诉流程</strong>：系统通知→处罚详情→提交申诉，附上<strong>代码原创证明</strong>/<strong>授权文件</strong>/<strong>AI使用说明</strong>，24小时内回复</li>
<li><strong>技术创作者交流群</strong>：关注头条号官方账号，获取加入方式，与其他技术博主交流经验</li>
</ol><hr /></section><section><h2>六、程序员创作黄金建议<a href="#六程序员创作黄金建议"><span>#</span></a></h2><ol>
<li><strong>技术+人文结合</strong>：在技术内容中融入个人经验、行业洞察，让文章既有深度又有温度，吸引更多读者</li>
<li><strong>实战为王</strong>：技术文章必须包含<strong>可运行代码</strong>、<strong>实际案例</strong>、<strong>踩坑经验</strong>，这是区别于AI生成内容的关键</li>
<li><strong>持续输出</strong>：新号前7天保持日更，建立系统对账号的信任度，之后保持每周3-5篇稳定更新</li>
<li><strong>对标学习</strong>：关注10个同领域爆款技术账号（如”Linux内核之旅”、“AI编程实验室”），分析其选题、标题、结构特点</li>
<li><strong>数据驱动</strong>：定期查看创作中心数据分析，优化内容方向和发布时间，提升推荐量和收益</li>
</ol><p><strong>记住</strong>：今日头条对技术内容（特别是AI、嵌入式、流媒体等前沿领域）有<strong>流量扶持</strong>，作为软件程序员，你拥有天然的内容优势。只要遵守规则、坚持原创、深耕垂直领域，就能在平台快速成长，实现技术变现与个人品牌建设的双赢！</p><p>要不要我给你整理一份可直接套用的技术文章发布模板（含标题公式、内容结构、代码展示规范和AI标注说明），你照着填就能快速发布？</p><hr /></section></section>
<section><h1>引流<a href="#引流"><span>#</span></a></h1><p>可以给个人博客引流，但<strong>必须严格遵守今日头条导流规范</strong>，违规会被限流、扣分（信用分），严重时影响账号权益，具体要求如下：</p><ol>
<li>合规引流边界：仅可在账号简介中合理标注个人博客信息（需清晰、无诱导性），且博客需为正规备案、非违规平台，不可出现“点击跳转”“去我博客看完整内容”等诱导性表述；</li>
<li>绝对禁止行为：正文（文章、微头条、视频）、评论区、私信中，禁止放置博客链接、引导跳转至博客（如“完整代码在我博客”），禁止用谐音、隐晦表述引导（参考superscript:1superscript&lt;3&gt;），否则判定为违规导流，触发限流+扣分；</li>
<li>补充说明：即使是技术类个人博客，未按规范引流也会违规（对应canvas中“违规导流”红线），平台严打私自向外导流行为，所有推广类引导需符合《今日头条内容电商创作管理规范》（参考superscript&lt;1&gt;）。</li>
</ol><p>核心原则：不影响头条内容本身的完整性，不诱导用户脱离平台，仅在简介中被动展示博客信息，不主动引导跳转。</p><p>需要我帮你检查一下你准备的<strong>简介博客标注文案</strong>，判断是否符合平台合规要求吗？</p><hr /><p>结合你的博客情况（阿里云域名+GitHub Pages搭建），<strong>可以合规引流，但需严格遵守今日头条规则</strong>，违规会直接影响账号权益，具体说明如下：</p><section><h3>一、能否引流（核心判断）<a href="#一能否引流核心判断"><span>#</span></a></h3><p>可以引流，但仅限定「合规场景」，你的博客本身符合平台要求（GitHub Pages允许个人非商业使用，阿里云域名完成实名认证后可正常展示），但需满足2个核心前提：</p><ol>
<li>阿里云域名需合规：若为国内后缀（如.cn），必须完成ICP备案（备案流程参考）；若为国际后缀（如.com），需完成域名实名认证，无违规内容（如低俗、违法信息）；</li>
<li>引流方式仅可「被动展示」：仅能在账号简介中标注博客域名（无诱导性表述），不可在正文、评论区、私信中引导跳转。</li>
</ol></section><section><h3>二、引流的影响（分合规/违规两种情况）<a href="#二引流的影响分合规违规两种情况"><span>#</span></a></h3><section><h4>1. 合规引流（无负面影响，可实现被动引流）<a href="#1-合规引流无负面影响可实现被动引流"><span>#</span></a></h4><ul>
<li>无处罚：严格按规则在简介标注（如“个人技术博客：xxx.xxx（阿里云域名），分享编程干货”），不主动引导，不会触发限流、扣分；</li>
<li>正向作用：吸引对技术内容感兴趣的粉丝，主动访问你的博客，实现头条账号与个人博客的联动，助力个人品牌建设（契合canvas中“品牌建设”进阶要求）。</li>
</ul></section><section><h4>2. 违规引流（直接影响账号权益，重点规避）<a href="#2-违规引流直接影响账号权益重点规避"><span>#</span></a></h4><ul>
<li>处罚类型：对应canvas中“违规导流”红线，触发限流（内容推荐量骤降）、信用分扣分（10-20分），严重时冻结创作收益、原创标签；</li>
<li>常见违规场景（针对你的博客）：
<ol>
<li>正文/评论区引导：如“完整代码在我的博客（xxx.xxx）”“去我博客看详细教程”，或用谐音、隐晦表述引导；</li>
<li>域名不合规：阿里云国内后缀域名未完成ICP备案，标注后被判定为“违规平台导流”；</li>
<li>诱导性表述：简介中出现“点击”“跳转”“关注博客”等诱导性文字（参考头条严打诱导导流要求）。</li>
</ol>
</li>
</ul></section></section><section><h3>三、补充注意事项<a href="#三补充注意事项"><span>#</span></a></h3><ol>
<li>GitHub Pages本身合规（个人非商业使用不受限），但不可在头条内容中放置GitHub Pages的原生链接（仅可标注绑定的阿里云域名）；</li>
<li>博客内容需与头条账号垂直领域一致（均为程序员/科技类），避免博客含违规内容（如无资质专业内容、低俗信息），否则会连带影响头条账号；</li>
<li>若违规被处罚，可按canvas中“申诉流程”，提交域名备案证明、博客合规说明，24小时内平台会回复。</li>
</ol></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/cloud-service-models-comprehensive-guide/</id>
      <title type="text">☁️ 云计算服务模型全解析：从 IaaS 到 CaaS，一文读懂所有「aaS」</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/cloud-service-models-comprehensive-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>云计算服务模型全解析：从 IaaS 到 CaaS，一文读懂所有「aaS」<a href="#云计算服务模型全解析从-iaas-到-caas一文读懂所有aas"><span>#</span></a></h1><blockquote><p>给程序员的云计算模型指南，建议收藏</p></blockquote><section><h2>目录<a href="#目录"><span>#</span></a></h2><ol>
<li>概述：什么是 XaaS</li>
<li>IaaS - 基础设施即服务</li>
<li>PaaS - 平台即服务</li>
<li>SaaS - 软件即服务</li>
<li>DaaS - 数据即服务</li>
<li>BaaS - 后端即服务</li>
<li>CaaS - 容器即服务</li>
<li>对比总结</li>
<li>如何选择</li>
</ol><hr /></section><section><h2>概述：什么是 XaaS<a href="#概述什么是-xaas"><span>#</span></a></h2><p><strong>XaaS (Everything as a Service)</strong> 指的是”一切皆服务”，是云计算的核心商业模式。本质上是<strong>将 IT 资源、能力以服务的形式通过互联网提供给用户</strong>，按需使用，按量付费。</p><section><h3>核心特征<a href="#核心特征"><span>#</span></a></h3><ul>
<li><strong>按需自助服务</strong>：用户可自行获取资源，无需人工干预</li>
<li><strong>广泛的网络访问</strong>：通过网络随时随地访问</li>
<li><strong>资源池化</strong>：多租户共享资源池</li>
<li><strong>快速弹性</strong>：资源可快速扩展或收缩</li>
<li><strong>可计量服务</strong>：使用情况可监控、控制和报告</li>
</ul></section><section><h3>传统模型 vs 云服务模型<a href="#传统模型-vs-云服务模型"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>传统自建模式：</span></div></div><div><div><div>2</div></div><div><span>┌─────────────────────────────────────┐</span></div></div><div><div><div>3</div></div><div><span>│  应用  │  数据  │  运行时  │  中间件  │</span></div></div><div><div><div>4</div></div><div><span>├─────────────────────────────────────┤</span></div></div><div><div><div>5</div></div><div><span>│        操作系统      │   虚拟化     │</span></div></div><div><div><div>6</div></div><div><span>├─────────────────────────────────────┤</span></div></div><div><div><div>7</div></div><div><span>│           服务器         │   存储     │</span></div></div><div><div><div>8</div></div><div><span>├─────────────────────────────────────┤</span></div></div><div><div><div>9</div></div><div><span>│              网络设备                │</span></div></div><div><div><div>10</div></div><div><span>└─────────────────────────────────────┘</span></div></div><div><div><div>11</div></div><div><span><span>     </span></span><span>↑ 全部自己管理和维护</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>云服务模式：</span></div></div><div><div><div>14</div></div><div><span>┌─────────────────────────────────────┐</span></div></div><div><div><div>15</div></div><div><span>│  应用  │  数据  │  运行时  │  中间件  │  ← SaaS</span></div></div><div><div><div>16</div></div><div><span>├─────────────────────────────────────┤</span></div></div><div><div><div>17</div></div><div><span>│        操作系统      │   虚拟化     │  ← PaaS</span></div></div><div><div><div>18</div></div><div><span>├─────────────────────────────────────┤</span></div></div><div><div><div>19</div></div><div><span>│           服务器         │   存储     │  ← IaaS</span></div></div><div><div><div>20</div></div><div><span>└─────────────────────────────────────┘</span></div></div><div><div><div>21</div></div><div><span><span>     </span></span><span>↑ 云服务商管理底层，你专注上层</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>IaaS - 基础设施即服务<a href="#iaas---基础设施即服务"><span>#</span></a></h2><section><h3>定义<a href="#定义"><span>#</span></a></h3><p><strong>Infrastructure as a Service</strong> 提供虚拟化的计算资源（虚拟机、存储、网络等），是最底层的云服务模型。</p></section><section><h3>你管理什么 vs 云厂商管理什么<a href="#你管理什么-vs-云厂商管理什么"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>你管理：    应用 + 数据 + 运行时 + 中间件 + 操作系统</span></div></div><div><div><div>2</div></div><div><span>云厂商管理：虚拟化 + 服务器 + 存储 + 网络</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>核心服务<a href="#核心服务"><span>#</span></a></h3><ul>
<li><strong>计算资源</strong>：虚拟机实例（EC2、ECS）</li>
<li><strong>存储服务</strong>：块存储、对象存储（S3、OSS）</li>
<li><strong>网络资源</strong>：VPC、负载均衡、CDN</li>
<li><strong>安全组</strong>：防火墙规则</li>
</ul></section><section><h3>典型场景<a href="#典型场景"><span>#</span></a></h3><ul>
<li>需要完全控制操作系统和运行环境</li>
<li>迁移现有应用到云端（Lift &amp; Shift）</li>
<li>开发和测试环境</li>
<li>高性能计算（HPC）</li>
<li>大数据处理</li>
</ul></section><section><h3>主流产品<a href="#主流产品"><span>#</span></a></h3>

<table><thead><tr><th>厂商</th><th>产品</th></tr></thead><tbody><tr><td>AWS</td><td>EC2, S3, VPC</td></tr><tr><td>阿里云</td><td>ECS, OSS, VPC</td></tr><tr><td>腾讯云</td><td>CVM, COS, VPC</td></tr><tr><td>Azure</td><td>Virtual Machines, Blob Storage</td></tr></tbody></table></section><section><h3>代码示例：启动一台 EC2 实例<a href="#代码示例启动一台-ec2-实例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> boto3</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>ec2 </span><span>=</span><span> boto3.</span><span>resource</span><span>(</span></span><span>'ec2'</span><span>)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>instance </span><span>=</span><span> ec2.</span><span>create_instances</span><span>(</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>ImageId</span><span>=</span><span>'ami-0c55b159cbfafe1f0'</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>InstanceType</span><span>=</span><span>'t2.micro'</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>MinCount</span><span>=</span><span>1</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>MaxCount</span><span>=</span><span>1</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>KeyName</span><span>=</span><span>'my-key-pair'</span></div></div><div><div><div>11</div></div><div><span>)</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>print</span><span>(</span><span>f</span><span>"Instance ID: </span><span>{</span><span>instance[</span><span>0</span><span>].id</span><span>}</span><span>"</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>优缺点<a href="#优缺点"><span>#</span></a></h3><p>✅ <strong>优点</strong>：</p><ul>
<li>最高灵活性和控制权</li>
<li>无需投资硬件</li>
<li>按需付费，成本可控</li>
</ul><p>❌ <strong>缺点</strong>：</p><ul>
<li>需要自己管理操作系统和安全补丁</li>
<li>运维复杂度较高</li>
<li>需要专业的系统管理员</li>
</ul><hr /></section></section><section><h2>PaaS - 平台即服务<a href="#paas---平台即服务"><span>#</span></a></h2><section><h3>定义<a href="#定义-1"><span>#</span></a></h3><p><strong>Platform as a Service</strong> 提供应用程序开发和部署所需的完整平台环境，开发者只需关注代码。</p></section><section><h3>你管理什么 vs 云厂商管理什么<a href="#你管理什么-vs-云厂商管理什么-1"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>你管理：    应用 + 数据</span></div></div><div><div><div>2</div></div><div><span>云厂商管理：运行时 + 中间件 + 操作系统 + 虚拟化 + 服务器 + 存储 + 网络</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>核心服务<a href="#核心服务-1"><span>#</span></a></h3><ul>
<li><strong>应用托管</strong>：自动部署和扩展</li>
<li><strong>数据库服务</strong>：托管数据库（RDS、Cloud SQL）</li>
<li><strong>中间件</strong>：消息队列、缓存</li>
<li><strong>开发工具</strong>：CI/CD、监控</li>
</ul></section><section><h3>典型场景<a href="#典型场景-1"><span>#</span></a></h3><ul>
<li>Web 应用快速开发和部署</li>
<li>微服务架构</li>
<li>API 服务</li>
<li>移动应用后端</li>
<li>需要自动扩缩容的场景</li>
</ul></section><section><h3>主流产品<a href="#主流产品-1"><span>#</span></a></h3>

<table><thead><tr><th>厂商</th><th>产品</th></tr></thead><tbody><tr><td>Heroku</td><td>Heroku Platform</td></tr><tr><td>Google</td><td>App Engine, Cloud Run</td></tr><tr><td>AWS</td><td>Elastic Beanstalk, Lambda</td></tr><tr><td>阿里云</td><td>函数计算，Web+</td></tr></tbody></table></section><section><h3>代码示例：部署到 Heroku<a href="#代码示例部署到-heroku"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 初始化 Git 仓库</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>init</span></div></div><div><div><div>3</div></div><div><span>git</span><span> </span><span>add</span><span> </span><span>.</span></div></div><div><div><div>4</div></div><div><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>"Initial commit"</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 添加 Heroku remote</span></div></div><div><div><div>7</div></div><div><span>heroku</span><span> </span><span>git:remote</span><span> </span><span>-a</span><span> </span><span>my-app</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 部署</span></div></div><div><div><div>10</div></div><div><span>git</span><span> </span><span>push</span><span> </span><span>heroku</span><span> </span><span>main</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 查看日志</span></div></div><div><div><div>13</div></div><div><span>heroku</span><span> </span><span>logs</span><span> </span><span>--tail</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>优缺点<a href="#优缺点-1"><span>#</span></a></h3><p>✅ <strong>优点</strong>：</p><ul>
<li>专注业务逻辑，无需关心基础设施</li>
<li>快速开发和部署</li>
<li>自动扩缩容</li>
<li>内置高可用和备份</li>
</ul><p>❌ <strong>缺点</strong>：</p><ul>
<li>平台锁定风险</li>
<li>自定义能力受限</li>
<li>成本可能高于 IaaS（大规模时）</li>
</ul><hr /></section></section><section><h2>SaaS - 软件即服务<a href="#saas---软件即服务"><span>#</span></a></h2><section><h3>定义<a href="#定义-2"><span>#</span></a></h3><p><strong>Software as a Service</strong> 提供完整的软件应用，用户通过浏览器或客户端直接使用，无需安装和维护。</p></section><section><h3>你管理什么 vs 云厂商管理什么<a href="#你管理什么-vs-云厂商管理什么-2"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>你管理：    （几乎不管理，只配置和使用）</span></div></div><div><div><div>2</div></div><div><span>云厂商管理：应用 + 数据 + 运行时 + 中间件 + 操作系统 + 所有底层</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>核心特征<a href="#核心特征-1"><span>#</span></a></h3><ul>
<li>通过浏览器访问</li>
<li>订阅制付费</li>
<li>自动更新</li>
<li>多租户架构</li>
</ul></section><section><h3>典型场景<a href="#典型场景-2"><span>#</span></a></h3><ul>
<li>办公协作（Office 365、Google Workspace）</li>
<li>CRM（Salesforce）</li>
<li>项目管理（Jira、Trello）</li>
<li>邮件服务（Gmail）</li>
<li>在线会议（Zoom、飞书）</li>
</ul></section><section><h3>主流产品<a href="#主流产品-2"><span>#</span></a></h3>

<table><thead><tr><th>类别</th><th>产品</th></tr></thead><tbody><tr><td>办公套件</td><td>Office 365, Google Workspace, 飞书</td></tr><tr><td>CRM</td><td>Salesforce, HubSpot</td></tr><tr><td>项目管理</td><td>Jira, Asana, Trello</td></tr><tr><td>设计</td><td>Figma, Canva</td></tr><tr><td>客服</td><td>Zendesk, Intercom</td></tr></tbody></table></section><section><h3>优缺点<a href="#优缺点-2"><span>#</span></a></h3><p>✅ <strong>优点</strong>：</p><ul>
<li>开箱即用，零运维</li>
<li>跨设备访问</li>
<li>自动更新和安全补丁</li>
<li>低成本启动</li>
</ul><p>❌ <strong>缺点</strong>：</p><ul>
<li>数据存储在第三方</li>
<li>定制化能力有限</li>
<li>依赖网络连接</li>
<li>长期成本可能较高</li>
</ul><hr /></section></section><section><h2>DaaS - 数据即服务<a href="#daas---数据即服务"><span>#</span></a></h2><section><h3>定义<a href="#定义-3"><span>#</span></a></h3><p><strong>Data as a Service</strong> 提供数据访问、管理和分析服务，用户按需获取数据，无需关心数据存储和处理细节。</p></section><section><h3>核心服务<a href="#核心服务-2"><span>#</span></a></h3><ul>
<li><strong>数据存储</strong>：托管数据库、数据仓库</li>
<li><strong>数据处理</strong>：ETL、数据清洗</li>
<li><strong>数据分析</strong>：BI 工具、报表</li>
<li><strong>数据 API</strong>：通过 API 访问数据</li>
</ul></section><section><h3>典型场景<a href="#典型场景-3"><span>#</span></a></h3><ul>
<li>大数据分析</li>
<li>商业智能（BI）</li>
<li>数据湖/数据仓库</li>
<li>实时数据流处理</li>
<li>机器学习数据管道</li>
</ul></section><section><h3>主流产品<a href="#主流产品-3"><span>#</span></a></h3>

<table><thead><tr><th>厂商</th><th>产品</th></tr></thead><tbody><tr><td>Snowflake</td><td>Data Cloud</td></tr><tr><td>AWS</td><td>Redshift, Athena</td></tr><tr><td>Google</td><td>BigQuery</td></tr><tr><td>阿里云</td><td>MaxCompute, DataWorks</td></tr></tbody></table></section><section><h3>代码示例：查询 BigQuery<a href="#代码示例查询-bigquery"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>from</span><span> google.cloud </span><span>import</span><span> bigquery</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>client </span><span>=</span><span> bigquery.</span><span>Client</span><span>()</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span><span>query </span><span>=</span><span> </span></span><span>"""</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>SELECT</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>user_id,</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>COUNT(*) as order_count,</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>SUM(amount) as total_amount</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>FROM `project.dataset.orders`</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>WHERE created_at &gt;= '2024-01-01'</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>GROUP BY user_id</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>ORDER BY total_amount DESC</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>LIMIT 100</span></div></div><div><div><div>15</div></div><div><span>"""</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>query_job </span><span>=</span><span> client.</span><span>query</span><span>(query)</span></span></div></div><div><div><div>18</div></div><div><span><span>results </span><span>=</span><span> query_job.</span><span>result</span><span>()</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>for</span><span> row </span><span>in</span><span> results:</span></div></div><div><div><div>21</div></div><div><span>    </span><span>print</span><span>(</span><span>f</span><span>"</span><span>{</span><span>row.user_id</span><span>}</span><span>: </span><span>{</span><span>row.order_count</span><span>}</span><span> orders, $</span><span>{</span><span>row.total_amount</span><span>}</span><span>"</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>优缺点<a href="#优缺点-3"><span>#</span></a></h3><p>✅ <strong>优点</strong>：</p><ul>
<li>无需管理数据基础设施</li>
<li>弹性扩展</li>
<li>内置分析和可视化工具</li>
<li>支持多种数据源</li>
</ul><p>❌ <strong>缺点</strong>：</p><ul>
<li>数据迁移成本高</li>
<li>查询成本可能不可控</li>
<li>数据安全和合规要求</li>
</ul><hr /></section></section><section><h2>BaaS - 后端即服务<a href="#baas---后端即服务"><span>#</span></a></h2><section><h3>定义<a href="#定义-4"><span>#</span></a></h3><p><strong>Backend as a Service</strong> 提供现成的后端功能（数据库、认证、存储等），让开发者专注于前端开发。</p></section><section><h3>核心服务<a href="#核心服务-3"><span>#</span></a></h3><ul>
<li><strong>数据库</strong>：实时数据库、NoSQL</li>
<li><strong>用户认证</strong>：登录、注册、权限</li>
<li><strong>文件存储</strong>：图片、视频上传</li>
<li><strong>推送通知</strong>：消息推送</li>
<li><strong>云函数</strong>：无服务器计算</li>
</ul></section><section><h3>典型场景<a href="#典型场景-4"><span>#</span></a></h3><ul>
<li>移动应用快速开发</li>
<li>小程序后端</li>
<li>原型验证（MVP）</li>
<li>物联网应用</li>
<li>实时协作应用</li>
</ul></section><section><h3>主流产品<a href="#主流产品-4"><span>#</span></a></h3>

<table><thead><tr><th>厂商</th><th>产品</th></tr></thead><tbody><tr><td>Firebase</td><td>Firebase Platform</td></tr><tr><td>Supabase</td><td>Supabase</td></tr><tr><td>AWS</td><td>Amplify</td></tr><tr><td>LeanCloud</td><td>LeanCloud</td></tr></tbody></table></section><section><h3>代码示例：使用 Firebase<a href="#代码示例使用-firebase"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 初始化 Firebase</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>initializeApp</span><span> } </span><span>from</span><span> </span><span>'firebase/app'</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>getFirestore</span><span>, </span><span>collection</span><span>, </span><span>addDoc</span><span> } </span><span>from</span><span> </span><span>'firebase/firestore'</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>firebaseConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>apiKey</span><span>:</span><span> </span><span>"your-api-key"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>  </span><span>authDomain</span><span>:</span><span> </span><span>"your-app.firebaseapp.com"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>  </span><span>projectId</span><span>:</span><span> </span><span>"your-project-id"</span></div></div><div><div><div>9</div></div><div><span>};</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>app</span><span> </span><span>=</span><span> </span><span>initializeApp</span><span><span>(</span><span>firebaseConfig</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>const</span><span> </span><span>db</span><span> </span><span>=</span><span> </span><span>getFirestore</span><span><span>(</span><span>app</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 添加数据</span></div></div><div><div><div>15</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>addUser</span><span><span>(</span><span>name</span><span>, </span><span>email</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>  </span><span>const</span><span> </span><span>docRef</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>addDoc</span><span>(</span><span>collection</span><span><span>(</span><span>db</span><span>, </span></span><span>"users"</span><span>), {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>name</span><span>:</span><span><span> </span><span>name</span><span>,</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>email</span><span>:</span><span><span> </span><span>email</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>createdAt</span><span>:</span><span> </span><span>new</span><span> </span><span>Date</span><span>()</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"Document written with ID: "</span><span><span>, </span><span>docRef</span><span>.</span></span><span>id</span><span>);</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>优缺点<a href="#优缺点-4"><span>#</span></a></h3><p>✅ <strong>优点</strong>：</p><ul>
<li>极速开发</li>
<li>无需后端开发经验</li>
<li>内置实时同步</li>
<li>免费额度友好</li>
</ul><p>❌ <strong>缺点</strong>：</p><ul>
<li>平台锁定严重</li>
<li>复杂业务逻辑受限</li>
<li>大规模时成本高</li>
<li>数据导出困难</li>
</ul><hr /></section></section><section><h2>CaaS - 容器即服务<a href="#caas---容器即服务"><span>#</span></a></h2><section><h3>定义<a href="#定义-5"><span>#</span></a></h3><p><strong>Container as a Service</strong> 提供容器化应用的部署、管理和编排服务，基于 Kubernetes 等容器编排技术。</p></section><section><h3>你管理什么 vs 云厂商管理什么<a href="#你管理什么-vs-云厂商管理什么-3"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>你管理：    应用 + 数据 + 容器镜像</span></div></div><div><div><div>2</div></div><div><span>云厂商管理：Kubernetes + 操作系统 + 虚拟化 + 服务器 + 存储 + 网络</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>核心服务<a href="#核心服务-4"><span>#</span></a></h3><ul>
<li><strong>容器编排</strong>：Kubernetes 集群</li>
<li><strong>容器 registry</strong>：镜像仓库</li>
<li><strong>服务网格</strong>：Istio、Linkerd</li>
<li><strong>监控日志</strong>：Prometheus、ELK</li>
</ul></section><section><h3>典型场景<a href="#典型场景-5"><span>#</span></a></h3><ul>
<li>微服务架构</li>
<li>持续集成/持续部署（CI/CD）</li>
<li>混合云部署</li>
<li>需要环境一致性的场景</li>
<li>大规模分布式系统</li>
</ul></section><section><h3>主流产品<a href="#主流产品-5"><span>#</span></a></h3>

<table><thead><tr><th>厂商</th><th>产品</th></tr></thead><tbody><tr><td>AWS</td><td>EKS, ECS</td></tr><tr><td>Google</td><td>GKE</td></tr><tr><td>Azure</td><td>AKS</td></tr><tr><td>阿里云</td><td>ACK</td></tr></tbody></table></section><section><h3>代码示例：Kubernetes 部署 YAML<a href="#代码示例kubernetes-部署-yaml"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>apiVersion</span><span>: </span><span>apps/v1</span></div></div><div><div><div>2</div></div><div><span>kind</span><span>: </span><span>Deployment</span></div></div><div><div><div>3</div></div><div><span>metadata</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>name</span><span>: </span><span>my-app</span></div></div><div><div><div>5</div></div><div><span>spec</span><span>:</span></div></div><div><div><div>6</div></div><div><span>  </span><span>replicas</span><span>: </span><span>3</span></div></div><div><div><div>7</div></div><div><span>  </span><span>selector</span><span>:</span></div></div><div><div><div>8</div></div><div><span>    </span><span>matchLabels</span><span>:</span></div></div><div><div><div>9</div></div><div><span>      </span><span>app</span><span>: </span><span>my-app</span></div></div><div><div><div>10</div></div><div><span>  </span><span>template</span><span>:</span></div></div><div><div><div>11</div></div><div><span>    </span><span>metadata</span><span>:</span></div></div><div><div><div>12</div></div><div><span>      </span><span>labels</span><span>:</span></div></div><div><div><div>13</div></div><div><span>        </span><span>app</span><span>: </span><span>my-app</span></div></div><div><div><div>14</div></div><div><span>    </span><span>spec</span><span>:</span></div></div><div><div><div>15</div></div><div><span>      </span><span>containers</span><span>:</span></div></div><div><div><div>16</div></div><div><span><span>      </span></span><span>- </span><span>name</span><span>: </span><span>my-app</span></div></div><div><div><div>17</div></div><div><span>        </span><span>image</span><span>: </span><span>myregistry/my-app:v1.0</span></div></div><div><div><div>18</div></div><div><span>        </span><span>ports</span><span>:</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>- </span><span>containerPort</span><span>: </span><span>8080</span></div></div><div><div><div>20</div></div><div><span>        </span><span>resources</span><span>:</span></div></div><div><div><div>21</div></div><div><span>          </span><span>requests</span><span>:</span></div></div><div><div><div>22</div></div><div><span>            </span><span>memory</span><span>: </span><span>"256Mi"</span></div></div><div><div><div>23</div></div><div><span>            </span><span>cpu</span><span>: </span><span>"250m"</span></div></div><div><div><div>24</div></div><div><span>          </span><span>limits</span><span>:</span></div></div><div><div><div>25</div></div><div><span>            </span><span>memory</span><span>: </span><span>"512Mi"</span></div></div><div><div><div>26</div></div><div><span>            </span><span>cpu</span><span>: </span><span>"500m"</span></div></div><div><div><div>27</div></div><div><span>---</span></div></div><div><div><div>28</div></div><div><span>apiVersion</span><span>: </span><span>v1</span></div></div><div><div><div>29</div></div><div><span>kind</span><span>: </span><span>Service</span></div></div><div><div><div>30</div></div><div><span>metadata</span><span>:</span></div></div><div><div><div>31</div></div><div><span>  </span><span>name</span><span>: </span><span>my-app-service</span></div></div><div><div><div>32</div></div><div><span>spec</span><span>:</span></div></div><div><div><div>33</div></div><div><span>  </span><span>selector</span><span>:</span></div></div><div><div><div>34</div></div><div><span>    </span><span>app</span><span>: </span><span>my-app</span></div></div><div><div><div>35</div></div><div><span>  </span><span>ports</span><span>:</span></div></div><div><div><div>36</div></div><div><span><span>  </span></span><span>- </span><span>port</span><span>: </span><span>80</span></div></div><div><div><div>37</div></div><div><span>    </span><span>targetPort</span><span>: </span><span>8080</span></div></div><div><div><div>38</div></div><div><span>  </span><span>type</span><span>: </span><span>LoadBalancer</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>优缺点<a href="#优缺点-5"><span>#</span></a></h3><p>✅ <strong>优点</strong>：</p><ul>
<li>环境一致性</li>
<li>快速部署和回滚</li>
<li>资源利用率高</li>
<li>强大的编排能力</li>
</ul><p>❌ <strong>缺点</strong>：</p><ul>
<li>学习曲线陡峭</li>
<li>Kubernetes 复杂度高</li>
<li>运维成本不低</li>
<li>需要容器化改造</li>
</ul><hr /></section></section><section><h2>对比总结<a href="#对比总结"><span>#</span></a></h2><section><h3>管理层级对比<a href="#管理层级对比"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>控制程度：IaaS &gt; PaaS &gt; CaaS &gt; BaaS &gt; DaaS &gt; SaaS</span></div></div><div><div><div>2</div></div><div><span>抽象程度：SaaS &gt; DaaS &gt; BaaS &gt; CaaS &gt; PaaS &gt; IaaS</span></div></div><div><div><div>3</div></div><div><span>灵活性：  IaaS &gt; PaaS &gt; CaaS &gt; BaaS &gt; DaaS &gt; SaaS</span></div></div><div><div><div>4</div></div><div><span>开发速度：SaaS &gt; BaaS &gt; PaaS &gt; CaaS &gt; DaaS &gt; IaaS</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>详细对比表<a href="#详细对比表"><span>#</span></a></h3>

<table><thead><tr><th>模型</th><th>管理内容</th><th>适用场景</th><th>技术门槛</th><th>成本特点</th></tr></thead><tbody><tr><td><strong>IaaS</strong></td><td>应用+OS+中间件</td><td>迁移上云、完全控制</td><td>高</td><td>按资源计费</td></tr><tr><td><strong>PaaS</strong></td><td>应用 + 数据</td><td>Web 应用、快速开发</td><td>中</td><td>按使用量计费</td></tr><tr><td><strong>SaaS</strong></td><td>仅配置</td><td>办公软件、CRM</td><td>低</td><td>订阅制</td></tr><tr><td><strong>DaaS</strong></td><td>数据应用</td><td>大数据分析、BI</td><td>中高</td><td>按查询量计费</td></tr><tr><td><strong>BaaS</strong></td><td>前端应用</td><td>移动应用、MVP</td><td>低中</td><td>按请求计费</td></tr><tr><td><strong>CaaS</strong></td><td>容器化应用</td><td>微服务、DevOps</td><td>高</td><td>按节点计费</td></tr></tbody></table></section><section><h3>可视化对比图<a href="#可视化对比图"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                        你管理的部分                        │</span></div></div><div><div><div>3</div></div><div><span>├──────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│  SaaS  │                    ●                           │</span></div></div><div><div><div>5</div></div><div><span>│  DaaS  │                 ●  ●                           │</span></div></div><div><div><div>6</div></div><div><span>│  BaaS  │              ●  ●  ●                           │</span></div></div><div><div><div>7</div></div><div><span>│  CaaS  │           ●  ●  ●  ●                           │</span></div></div><div><div><div>8</div></div><div><span>│  PaaS  │        ●  ●  ●  ●  ●                           │</span></div></div><div><div><div>9</div></div><div><span>│  IaaS  │     ●  ●  ●  ●  ●  ●  ●                        │</span></div></div><div><div><div>10</div></div><div><span>├────────┼────────────────────────────────────────────────┤</span></div></div><div><div><div>11</div></div><div><span>│        │ 应 数 运 中 操 虚 服 存 网                      │</span></div></div><div><div><div>12</div></div><div><span>│        │ 用 据 行 间 作 拟 务 储 络                      │</span></div></div><div><div><div>13</div></div><div><span>└────────┴────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>如何选择<a href="#如何选择"><span>#</span></a></h2><section><h3>决策树<a href="#决策树"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>开始</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>│</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>├─ 只需要使用软件？ ──────────────→ SaaS</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>│</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>├─ 需要快速开发移动应用？ ────────→ BaaS</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>│</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>├─ 主要做大数据分析？ ────────────→ DaaS</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>│</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>├─ 已经容器化了？ ────────────────→ CaaS</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>│</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>├─ 想专注业务逻辑，不想管运维？ ──→ PaaS</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>│</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>└─ 需要完全控制，或有特殊需求？ ──→ IaaS</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>选择建议<a href="#选择建议"><span>#</span></a></h3><section><h4>选择 IaaS 如果：<a href="#选择-iaas-如果"><span>#</span></a></h4><ul>
<li>需要完全控制操作系统</li>
<li>有特殊的性能或安全要求</li>
<li>正在迁移现有应用到云端</li>
<li>团队有系统管理经验</li>
</ul></section><section><h4>选择 PaaS 如果：<a href="#选择-paas-如果"><span>#</span></a></h4><ul>
<li>快速开发和部署是首要目标</li>
<li>团队规模小，想减少运维负担</li>
<li>应用是标准的 Web 架构</li>
<li>需要自动扩缩容</li>
</ul></section><section><h4>选择 SaaS 如果：<a href="#选择-saas-如果"><span>#</span></a></h4><ul>
<li>需求是通用的（办公、CRM、协作）</li>
<li>不想投入任何运维资源</li>
<li>需要快速上线</li>
<li>预算有限，按订阅付费</li>
</ul></section><section><h4>选择 CaaS 如果：<a href="#选择-caas-如果"><span>#</span></a></h4><ul>
<li>采用微服务架构</li>
<li>需要混合云或多云部署</li>
<li>已有容器化经验</li>
<li>追求 DevOps 最佳实践</li>
</ul></section></section><section><h3>混合使用策略<a href="#混合使用策略"><span>#</span></a></h3><p>实际项目中，经常<strong>混合使用多种模型</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>示例：现代电商架构</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>SaaS: 飞书（办公协作）、Salesforce（CRM）</span></div></div><div><div><div>4</div></div><div><span><span>      </span></span><span>↓</span></div></div><div><div><div>5</div></div><div><span>PaaS: Heroku（营销页面）、Vercel（前端托管）</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>↓</span></div></div><div><div><div>7</div></div><div><span>CaaS: EKS（核心交易系统 - 微服务）</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>↓</span></div></div><div><div><div>9</div></div><div><span>DaaS: Snowflake（数据分析）、Redis Cloud（缓存）</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>↓</span></div></div><div><div><div>11</div></div><div><span>IaaS: EC2（遗留系统、特殊性能需求）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>总结<a href="#总结"><span>#</span></a></h2>

<table><thead><tr><th>关键点</th><th>说明</th></tr></thead><tbody><tr><td><strong>本质</strong></td><td>都是将 IT 能力以服务形式提供，按需使用</td></tr><tr><td><strong>趋势</strong></td><td>从底层基础设施向高层应用抽象演进</td></tr><tr><td><strong>选择</strong></td><td>根据团队能力、业务需求、成本综合考量</td></tr><tr><td><strong>现实</strong></td><td>混合使用多种模型是常态</td></tr></tbody></table><section><h3>学习路线建议<a href="#学习路线建议"><span>#</span></a></h3><ol>
<li><strong>入门</strong>：先从 SaaS 和 PaaS 理解云服务的基本理念</li>
<li><strong>进阶</strong>：学习 IaaS，理解云计算的底层原理</li>
<li><strong>深入</strong>：掌握 CaaS，跟上容器化和云原生趋势</li>
<li><strong>专精</strong>：根据职业方向选择 DaaS（数据工程师）或 BaaS（全栈开发）</li>
</ol><hr /></section></section><section><h2>参考资源<a href="#参考资源"><span>#</span></a></h2><ul>
<li><a href="https://aws.amazon.com/architecture/well-architected/" target="_blank">AWS Well-Architected Framework</a></li>
<li><a href="https://landscape.cncf.io/" target="_blank">CNCF Cloud Native Landscape</a></li>
<li><a href="https://www.gartner.com/" target="_blank">Gartner Magic Quadrant for Cloud Infrastructure</a></li>
<li>《云原生模式》- Leonard Richardson 等著</li>
<li>《凤凰架构》- 周志明 著</li>
</ul><hr /><p><em>本文最后更新于 2026-03-31</em>
*作者：xutopia *</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-bufferevent/</id>
      <title type="text">📤 bufferevent详解 - libevent源码深度解析系列（七）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-bufferevent/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📊 bufferevent详解<a href="#-bufferevent详解"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>在libevent中，bufferevent是一个高级抽象，它封装了底层的I/O事件和缓冲区管理，使得开发者可以更方便地处理网络I/O操作。bufferevent提供了一种机制，允许应用程序在数据可读或可写时得到通知，而不需要手动处理底层的I/O事件。</p><p>bufferevent内部有两个缓冲区：一个用于读取数据，一个用于写入数据。当底层socket有数据可读时，bufferevent会自动将数据读入读缓冲区，并触发读回调；当写缓冲区有数据时，bufferevent会自动将数据写入底层socket，并触发写回调。</p></section><section><h2>二、bufferevent工作流程<a href="#二bufferevent工作流程"><span>#</span></a></h2><section><h3>2.1 bufferevent架构<a href="#21-bufferevent架构"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                    bufferevent                       │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│  ┌────────────┐  ┌────────────┐  ┌────────────┐      │</span></div></div><div><div><div>5</div></div><div><span>│  │  输入缓冲区  │  │  输出缓冲区  │  │  事件回调  │      │</span></div></div><div><div><div>6</div></div><div><span>│  └─────┬──────┘  └─────┬──────┘  └─────┬──────┘      │</span></div></div><div><div><div>7</div></div><div><span>│        │               │               │              │</span></div></div><div><div><div>8</div></div><div><span>├────────┼───────────────┼───────────────┼──────────────┤</span></div></div><div><div><div>9</div></div><div><span>│        │               │               │              │</span></div></div><div><div><div>10</div></div><div><span>├────────▼───────────────▼───────────────▼──────────────┤</span></div></div><div><div><div>11</div></div><div><span>│                 底层event机制                         │</span></div></div><div><div><div>12</div></div><div><span>└─────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 bufferevent数据流程<a href="#22-bufferevent数据流程"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                bufferevent数据流程                   │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│ 1. 网络数据到达                                       │</span></div></div><div><div><div>5</div></div><div><span>│ 2. 底层event触发读事件                                 │</span></div></div><div><div><div>6</div></div><div><span>│ 3. bufferevent自动读取数据到输入缓冲区                    │</span></div></div><div><div><div>7</div></div><div><span>│ 4. 当数据达到读水位时，触发用户读回调                      │</span></div></div><div><div><div>8</div></div><div><span>│ 5. 用户通过bufferevent_read读取数据                    │</span></div></div><div><div><div>9</div></div><div><span>│ 6. 用户通过bufferevent_write写入数据                   │</span></div></div><div><div><div>10</div></div><div><span>│ 7. 数据被写入输出缓冲区                                  │</span></div></div><div><div><div>11</div></div><div><span>│ 8. 当输出缓冲区有数据且可写时，自动写入网络                  │</span></div></div><div><div><div>12</div></div><div><span>│ 9. 当输出缓冲区数据低于写水位时，触发用户写回调                │</span></div></div><div><div><div>13</div></div><div><span>└─────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、bufferevent结构体<a href="#三bufferevent结构体"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> bufferevent {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>be_base;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> bufferevent_ops </span><span>*</span><span>be_ops;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> event ev_read;</span><span>       // 读事件</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> event ev_write;</span><span>      // 写事件</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input;</span><span>     // 读缓冲区</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output;</span><span>    // 写缓冲区</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>bufferevent_data_cb readcb;</span><span>   // 读回调</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>bufferevent_data_cb writecb;</span><span>  // 写回调</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>bufferevent_event_cb errorcb;</span><span> // 错误回调</span></div></div><div><div><div>12</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>cbarg;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> timeval timeout_read;</span><span>  // 读超时</span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> timeval timeout_write;</span><span> // 写超时</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>short</span><span> enabled;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>short</span><span> options;</span></div></div><div><div><div>19</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>四、bufferevent类型<a href="#四bufferevent类型"><span>#</span></a></h2>

<table><thead><tr><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>bufferevent_socket</code></td><td>基于socket的bufferevent</td></tr><tr><td><code>bufferevent_async</code></td><td>Windows IOCP</td></tr><tr><td><code>bufferevent_pair</code></td><td>用于线程间通信</td></tr><tr><td><code>bufferevent_filter</code></td><td>数据过滤</td></tr><tr><td><code>bufferevent_openssl</code></td><td>SSL/TLS加密</td></tr></tbody></table></section><section><h2>四、创建与销毁<a href="#四创建与销毁"><span>#</span></a></h2><section><h3>4.1 创建bufferevent<a href="#41-创建bufferevent"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建基于socket的bufferevent</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base   event_base</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param fd     文件描述符，如果为-1，后续需要调用bufferevent_socket_connect</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param options 选项标志</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>struct</span><span> bufferevent </span><span>*</span><span>bufferevent_socket_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>options</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 释放bufferevent */</span></div></div><div><div><div>10</div></div><div><span>void</span><span> </span><span>bufferevent_free</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 选项标志<a href="#42-选项标志"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>BEV_OPT_CLOSE_ON_FREE</span><span>    </span><span><span>0x</span><span>01</span></span><span>  // 释放时关闭底层文件描述符</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>BEV_OPT_THREADSAFE</span><span>       </span><span><span>0x</span><span>02</span></span><span>  // 使bufferevent线程安全</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>BEV_OPT_DEFER_CALLBACKS</span><span>  </span><span><span>0x</span><span>04</span></span><span>  // 延迟回调执行</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>BEV_OPT_UNLOCK_CALLBACKS</span><span> </span><span><span>0x</span><span>08</span></span><span>  // 回调时不持有锁</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>五、回调函数<a href="#五回调函数"><span>#</span></a></h2><section><h3>5.1 回调函数类型<a href="#51-回调函数类型"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 读/写回调函数类型</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param bev   bufferevent指针</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param ctx   用户提供的参数</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>bufferevent_data_cb)(</span><span>struct</span><span> bufferevent </span><span>*</span><span>bev, </span><span>void</span><span> </span><span>*</span><span>ctx);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 事件回调函数类型</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>* @param bev   bufferevent指针</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>* @param what  事件标志</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>* @param ctx   用户提供的参数</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>12</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>bufferevent_event_cb)(</span><span>struct</span><span> bufferevent </span><span>*</span><span>bev, </span><span>short</span><span> what, </span><span>void</span><span> </span><span>*</span><span>ctx);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.2 事件标志<a href="#52-事件标志"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>BEV_EVENT_READING</span><span>    </span><span><span>0x</span><span>01</span></span><span>  // 读操作时发生</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>BEV_EVENT_WRITING</span><span>    </span><span><span>0x</span><span>02</span></span><span>  // 写操作时发生</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>BEV_EVENT_EOF</span><span>        </span><span><span>0x</span><span>10</span></span><span>  // 遇到文件结束符</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>BEV_EVENT_ERROR</span><span>      </span><span><span>0x</span><span>20</span></span><span>  // 发生错误</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>BEV_EVENT_TIMEOUT</span><span>    </span><span><span>0x</span><span>40</span></span><span>  // 超时</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>BEV_EVENT_CONNECTED</span><span>  </span><span><span>0x</span><span>80</span></span><span>  // 连接成功</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.3 设置回调<a href="#53-设置回调"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置回调函数</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param readcb  读回调，当读缓冲区有数据时触发，NULL表示禁用</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param writecb 写回调，当写缓冲区数据发送完毕时触发，NULL表示禁用</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param eventcb 事件回调，连接、错误等事件</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param cbarg   回调函数的参数</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>void</span><span> </span><span>bufferevent_setcb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>bufferevent_data_cb </span><span>readcb</span><span>,</span><span> bufferevent_data_cb </span><span>writecb</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>bufferevent_event_cb </span><span>eventcb</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>cbarg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、启用和禁用事件<a href="#六启用和禁用事件"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 启用事件</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param event EV_READ, EV_WRITE, 或两者的组合</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>bufferevent_enable</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 禁用事件 */</span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>bufferevent_disable</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 获取当前启用的事件 */</span></div></div><div><div><div>10</div></div><div><span>short</span><span> </span><span>bufferevent_get_enabled</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>七、缓冲区操作<a href="#七缓冲区操作"><span>#</span></a></h2><section><h3>7.1 获取缓冲区<a href="#71-获取缓冲区"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取输入缓冲区（读缓冲区） */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>bufferevent_get_input</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取输出缓冲区（写缓冲区） */</span></div></div><div><div><div>5</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>bufferevent_get_output</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>7.2 读写数据<a href="#72-读写数据"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 向输出缓冲区写入数据 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>bufferevent_write</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>size</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 向输出缓冲区写入格式化数据 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>bufferevent_write_buffer</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 从输入缓冲区读取数据 */</span></div></div><div><div><div>8</div></div><div><span>size_t</span><span> </span><span>bufferevent_read</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>size</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 从输入缓冲区读取数据到evbuffer */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>bufferevent_read_buffer</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>八、水位控制<a href="#八水位控制"><span>#</span></a></h2><p>水位（Watermarks）用于控制缓冲区的大小，当缓冲区中的数据量达到水位时，会触发相应的行为。</p><section><h3>8.1 读水位<a href="#81-读水位"><span>#</span></a></h3>

<table><thead><tr><th>水位类型</th><th>说明</th></tr></thead><tbody><tr><td><strong>低水位</strong></td><td>读缓冲区数据量达到此值时，才会触发读回调。默认为0，表示有数据就触发</td></tr><tr><td><strong>高水位</strong></td><td>读缓冲区数据量达到此值时，停止读取数据，直到缓冲区数据量降低</td></tr></tbody></table></section><section><h3>8.2 写水位<a href="#82-写水位"><span>#</span></a></h3>

<table><thead><tr><th>水位类型</th><th>说明</th></tr></thead><tbody><tr><td><strong>低水位</strong></td><td>写缓冲区数据量低于此值时，才会触发写回调。默认为0</td></tr></tbody></table></section><section><h3>8.3 设置水位<a href="#83-设置水位"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置水位</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param events    EV_READ 或 EV_WRITE</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param lowmark   低水位</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param highmark  高水位，0表示无限制</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>void</span><span> </span><span>bufferevent_setwatermark</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>size_t</span><span><span> </span><span>lowmark</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>highmark</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>8.4 水位示例<a href="#84-水位示例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 设置读高水位为1MB，防止内存耗尽</span></div></div><div><div><div>2</div></div><div><span>bufferevent_setwatermark</span><span><span>(</span><span>bev</span><span>,</span><span> EV_READ</span><span>,</span><span> </span></span><span>0</span><span><span>,</span><span> </span></span><span>1024</span><span>*</span><span>1024</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 设置读低水位为100字节，只有数据量&gt;=100时才触发读回调</span></div></div><div><div><div>5</div></div><div><span>bufferevent_setwatermark</span><span><span>(</span><span>bev</span><span>,</span><span> EV_READ</span><span>,</span><span> </span></span><span>100</span><span><span>,</span><span> </span></span><span>0</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>九、超时设置<a href="#九超时设置"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置超时</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param timeout_read  读超时，NULL表示禁用</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param timeout_write 写超时，NULL表示禁用</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>bufferevent_set_timeouts</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>timeout_read</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>timeout_write</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>十、连接操作<a href="#十连接操作"><span>#</span></a></h2><section><h3>10.1 服务端连接<a href="#101-服务端连接"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取底层socket */</span></div></div><div><div><div>2</div></div><div><span>evutil_socket_t</span><span> </span><span>bufferevent_getfd</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取所属的event_base */</span></div></div><div><div><div>5</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>bufferevent_get_base</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>10.2 客户端连接<a href="#102-客户端连接"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 连接服务器</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param addr 服务器地址</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param socklen 地址长度</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>bufferevent_socket_connect</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 通过主机名连接 */</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>bufferevent_socket_connect_hostname</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>dns_base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>family</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>hostname</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>port</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 获取连接错误码 */</span></div></div><div><div><div>13</div></div><div><span>int</span><span> </span><span>bufferevent_socket_get_dns_error</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>十一、使用示例<a href="#十一使用示例"><span>#</span></a></h2><section><h3>11.1 TCP服务端<a href="#111-tcp服务端"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>// 回显数据</span></div></div><div><div><div>14</div></div><div><span>    </span><span>evbuffer_add_buffer</span><span>(output, input);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Error from bufferevent"</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_conn_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>27</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>33</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>40</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>41</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>42</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(base,</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>accept_conn_cb, </span><span>NULL</span><span>, LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>47</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr</span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>53</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>54</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>11.2 TCP客户端<a href="#112-tcp客户端"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>10</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>fwrite</span><span>(buf, </span><span>1</span><span>, n, stdout);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>19</div></div><div><span>        </span><span>// 发送数据</span></div></div><div><div><div>20</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, </span><span>"Hello, Server!</span><span>\n</span><span>"</span><span>, </span><span>15</span><span>);</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>strerror</span><span>(errno));</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>31</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, </span><span>-</span><span>1</span><span>, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>34</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>37</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>38</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>39</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, </span><span>"127.0.0.1"</span><span>, </span><span>&amp;</span><span>sin</span><span>.</span><span>sin_addr</span><span>);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>bufferevent_socket_connect</span><span>(bev, (</span><span>struct</span><span> sockaddr</span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>47</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-bufferevent-advanced/</id>
      <title type="text">🔗 bufferevent高级特性 - libevent源码深度解析系列（二十四）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-bufferevent-advanced/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔗 bufferevent高级特性<a href="#-bufferevent高级特性"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>除了基本的socket bufferevent，libevent还提供了多种高级bufferevent类型，包括pair（成对连接）、filter（过滤器）等。这些特性使得bufferevent可以用于更复杂的场景。</p></section><section><h2>二、bufferevent_pair<a href="#二bufferevent_pair"><span>#</span></a></h2><section><h3>2.1 概述<a href="#21-概述"><span>#</span></a></h3><p><code>bufferevent_pair</code> 创建两个相互连接的bufferevent，写入其中一个的数据可以从另一个读取。这非常适合进程内通信、测试、以及构建复杂的数据流管道。</p></section><section><h3>2.2 创建API<a href="#22-创建api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>/* 创建一对相互连接的bufferevent</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param base     event_base</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param options  选项标志</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* @param pair     输出数组，存储两个bufferevent</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* @return         0成功，-1失败</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>bufferevent_pair_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>options</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>                         </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span><span>]</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 获取pair的另一个端点 */</span></div></div><div><div><div>13</div></div><div><span>struct</span><span> bufferevent </span><span>*</span><span>bufferevent_pair_get_partner</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.3 基本示例<a href="#23-基本示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>9</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received: </span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 创建一对bufferevent</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>bufferevent_pair_new</span><span>(base, BEV_OPT_CLOSE_ON_FREE, pair) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create pair</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>24</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>// 设置回调</span></div></div><div><div><div>28</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>], read_cb, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>], EV_READ);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>// 从pair[0]写入，pair[1]读取</span></div></div><div><div><div>32</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"Hello from pair[0]!</span><span>\n</span><span>"</span><span>;</span></div></div><div><div><div>33</div></div><div><span>    </span><span>bufferevent_write</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>], msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>// 处理事件</span></div></div><div><div><div>36</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_ONCE);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>// 获取伙伴</span></div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>partner </span><span>=</span><span> </span><span>bufferevent_pair_get_partner</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Partner of pair[0] is pair[1]: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>41</div></div><div><span><span>           </span></span><span>partner </span><span>==</span><span> </span><span>pair</span><span>[</span><span>1</span><span>] </span><span>?</span><span> </span><span>"yes"</span><span> </span><span>:</span><span> </span><span>"no"</span><span>);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>// 释放（只需释放一个，另一个会自动关闭）</span></div></div><div><div><div>44</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.4 进程间通信示例<a href="#24-进程间通信示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;sys/socket.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>on_read_from_stdin</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>partner </span><span>=</span><span> arg;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>12</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>// 转发到伙伴</span></div></div><div><div><div>16</div></div><div><span>        </span><span>bufferevent_write</span><span>(partner, buf, n);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>on_read_from_partner</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>22</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>// 输出到stdout</span></div></div><div><div><div>26</div></div><div><span>        </span><span>write</span><span>(STDOUT_FILENO, </span><span>"Echo: "</span><span>, </span><span>6</span><span>);</span></div></div><div><div><div>27</div></div><div><span>        </span><span>write</span><span>(STDOUT_FILENO, buf, n);</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(base, BEV_OPT_CLOSE_ON_FREE, pair);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>// 创建stdin的bufferevent</span></div></div><div><div><div>38</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>stdin_bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>base, STDIN_FILENO, </span><span>0</span><span>);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>// stdin -&gt; pair[0]</span></div></div><div><div><div>42</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(stdin_bev, on_read_from_stdin, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>bufferevent_enable</span><span>(stdin_bev, EV_READ);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// pair[1] -&gt; stdout</span></div></div><div><div><div>46</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>], on_read_from_partner, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>], EV_READ);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Type something and press Enter (Ctrl+D to exit):</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>bufferevent_free</span><span>(stdin_bev);</span></div></div><div><div><div>54</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>55</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>56</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>59</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、bufferevent_filter<a href="#三bufferevent_filter"><span>#</span></a></h2><section><h3>3.1 概述<a href="#31-概述"><span>#</span></a></h3><p><code>bufferevent_filter</code> 是一种包装器，可以在底层bufferevent之上添加数据转换层。它允许在读写数据时进行自定义处理，如压缩、加密、编码转换等。</p></section><section><h3>3.2 创建API<a href="#32-创建api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>/* 过滤器回调函数类型 */</span></div></div><div><div><div>4</div></div><div><span>typedef</span><span> </span><span>enum</span><span> </span><span>bufferevent_filter_result</span><span><span> </span><span>(</span></span><span>*</span><span><span>bufferevent_filter_cb</span><span>)(</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>src, </span><span>struct</span><span> evbuffer </span><span>*</span><span>dst,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>ev_ssize_t</span><span> dst_limit, </span><span>enum</span><span> bufferevent_flush_mode mode, </span><span>void</span><span> </span><span>*</span><span>ctx);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 创建过滤bufferevent</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>* @param base          event_base</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>* @param underlying    底层bufferevent</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>* @param input_filter  输入过滤器（读取时调用）</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>* @param output_filter 输出过滤器（写入时调用）</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>* @param options       选项标志</span></div></div><div><div><div>14</div></div><div><span><span> </span></span><span>* @param free_ctx      释放上下文的函数</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>* @param ctx           过滤器上下文</span></div></div><div><div><div>16</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>17</div></div><div><span>struct</span><span> bufferevent </span><span>*</span><span>bufferevent_filter_new</span><span>(</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span><span>underlying</span><span>,</span></span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>bufferevent_filter_cb </span><span>input_filter</span><span>,</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>bufferevent_filter_cb </span><span>output_filter</span><span>,</span></div></div><div><div><div>22</div></div><div><span>    </span><span>int</span><span><span> </span><span>options</span><span>,</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>free_ctx</span><span>)(</span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>);</span></span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>/* 过滤器返回值 */</span></div></div><div><div><div>26</div></div><div><span>enum</span><span> bufferevent_filter_result {</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>BEV_OK </span><span>=</span><span> </span><span>0</span><span>,</span><span>      // 成功</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>BEV_NEED_MORE,</span><span>   // 需要更多数据</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>BEV_ERROR</span><span>        // 错误</span></div></div><div><div><div>30</div></div><div><span>};</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>/* 刷新模式 */</span></div></div><div><div><div>33</div></div><div><span>enum</span><span> bufferevent_flush_mode {</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>BEV_NORMAL </span><span>=</span><span> </span><span>0</span><span>,</span><span>  // 正常</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>BEV_FLUSH,</span><span>       // 刷新</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>BEV_FINISHED</span><span>     // 完成</span></div></div><div><div><div>37</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.3 大写转换过滤器示例<a href="#33-大写转换过滤器示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;ctype.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>enum</span><span> bufferevent_filter_result </span><span>uppercase_filter</span><span>(</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>ev_ssize_t</span><span><span> </span><span>dst_limit</span><span>,</span><span> </span></span><span>enum</span><span><span> bufferevent_flush_mode </span><span>mode</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>14</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(src, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>// 转换为大写</span></div></div><div><div><div>18</div></div><div><span>        </span><span>for</span><span> (</span><span>size_t</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n; i</span><span>++</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>            </span><span>buf</span><span>[i] </span><span>=</span><span> </span><span>toupper</span><span>((</span><span>unsigned</span><span> </span><span>char</span><span>)</span><span>buf</span><span>[i]);</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>        </span><span>evbuffer_add</span><span>(dst, buf, n);</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>return</span><span> BEV_OK;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>29</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>32</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>33</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received (filtered): </span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>38</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(base, BEV_OPT_CLOSE_ON_FREE, pair);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>// 在pair[1]上添加过滤器</span></div></div><div><div><div>44</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>filtered </span><span>=</span><span> </span><span>bufferevent_filter_new</span><span>(</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>base, </span><span>pair</span><span>[</span><span>1</span><span>],</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>uppercase_filter,</span><span>  // 输入过滤器</span></div></div><div><div><div>47</div></div><div><span>        </span><span>NULL</span><span>,</span><span>              // 无输出过滤器</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>49</div></div><div><span>        </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(filtered, read_cb, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>bufferevent_enable</span><span>(filtered, EV_READ);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>// 写入小写数据</span></div></div><div><div><div>55</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"hello, world!</span><span>\n</span><span>"</span><span>;</span></div></div><div><div><div>56</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Sending: </span><span>%s</span><span>"</span><span>, msg);</span></div></div><div><div><div>57</div></div><div><span>    </span><span>bufferevent_write</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>], msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_ONCE);</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>bufferevent_free</span><span>(filtered);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>66</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.4 Base64编码过滤器<a href="#34-base64编码过滤器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>const</span><span> </span><span>char</span><span> base64_table</span><span>[]</span><span> </span><span>=</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>enum</span><span> bufferevent_filter_result </span><span>base64_encode_filter</span><span>(</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>ev_ssize_t</span><span><span> </span><span>dst_limit</span><span>,</span><span> </span></span><span>enum</span><span><span> bufferevent_flush_mode </span><span>mode</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>in</span><span>[</span><span>3</span><span>];</span></div></div><div><div><div>16</div></div><div><span>    </span><span>char</span><span> </span><span>out</span><span>[</span><span>4</span><span>];</span></div></div><div><div><div>17</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(src, in, </span><span>3</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>20</div></div><div><span>        </span><span>// 编码</span></div></div><div><div><div>21</div></div><div><span>        </span><span>out</span><span>[</span><span>0</span><span>] </span><span>=</span><span> </span><span>base64_table</span><span>[</span><span>in</span><span>[</span><span>0</span><span>] </span><span>&gt;&gt;</span><span> </span><span>2</span><span>];</span></div></div><div><div><div>22</div></div><div><span>        </span><span>out</span><span>[</span><span>1</span><span>] </span><span>=</span><span> </span><span>base64_table</span><span>[((</span><span>in</span><span>[</span><span>0</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>03</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>4</span><span>) </span><span>|</span><span> (</span><span>in</span><span>[</span><span>1</span><span>] </span><span>&gt;&gt;</span><span> </span><span>4</span><span>)];</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>        </span><span>if</span><span> (n </span><span>&gt;</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>            </span><span>out</span><span>[</span><span>2</span><span>] </span><span>=</span><span> </span><span>base64_table</span><span>[((</span><span>in</span><span>[</span><span>1</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0f</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>2</span><span>) </span><span>|</span><span> (</span><span>in</span><span>[</span><span>2</span><span>] </span><span>&gt;&gt;</span><span> </span><span>6</span><span>)];</span></div></div><div><div><div>26</div></div><div><span>            </span><span>out</span><span>[</span><span>3</span><span>] </span><span>=</span><span> (n </span><span>&gt;</span><span> </span><span>2</span><span>) </span><span>?</span><span> </span><span>base64_table</span><span>[</span><span>in</span><span>[</span><span>2</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>3f</span></span><span>] </span><span>:</span><span> </span><span>'='</span><span>;</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>28</div></div><div><span>            </span><span>out</span><span>[</span><span>2</span><span>] </span><span>=</span><span> </span><span>'='</span><span>;</span></div></div><div><div><div>29</div></div><div><span>            </span><span>out</span><span>[</span><span>3</span><span>] </span><span>=</span><span> </span><span>'='</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>        </span><span>evbuffer_add</span><span>(dst, out, </span><span>4</span><span>);</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>if</span><span> (mode </span><span>==</span><span> BEV_FLUSH </span><span>||</span><span> mode </span><span>==</span><span> BEV_FINISHED) {</span></div></div><div><div><div>36</div></div><div><span>        </span><span>evbuffer_add</span><span>(dst, </span><span>"</span><span>\n</span><span>"</span><span>, </span><span>1</span><span>);</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>return</span><span> BEV_OK;</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>43</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>44</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>47</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>48</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>50</div></div><div><span>}</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>53</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>54</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(base, BEV_OPT_CLOSE_ON_FREE, pair);</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>filtered </span><span>=</span><span> </span><span>bufferevent_filter_new</span><span>(</span></div></div><div><div><div>59</div></div><div><span><span>        </span></span><span>base, </span><span>pair</span><span>[</span><span>1</span><span>],</span></div></div><div><div><div>60</div></div><div><span><span>        </span></span><span>base64_encode_filter,</span></div></div><div><div><div>61</div></div><div><span>        </span><span>NULL</span><span>,</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>63</div></div><div><span>        </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(filtered, read_cb, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>66</div></div><div><span>    </span><span>bufferevent_enable</span><span>(filtered, EV_READ);</span></div></div><div><div><div>67</div></div><div>
</div></div><div><div><div>68</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"Hello, World!"</span><span>;</span></div></div><div><div><div>69</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Original: </span><span>%s</span><span>\n</span><span>"</span><span>, msg);</span></div></div><div><div><div>70</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Base64: "</span><span>);</span></div></div><div><div><div>71</div></div><div><span>    </span><span>bufferevent_write</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>], msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>72</div></div><div>
</div></div><div><div><div>73</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_ONCE);</span></div></div><div><div><div>74</div></div><div>
</div></div><div><div><div>75</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>76</div></div><div><span>    </span><span>bufferevent_free</span><span>(filtered);</span></div></div><div><div><div>77</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>80</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.5 行缓冲过滤器<a href="#35-行缓冲过滤器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>partial_line;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>size_t</span><span> partial_len;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>size_t</span><span> partial_cap;</span></div></div><div><div><div>12</div></div><div><span>} </span><span>line_buffer_ctx_t</span><span>;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>enum</span><span> bufferevent_filter_result </span><span>line_buffer_filter</span><span>(</span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>ev_ssize_t</span><span><span> </span><span>dst_limit</span><span>,</span><span> </span></span><span>enum</span><span><span> bufferevent_flush_mode </span><span>mode</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>line_buffer_ctx_t</span><span> </span><span>*</span><span>lb </span><span>=</span><span> ctx;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>20</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(src, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>for</span><span> (</span><span>size_t</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n; i</span><span>++</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>            </span><span>if</span><span> (</span><span>buf</span><span>[i] </span><span>==</span><span> </span><span>'</span><span>\n</span><span>'</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>                </span><span>// 找到换行符，输出完整行</span></div></div><div><div><div>26</div></div><div><span>                </span><span>if</span><span> (</span><span>lb</span><span>-&gt;</span><span>partial_len</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>27</div></div><div><span>                    </span><span>evbuffer_add</span><span>(dst, </span><span>lb</span><span>-&gt;</span><span>partial_line</span><span>, </span><span>lb</span><span>-&gt;</span><span>partial_len</span><span>);</span></div></div><div><div><div>28</div></div><div><span>                    </span><span>lb</span><span>-&gt;</span><span>partial_len</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span>                </span><span>evbuffer_add</span><span>(dst, </span><span>"</span><span>\n</span><span>"</span><span>, </span><span>1</span><span>);</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>32</div></div><div><span>                </span><span>// 累积字符</span></div></div><div><div><div>33</div></div><div><span>                </span><span>if</span><span> (</span><span>lb</span><span>-&gt;</span><span>partial_len</span><span> </span><span>&gt;=</span><span> </span><span>lb</span><span>-&gt;</span><span>partial_cap</span><span>) {</span></div></div><div><div><div>34</div></div><div><span>                    </span><span>lb</span><span>-&gt;</span><span>partial_cap</span><span> </span><span>=</span><span> </span><span>lb</span><span>-&gt;</span><span>partial_cap</span><span> </span><span>?</span><span> </span><span>lb</span><span>-&gt;</span><span>partial_cap</span><span> </span><span>*</span><span> </span><span>2</span><span> </span><span>:</span><span> </span><span>256</span><span>;</span></div></div><div><div><div>35</div></div><div><span>                    </span><span>lb</span><span>-&gt;</span><span>partial_line</span><span> </span><span>=</span><span> </span><span>realloc</span><span>(</span><span>lb</span><span>-&gt;</span><span>partial_line</span><span>, </span><span>lb</span><span>-&gt;</span><span>partial_cap</span><span>);</span></div></div><div><div><div>36</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>37</div></div><div><span>                </span><span>lb</span><span>-&gt;</span><span>partial_line</span><span>[</span><span>lb</span><span>-&gt;</span><span>partial_len</span><span>++</span><span>] </span><span>=</span><span> </span><span>buf</span><span>[i];</span></div></div><div><div><div>38</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>// 刷新时输出剩余数据</span></div></div><div><div><div>43</div></div><div><span>    </span><span>if</span><span> (mode </span><span>==</span><span> BEV_FLUSH </span><span>&amp;&amp;</span><span> </span><span>lb</span><span>-&gt;</span><span>partial_len</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>44</div></div><div><span>        </span><span>evbuffer_add</span><span>(dst, </span><span>lb</span><span>-&gt;</span><span>partial_line</span><span>, </span><span>lb</span><span>-&gt;</span><span>partial_len</span><span>);</span></div></div><div><div><div>45</div></div><div><span>        </span><span>lb</span><span>-&gt;</span><span>partial_len</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>return</span><span> BEV_OK;</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>free_line_buffer_ctx</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>52</div></div><div><span>    </span><span>line_buffer_ctx_t</span><span> </span><span>*</span><span>lb </span><span>=</span><span> ctx;</span></div></div><div><div><div>53</div></div><div><span>    </span><span>free</span><span>(</span><span>lb</span><span>-&gt;</span><span>partial_line</span><span>);</span></div></div><div><div><div>54</div></div><div><span>    </span><span>free</span><span>(lb);</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、链式过滤器<a href="#四链式过滤器"><span>#</span></a></h2><section><h3>4.1 多层过滤<a href="#41-多层过滤"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;ctype.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>enum</span><span> bufferevent_filter_result </span><span>trim_filter</span><span>(</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>ev_ssize_t</span><span><span> </span><span>dst_limit</span><span>,</span><span> </span></span><span>enum</span><span><span> bufferevent_flush_mode </span><span>mode</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(src, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>size_t</span><span> start </span><span>=</span><span> </span><span>0</span><span>, end </span><span>=</span><span> n;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>        </span><span>// 去除前导空白</span></div></div><div><div><div>19</div></div><div><span>        </span><span>while</span><span> (start </span><span>&lt;</span><span> n </span><span>&amp;&amp;</span><span> </span><span>isspace</span><span>((</span><span>unsigned</span><span> </span><span>char</span><span>)</span><span>buf</span><span>[start])) start</span><span>++</span><span>;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>        </span><span>// 去除尾部空白</span></div></div><div><div><div>22</div></div><div><span>        </span><span>while</span><span> (end </span><span>&gt;</span><span> start </span><span>&amp;&amp;</span><span> </span><span>isspace</span><span>((</span><span>unsigned</span><span> </span><span>char</span><span>)</span><span>buf</span><span>[end </span><span>-</span><span> </span><span>1</span><span>])) end</span><span>--</span><span>;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>        </span><span>if</span><span> (start </span><span>&lt;</span><span> end) {</span></div></div><div><div><div>25</div></div><div><span>            </span><span>evbuffer_add</span><span>(dst, buf </span><span>+</span><span> start, end </span><span>-</span><span> start);</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>return</span><span> BEV_OK;</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>static</span><span> </span><span>enum</span><span> bufferevent_filter_result </span><span>uppercase_filter</span><span>(</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>34</div></div><div><span>    </span><span>ev_ssize_t</span><span><span> </span><span>dst_limit</span><span>,</span><span> </span></span><span>enum</span><span><span> bufferevent_flush_mode </span><span>mode</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>37</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(src, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>40</div></div><div><span>        </span><span>for</span><span> (</span><span>size_t</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n; i</span><span>++</span><span>) {</span></div></div><div><div><div>41</div></div><div><span>            </span><span>buf</span><span>[i] </span><span>=</span><span> </span><span>toupper</span><span>((</span><span>unsigned</span><span> </span><span>char</span><span>)</span><span>buf</span><span>[i]);</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>43</div></div><div><span>        </span><span>evbuffer_add</span><span>(dst, buf, n);</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>return</span><span> BEV_OK;</span></div></div><div><div><div>47</div></div><div><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>51</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(base, BEV_OPT_CLOSE_ON_FREE, pair);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>// 第一层：去除空白</span></div></div><div><div><div>56</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>filter1 </span><span>=</span><span> </span><span>bufferevent_filter_new</span><span>(</span></div></div><div><div><div>57</div></div><div><span><span>        </span></span><span>base, </span><span>pair</span><span>[</span><span>1</span><span>],</span></div></div><div><div><div>58</div></div><div><span><span>        </span></span><span>trim_filter,</span></div></div><div><div><div>59</div></div><div><span>        </span><span>NULL</span><span>,</span></div></div><div><div><div>60</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>61</div></div><div><span>        </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>// 第二层：转大写</span></div></div><div><div><div>64</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>filter2 </span><span>=</span><span> </span><span>bufferevent_filter_new</span><span>(</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>base, filter1,</span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>uppercase_filter,</span></div></div><div><div><div>67</div></div><div><span>        </span><span>NULL</span><span>,</span></div></div><div><div><div>68</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>69</div></div><div><span>        </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>72</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(filter2,</span></div></div><div><div><div>73</div></div><div><span>        </span><span>[]</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span>bev, </span><span>void</span><span> </span><span>*</span><span>arg) {</span></div></div><div><div><div>74</div></div><div><span>            </span><span>char</span><span> </span><span>b</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>75</div></div><div><span>            </span><span>size_t</span><span> n;</span></div></div><div><div><div>76</div></div><div><span>            </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, b, </span><span>sizeof</span><span>(b) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>77</div></div><div><span>                </span><span>b</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>78</div></div><div><span>                </span><span>printf</span><span>(</span><span>"Result: '</span><span>%s</span><span>'</span><span>\n</span><span>"</span><span>, b);</span></div></div><div><div><div>79</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>80</div></div><div><span><span>        </span></span><span>}, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>81</div></div><div><span>    </span><span>bufferevent_enable</span><span>(filter2, EV_READ);</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"  hello world  </span><span>\n</span><span>"</span><span>;</span></div></div><div><div><div>84</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Original: '</span><span>%s</span><span>'</span><span>\n</span><span>"</span><span>, msg);</span></div></div><div><div><div>85</div></div><div><span>    </span><span>bufferevent_write</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>], msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>86</div></div><div>
</div></div><div><div><div>87</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_ONCE);</span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>90</div></div><div><span>    </span><span>bufferevent_free</span><span>(filter2);</span></div></div><div><div><div>91</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>92</div></div><div>
</div></div><div><div><div>93</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>94</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、SSL过滤器<a href="#五ssl过滤器"><span>#</span></a></h2><section><h3>5.1 SSL作为过滤器<a href="#51-ssl作为过滤器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent_ssl.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>SSL_library_init</span><span>();</span></div></div><div><div><div>11</div></div><div><span>    </span><span>SSL_load_error_strings</span><span>();</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 创建SSL上下文</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ssl_ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>TLS_method</span><span>());</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 加载证书和私钥（服务器端）</span></div></div><div><div><div>19</div></div><div><span>    </span><span>SSL_CTX_use_certificate_file</span><span>(ssl_ctx, </span><span>"server.crt"</span><span>, SSL_FILETYPE_PEM);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>SSL_CTX_use_PrivateKey_file</span><span>(ssl_ctx, </span><span>"server.key"</span><span>, SSL_FILETYPE_PEM);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// 创建底层连接</span></div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>24</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(base, BEV_OPT_CLOSE_ON_FREE, pair);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// 在pair[0]上创建SSL客户端</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>SSL </span><span>*</span><span>ssl_client </span><span>=</span><span> </span><span>SSL_new</span><span>(ssl_ctx);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>SSL_set_connect_state</span><span>(ssl_client);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>ssl_bev_client </span><span>=</span><span> </span><span>bufferevent_openssl_filter_new</span><span>(</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>base, </span><span>pair</span><span>[</span><span>0</span><span>], ssl_client, BUFFEREVENT_SSL_CONNECTING,</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>// 在pair[1]上创建SSL服务器</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>SSL </span><span>*</span><span>ssl_server </span><span>=</span><span> </span><span>SSL_new</span><span>(ssl_ctx);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>SSL_set_accept_state</span><span>(ssl_server);</span></div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>ssl_bev_server </span><span>=</span><span> </span><span>bufferevent_openssl_filter_new</span><span>(</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>base, </span><span>pair</span><span>[</span><span>1</span><span>], ssl_server, BUFFEREVENT_SSL_ACCEPTING,</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>// 设置回调</span></div></div><div><div><div>41</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(ssl_bev_server,</span></div></div><div><div><div>42</div></div><div><span>        </span><span>[]</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span>bev, </span><span>void</span><span> </span><span>*</span><span>arg) {</span></div></div><div><div><div>43</div></div><div><span>            </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>44</div></div><div><span>            </span><span>size_t</span><span> n;</span></div></div><div><div><div>45</div></div><div><span>            </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>46</div></div><div><span>                </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>47</div></div><div><span>                </span><span>printf</span><span>(</span><span>"Server received: </span><span>%s</span><span>\n</span><span>"</span><span>, buf);</span></div></div><div><div><div>48</div></div><div><span>                </span><span>bufferevent_write</span><span>(bev, buf, n);</span><span>  // 回显</span></div></div><div><div><div>49</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>}, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>51</div></div><div><span>    </span><span>bufferevent_enable</span><span>(ssl_bev_server, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>// 客户端发送数据</span></div></div><div><div><div>54</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"Hello, SSL!"</span><span>;</span></div></div><div><div><div>55</div></div><div><span>    </span><span>bufferevent_write</span><span>(ssl_bev_client, msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_NONBLOCK);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>bufferevent_free</span><span>(ssl_bev_client);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>bufferevent_free</span><span>(ssl_bev_server);</span></div></div><div><div><div>61</div></div><div><span>    </span><span>SSL_CTX_free</span><span>(ssl_ctx);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>65</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、完整示例：代理服务器<a href="#六完整示例代理服务器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>g_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>client_bev;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>server_bev;</span></div></div><div><div><div>14</div></div><div><span>} </span><span>proxy_conn_t</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>client_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>server_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>client_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span>    </span><span>proxy_conn_t</span><span> </span><span>*</span><span>conn </span><span>=</span><span> arg;</span></div></div><div><div><div>22</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>23</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>26</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Client -&gt; Server: </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>, n);</span></div></div><div><div><div>27</div></div><div><span>        </span><span>bufferevent_write</span><span>(</span><span>conn</span><span>-&gt;</span><span>server_bev</span><span>, buf, n);</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>server_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>32</div></div><div><span>    </span><span>proxy_conn_t</span><span> </span><span>*</span><span>conn </span><span>=</span><span> arg;</span></div></div><div><div><div>33</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>34</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>37</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Server -&gt; Client: </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>, n);</span></div></div><div><div><div>38</div></div><div><span>        </span><span>bufferevent_write</span><span>(</span><span>conn</span><span>-&gt;</span><span>client_bev</span><span>, buf, n);</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>43</div></div><div><span>    </span><span>proxy_conn_t</span><span> </span><span>*</span><span>conn </span><span>=</span><span> arg;</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>46</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>        </span><span>if</span><span> (</span><span>conn</span><span>-&gt;</span><span>client_bev</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>            </span><span>bufferevent_free</span><span>(</span><span>conn</span><span>-&gt;</span><span>client_bev</span><span>);</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>51</div></div><div><span>        </span><span>if</span><span> (</span><span>conn</span><span>-&gt;</span><span>server_bev</span><span>) {</span></div></div><div><div><div>52</div></div><div><span>            </span><span>bufferevent_free</span><span>(</span><span>conn</span><span>-&gt;</span><span>server_bev</span><span>);</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span>        </span><span>free</span><span>(conn);</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>56</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected to upstream server</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>58</div></div><div><span>}</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>61</div></div><div><span>                      </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>62</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New client connection</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>proxy_conn_t</span><span> </span><span>*</span><span>conn </span><span>=</span><span> </span><span>calloc</span><span>(</span><span>1</span><span>, </span><span>sizeof</span><span>(</span><span>proxy_conn_t</span><span>));</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>// 客户端连接</span></div></div><div><div><div>67</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>client_bev</span><span> </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(g_base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>conn</span><span>-&gt;</span><span>client_bev</span><span>, client_read_cb, </span><span>NULL</span><span>, event_cb, conn);</span></div></div><div><div><div>69</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>conn</span><span>-&gt;</span><span>client_bev</span><span>, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>// 连接上游服务器</span></div></div><div><div><div>72</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>server_bev</span><span> </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(g_base, </span><span>-</span><span>1</span><span>, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>73</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>conn</span><span>-&gt;</span><span>server_bev</span><span>, server_read_cb, </span><span>NULL</span><span>, event_cb, conn);</span></div></div><div><div><div>74</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>conn</span><span>-&gt;</span><span>server_bev</span><span>, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>struct</span><span> sockaddr_in server_addr;</span></div></div><div><div><div>77</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>server_addr, </span><span>0</span><span>, </span><span>sizeof</span><span>(server_addr));</span></div></div><div><div><div>78</div></div><div><span>    </span><span>server_addr</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>79</div></div><div><span>    </span><span>server_addr</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>80</span><span>);</span><span>  // 代理到HTTP服务器</span></div></div><div><div><div>80</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, </span><span>"127.0.0.1"</span><span>, </span><span>&amp;</span><span>server_addr</span><span>.</span><span>sin_addr</span><span>);</span></div></div><div><div><div>81</div></div><div>
</div></div><div><div><div>82</div></div><div><span>    </span><span>bufferevent_socket_connect</span><span>(</span><span>conn</span><span>-&gt;</span><span>server_bev</span><span>,</span></div></div><div><div><div>83</div></div><div><span><span>                               </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>server_addr,</span></div></div><div><div><div>84</div></div><div><span>                               </span><span>sizeof</span><span>(server_addr));</span></div></div><div><div><div>85</div></div><div><span>}</span></div></div><div><div><div>86</div></div><div>
</div></div><div><div><div>87</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>88</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Shutting down...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>89</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(g_base);</span></div></div><div><div><div>90</div></div><div><span>}</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>93</div></div><div><span><span>    </span></span><span>g_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>94</div></div><div>
</div></div><div><div><div>95</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigint </span><span>=</span><span> </span><span>evsignal_new</span><span>(g_base, SIGINT, signal_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>96</div></div><div><span>    </span><span>event_add</span><span>(sigint, </span><span>NULL</span><span>);</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>    </span><span>struct</span><span> sockaddr_in listen_addr;</span></div></div><div><div><div>99</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>listen_addr, </span><span>0</span><span>, </span><span>sizeof</span><span>(listen_addr));</span></div></div><div><div><div>100</div></div><div><span>    </span><span>listen_addr</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>101</div></div><div><span>    </span><span>listen_addr</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>102</div></div><div><span>    </span><span>listen_addr</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>103</div></div><div>
</div></div><div><div><div>104</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>105</div></div><div><span><span>        </span></span><span>g_base, accept_cb, </span><span>NULL</span><span>,</span></div></div><div><div><div>106</div></div><div><span><span>        </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>107</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>listen_addr, </span><span>sizeof</span><span>(listen_addr));</span></div></div><div><div><div>108</div></div><div>
</div></div><div><div><div>109</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Proxy server listening on port 8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>110</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Forwarding to 127.0.0.1:80</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>111</div></div><div>
</div></div><div><div><div>112</div></div><div><span>    </span><span>event_base_dispatch</span><span>(g_base);</span></div></div><div><div><div>113</div></div><div>
</div></div><div><div><div>114</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>115</div></div><div><span>    </span><span>event_free</span><span>(sigint);</span></div></div><div><div><div>116</div></div><div><span>    </span><span>event_base_free</span><span>(g_base);</span></div></div><div><div><div>117</div></div><div>
</div></div><div><div><div>118</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>119</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>七、最佳实践<a href="#七最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>特性</th><th>用途</th><th>建议</th></tr></thead><tbody><tr><td><strong>pair</strong></td><td>进程内通信、测试</td><td>适合单元测试和管道构建</td></tr><tr><td><strong>filter</strong></td><td>数据转换</td><td>保持过滤器简单高效</td></tr><tr><td><strong>链式过滤</strong></td><td>多层处理</td><td>注意性能开销</td></tr><tr><td><strong>SSL过滤</strong></td><td>安全通信</td><td>使用filter模式更灵活</td></tr><tr><td><strong>代理</strong></td><td>数据转发</td><td>合理管理连接生命周期</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-core-concepts/</id>
      <title type="text">🔑 核心概念与API - libevent源码深度解析系列（四）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-core-concepts/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🧠 核心概念与API<a href="#-核心概念与api"><span>#</span></a></h1><section><h2>一、事件编程模型<a href="#一事件编程模型"><span>#</span></a></h2><p>在事件驱动编程模型中，主要有三个核心组件：</p><ul>
<li>🔷 <strong>事件源（Event Source）</strong>：产生事件的对象，如网络连接、文件描述符等。</li>
<li>🔷 <strong>事件处理器（Event Handler）</strong>：负责处理事件的回调函数。</li>
<li>🔷 <strong>事件循环（Event Loop）</strong>：负责调度事件处理器的中心组件。</li>
</ul><section><h3>1.1 事件驱动模型流程图<a href="#11-事件驱动模型流程图"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                   事件循环 (event_base)              │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│ 1. 初始化事件基                                     │</span></div></div><div><div><div>5</div></div><div><span>│ 2. 注册事件 (event_new + event_add)                 │</span></div></div><div><div><div>6</div></div><div><span>│ 3. 进入事件循环 (event_base_dispatch)               │</span></div></div><div><div><div>7</div></div><div><span>│    ├─ 等待事件触发                                   │</span></div></div><div><div><div>8</div></div><div><span>│    ├─ 分发事件到对应的处理器                           │</span></div></div><div><div><div>9</div></div><div><span>│    ├─ 执行事件回调                                   │</span></div></div><div><div><div>10</div></div><div><span>│    └─ 重复上述步骤                                   │</span></div></div><div><div><div>11</div></div><div><span>│ 4. 退出事件循环                                     │</span></div></div><div><div><div>12</div></div><div><span>└─────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>1.2 事件处理流程<a href="#12-事件处理流程"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                 事件处理流程                         │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│ 1. 事件源产生事件 (如网络数据到达)                    │</span></div></div><div><div><div>5</div></div><div><span>│ 2. I/O多路复用机制检测到事件                          │</span></div></div><div><div><div>6</div></div><div><span>│ 3. event_base获取就绪事件                            │</span></div></div><div><div><div>7</div></div><div><span>│ 4. 查找对应的event结构体                             │</span></div></div><div><div><div>8</div></div><div><span>│ 5. 将event添加到活跃事件队列                          │</span></div></div><div><div><div>9</div></div><div><span>│ 6. 按优先级处理活跃事件                              │</span></div></div><div><div><div>10</div></div><div><span>│ 7. 调用事件的回调函数                                │</span></div></div><div><div><div>11</div></div><div><span>│ 8. 如果是持久事件(EV_PERSIST)，重新添加到事件队列        │</span></div></div><div><div><div>12</div></div><div><span>└─────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>二、事件基（Event Base）<a href="#二事件基event-base"><span>#</span></a></h2><p>在 <code>libevent</code> 中，事件基（Event Base）是一个核心概念，它是事件驱动编程模型中的事件循环的实现。事件基负责管理所有注册的事件，并在事件发生时调度相应的事件处理器。</p><section><h3>2.1 主要功能<a href="#21-主要功能"><span>#</span></a></h3><ul>
<li>🔶 <strong>事件注册</strong>：将事件注册到事件基</li>
<li>🔶 <strong>事件调度</strong>：在事件发生时调度相应的事件处理器</li>
<li>🔶 <strong>事件循环</strong>：通过 <code>event_base_dispatch()</code> 启动事件循环，处理事件</li>
</ul></section><section><h3>2.2 event_base_new API 详解<a href="#22-event_base_new-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_base_new</span><span>(</span><span>void</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>返回值<a href="#返回值"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回事件基结构体指针</li>
<li><strong>失败</strong>：返回 NULL</li>
</ul></section></section><section><h3>2.3 event_base_new_with_config API 详解<a href="#23-event_base_new_with_config-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_base_new_with_config</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>cfg</td><td>const struct event_config*</td><td>事件基配置结构体</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-1"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回事件基结构体指针</li>
<li><strong>失败</strong>：返回 NULL</li>
</ul></section></section><section><h3>2.4 event_base_dispatch API 详解<a href="#24-event_base_dispatch-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_dispatch</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-1"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>base</td><td>struct event_base*</td><td>要启动的事件基</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-2"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回 0</li>
<li><strong>失败</strong>：返回 -1</li>
</ul></section></section><section><h3>2.5 event_base_loop API 详解<a href="#25-event_base_loop-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_loop</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-2"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>base</td><td>struct event_base*</td><td>要启动的事件基</td></tr><tr><td>flags</td><td>int</td><td>循环标志，如 EVLOOP_ONCE、EVLOOP_NONBLOCK 等</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-3"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回 0</li>
<li><strong>失败</strong>：返回 -1</li>
</ul></section></section><section><h3>2.6 event_base_loopexit API 详解<a href="#26-event_base_loopexit-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_loopexit</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-3"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>base</td><td>struct event_base*</td><td>要退出的事件基</td></tr><tr><td>tv</td><td>const struct timeval*</td><td>退出延迟时间，NULL表示立即退出</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-4"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回 0</li>
<li><strong>失败</strong>：返回 -1</li>
</ul></section></section><section><h3>2.7 event_base_loopbreak API 详解<a href="#27-event_base_loopbreak-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_loopbreak</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-4"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>base</td><td>struct event_base*</td><td>要中断的事件基</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-5"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回 0</li>
<li><strong>失败</strong>：返回 -1</li>
</ul></section></section><section><h3>2.8 event_base_free API 详解<a href="#28-event_base_free-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>event_base_free</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-5"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>base</td><td>struct event_base*</td><td>要释放的事件基</td></tr></tbody></table></section></section><section><h3>2.9 基本操作示例<a href="#29-基本操作示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建事件基</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>3</div></div><div><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 带配置创建事件基</span></div></div><div><div><div>9</div></div><div><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>10</div></div><div><span>event_config_require_features</span><span><span>(</span><span>cfg</span><span>,</span><span> EV_FEATURE_ET</span><span>);</span></span></div></div><div><div><div>11</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>base_with_config </span><span>=</span><span> </span><span>event_base_new_with_config</span><span><span>(</span><span>cfg</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>event_config_free</span><span><span>(</span><span>cfg</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 启动事件循环（阻塞模式）</span></div></div><div><div><div>15</div></div><div><span>event_base_dispatch</span><span><span>(</span><span>base</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// 启动事件循环（非阻塞模式）</span></div></div><div><div><div>18</div></div><div><span>event_base_loop</span><span><span>(</span><span>base</span><span>,</span><span> EVLOOP_NONBLOCK</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 延迟退出事件循环</span></div></div><div><div><div>21</div></div><div><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span><span> // 5秒后退出</span></div></div><div><div><div>22</div></div><div><span>event_base_loopexit</span><span><span>(</span><span>base</span><span>,</span><span> </span></span><span>&amp;</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>// 立即中断事件循环</span></div></div><div><div><div>25</div></div><div><span>event_base_loopbreak</span><span><span>(</span><span>base</span><span>);</span></span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// 释放事件基</span></div></div><div><div><div>28</div></div><div><span>event_base_free</span><span><span>(</span><span>base</span><span>);</span></span></div></div><div><div><div>29</div></div><div><span>event_base_free</span><span><span>(</span><span>base_with_config</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、事件（Event）<a href="#三事件event"><span>#</span></a></h2><p>在 <code>libevent</code> 中，事件（Event）代表了一个具体的事件源及其相关的事件处理器。事件可以绑定到不同的事件源上，如文件描述符、网络套接字等。</p><section><h3>3.1 event_new API 详解<a href="#31-event_new-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event </span><span>*</span><span>event_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>2</div></div><div><span><span>                        </span></span><span>event_callback_fn </span><span>callback</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-6"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>base</td><td>struct event_base*</td><td>事件所属的事件基</td></tr><tr><td>fd</td><td>evutil_socket_t</td><td>事件绑定的文件描述符，对于定时器事件设为-1</td></tr><tr><td>flags</td><td>short</td><td>事件标志，如 EV_READ、EV_WRITE、EV_PERSIST 等</td></tr><tr><td>callback</td><td>event_callback_fn</td><td>事件触发时的回调函数</td></tr><tr><td>arg</td><td>void*</td><td>传递给回调函数的参数</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-6"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回事件结构体指针</li>
<li><strong>失败</strong>：返回 NULL</li>
</ul></section><section><h4>回调函数签名<a href="#回调函数签名"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>event_callback_fn)(</span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>arg);</span></div></div></code></pre><div><div></div><div></div></div></figure></div>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>fd</td><td>evutil_socket_t</td><td>事件绑定的文件描述符</td></tr><tr><td>events</td><td>short</td><td>触发的事件类型，如 EV_READ、EV_WRITE 等</td></tr><tr><td>arg</td><td>void*</td><td>传递给回调函数的参数</td></tr></tbody></table></section></section><section><h3>3.2 event_add API 详解<a href="#32-event_add-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_add</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>timeout</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-7"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>ev</td><td>struct event*</td><td>要添加的事件</td></tr><tr><td>timeout</td><td>const struct timeval*</td><td>超时时间，NULL表示无超时</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-7"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回 0</li>
<li><strong>失败</strong>：返回 -1</li>
</ul></section></section><section><h3>3.3 event_del API 详解<a href="#33-event_del-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_del</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-8"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>ev</td><td>struct event*</td><td>要删除的事件</td></tr></tbody></table></section><section><h4>返回值<a href="#返回值-8"><span>#</span></a></h4><ul>
<li><strong>成功</strong>：返回 0</li>
<li><strong>失败</strong>：返回 -1</li>
</ul></section></section><section><h3>3.4 event_free API 详解<a href="#34-event_free-api-详解"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>event_free</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>参数说明<a href="#参数说明-9"><span>#</span></a></h4>

<table><thead><tr><th>参数</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td>ev</td><td>struct event*</td><td>要释放的事件</td></tr></tbody></table></section></section><section><h3>3.5 事件的主要属性<a href="#35-事件的主要属性"><span>#</span></a></h3>

<table><thead><tr><th>属性</th><th>说明</th></tr></thead><tbody><tr><td><strong>描述符（Descriptor）</strong></td><td>事件绑定的文件描述符或套接字</td></tr><tr><td><strong>标志（Flags）</strong></td><td>表示事件类型和行为的标志位，如 <code>EV_READ</code>、<code>EV_WRITE</code>、<code>EV_PERSIST</code> 等</td></tr><tr><td><strong>回调函数（Callback）</strong></td><td>事件发生时调用的函数</td></tr><tr><td><strong>回调参数（Argument）</strong></td><td>传递给回调函数的参数</td></tr></tbody></table></section><section><h3>3.6 事件操作示例<a href="#36-事件操作示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建事件</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> event </span><span>*</span><span>ev </span><span>=</span><span> </span><span>event_new</span><span><span>(</span><span>base</span><span>,</span><span> fd</span><span>,</span><span> EV_READ </span></span><span>|</span><span><span> EV_PERSIST</span><span>,</span><span> read_callback</span><span>,</span><span> arg</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>if</span><span> (</span><span>!</span><span>ev) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 添加事件（无超时）</span></div></div><div><div><div>9</div></div><div><span>if</span><span> (</span><span>event_add</span><span><span>(</span><span>ev</span><span>,</span><span> </span></span><span>NULL</span><span>) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Failed to add event</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>event_free</span><span>(ev);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 添加事件（带超时）</span></div></div><div><div><div>16</div></div><div><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span><span> // 5秒超时</span></div></div><div><div><div>17</div></div><div><span>if</span><span> (</span><span>event_add</span><span><span>(</span><span>ev</span><span>,</span><span> </span></span><span>&amp;</span><span><span>tv</span><span>) </span></span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Failed to add event with timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>event_free</span><span>(ev);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 删除事件</span></div></div><div><div><div>24</div></div><div><span>if</span><span> (</span><span>event_del</span><span><span>(</span><span>ev</span><span>) </span></span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Failed to delete event</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>// 释放事件</span></div></div><div><div><div>29</div></div><div><span>event_free</span><span><span>(</span><span>ev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、事件处理（Event Handling）<a href="#四事件处理event-handling"><span>#</span></a></h2><p>事件处理是 <code>libevent</code> 的核心功能之一。当事件发生时，事件基会调度相应的事件处理器。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>event_callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>// 处理事件</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>struct</span><span> event </span><span>*</span><span>event </span><span>=</span><span> </span><span>event_new</span><span><span>(</span><span>base</span><span>,</span><span> fd</span><span>,</span><span> EV_READ </span></span><span>|</span><span><span> EV_PERSIST</span><span>,</span><span> event_callback</span><span>,</span><span> arg</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span>event_add</span><span><span>(</span><span>event</span><span>,</span><span> </span></span><span>NULL</span><span>);</span><span>  // 添加事件</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>五、事件标志<a href="#五事件标志"><span>#</span></a></h2><p>事件标志用于指定事件的类型和行为：</p>

<table><thead><tr><th>标志</th><th>值</th><th>说明</th></tr></thead><tbody><tr><td><code>EV_TIMEOUT</code></td><td>0x01</td><td>定时事件</td></tr><tr><td><code>EV_READ</code></td><td>0x02</td><td>读事件，表示当文件描述符可读时触发事件</td></tr><tr><td><code>EV_WRITE</code></td><td>0x04</td><td>写事件，表示当文件描述符可写时触发事件</td></tr><tr><td><code>EV_SIGNAL</code></td><td>0x08</td><td>信号事件</td></tr><tr><td><code>EV_PERSIST</code></td><td>0x10</td><td>持久事件，表示事件在触发后不会自动删除，而是继续保留在事件基中</td></tr><tr><td><code>EV_ET</code></td><td>0x20</td><td>边缘触发模式，仅在数据可用时触发</td></tr></tbody></table></section><section><h2>六、定时器（Timer）<a href="#六定时器timer"><span>#</span></a></h2><p><code>libevent</code> 支持定时器功能，允许开发者在特定的时间间隔内执行任务。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>timer_callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer fired!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// -1 表示定时器不绑定到任何文件描述符</span></div></div><div><div><div>6</div></div><div><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span><span>(</span><span>base</span><span>,</span><span> </span></span><span>-</span><span>1</span><span><span>,</span><span> EV_PERSIST</span><span>,</span><span> timer_callback</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>7</div></div><div><span>struct</span><span> timeval delay </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span><span>  // 设置定时器时间为 5 秒</span></div></div><div><div><div>8</div></div><div><span>event_add</span><span><span>(</span><span>timer</span><span>,</span><span> </span></span><span>&amp;</span><span><span>delay</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>七、信号处理<a href="#七信号处理"><span>#</span></a></h2><p><code>libevent</code> 支持信号处理，允许开发者在接收到特定信号时执行相应的动作。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>signal_handler</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Signal received!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>3</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>struct</span><span> event </span><span>*</span><span>signal_event </span><span>=</span><span> </span><span>evsignal_new</span><span><span>(</span><span>base</span><span>,</span><span> SIGINT</span><span>,</span><span> signal_handler</span><span>,</span><span> base</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span>event_add</span><span><span>(</span><span>signal_event</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>八、多线程支持<a href="#八多线程支持"><span>#</span></a></h2><p><code>libevent</code> 支持多线程环境，允许开发者在多线程程序中安全地使用事件基。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event_base_mutex_lock</span><span><span>(</span><span>base</span><span>);</span></span></div></div><div><div><div>2</div></div><div><span>event_base_mutex_unlock</span><span><span>(</span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>九、事件基的生命周期<a href="#九事件基的生命周期"><span>#</span></a></h2><p>事件基的生命周期包括创建、启动和销毁三个阶段：</p><ol>
<li><strong>创建</strong>：使用 <code>event_base_new()</code> 创建事件基</li>
<li><strong>启动</strong>：使用 <code>event_base_dispatch()</code> 启动事件循环</li>
<li><strong>销毁</strong>：使用 <code>event_base_free()</code> 销毁事件基</li>
</ol></section><section><h2>十、示例代码<a href="#十示例代码"><span>#</span></a></h2><p>下面是一个完整的示例代码，展示了如何使用 <code>libevent</code> 创建一个简单的定时器：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer fired!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 重新调度定时器</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> (</span><span>struct</span><span> event </span><span>*</span><span>)arg;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> timeval delay </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>12</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>delay);</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer_callback, timer);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>timer) {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create timer event</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>28</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>29</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> timeval delay </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>33</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>delay);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>41</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十一、使用注意事项<a href="#十一使用注意事项"><span>#</span></a></h2><section><h3>11.1 事件基的管理和使用<a href="#111-事件基的管理和使用"><span>#</span></a></h3><ul>
<li><strong>单线程原则</strong>：事件基（<code>event_base</code>）的设计是单线程的，即一个事件基只能在一个线程中使用。如果在多线程环境下使用 <code>libevent</code>，每个线程应有自己的事件基实例。</li>
</ul><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>base1 </span><span>=</span><span> </span><span>event_base_new</span><span>();</span><span>  // 第一个线程的事件基</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>base2 </span><span>=</span><span> </span><span>event_base_new</span><span>();</span><span>  // 第二个线程的事件基</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><strong>线程安全</strong>：虽然 <code>libevent</code> 提供了一些线程安全的功能，但在多线程环境中，对事件基的操作（如添加、删除事件）必须确保线程安全。</li>
</ul></section><section><h3>11.2 回调函数的设计<a href="#112-回调函数的设计"><span>#</span></a></h3><ul>
<li><strong>非阻塞性</strong>：事件处理函数（回调函数）不应执行长时间的阻塞操作，否则会影响事件循环的效率。</li>
<li><strong>资源管理</strong>：在事件处理函数中，应妥善管理资源，确保资源的正确释放。</li>
</ul></section><section><h3>11.3 定时器的使用<a href="#113-定时器的使用"><span>#</span></a></h3><ul>
<li><strong>精确度</strong>：定时器的精度取决于底层操作系统提供的定时器机制。在某些情况下，定时器的触发时间可能会有所偏差。</li>
<li><strong>循环定时器</strong>：使用 <code>EV_PERSIST</code> 标志创建的定时器会在每次触发后自动重新调度。</li>
</ul><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-evbuffer/</id>
      <title type="text">📦 evbuffer详解 - libevent源码深度解析系列（十八）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-evbuffer/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📦 evbuffer详解<a href="#-evbuffer详解"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p><code>evbuffer</code> 是libevent提供的动态缓冲区实现，用于高效地管理二进制数据。它是bufferevent的底层缓冲区实现，也可以独立使用。evbuffer提供了丰富的数据操作接口，支持数据的添加、删除、搜索、移动等操作，并且支持零拷贝优化。</p></section><section><h2>二、evbuffer结构体<a href="#二evbuffer结构体"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> evbuffer {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> evbuffer_chain </span><span>*</span><span>first;</span><span>   // 第一个数据块</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> evbuffer_chain </span><span>*</span><span>last;</span><span>    // 最后一个数据块</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> evbuffer_chain </span><span>**</span><span>last_with_datap;</span><span>  // 最后一个包含数据的块</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>size_t</span><span> total_len;</span><span>               // 总数据长度</span></div></div><div><div><div>7</div></div><div><span>    </span><span>size_t</span><span> n_add_for_cb;</span><span>            // 自上次回调以来添加的字节数</span></div></div><div><div><div>8</div></div><div><span>    </span><span>size_t</span><span> n_del_for_cb;</span><span>            // 自上次回调以来删除的字节数</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>int</span><span> freeze_start;</span><span>               // 冻结前端标志</span></div></div><div><div><div>11</div></div><div><span>    </span><span>int</span><span> freeze_end;</span><span>                 // 冻结后端标志</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>TAILQ_HEAD</span><span>(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;</span><span>  // 回调队列</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>ev_base;</span><span>     // 关联的event_base</span></div></div><div><div><div>16</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h3>2.1 evbuffer_chain结构体<a href="#21-evbuffer_chain结构体"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> evbuffer_chain {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> evbuffer_chain </span><span>*</span><span>next;</span><span>    // 下一个链</span></div></div><div><div><div>3</div></div><div><span>    </span><span>size_t</span><span> buffer_len;</span><span>              // 缓冲区总长度</span></div></div><div><div><div>4</div></div><div><span>    </span><span>size_t</span><span> misalign;</span><span>                // 未对齐的字节数（已读但未释放）</span></div></div><div><div><div>5</div></div><div><span>    </span><span>size_t</span><span> off;</span><span>                     // 有效数据长度</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span>buffer;</span><span>          // 数据缓冲区</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、创建与销毁<a href="#三创建与销毁"><span>#</span></a></h2><section><h3>3.1 创建evbuffer<a href="#31-创建evbuffer"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建新的evbuffer */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>evbuffer_new</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 释放evbuffer */</span></div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>evbuffer_free</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 启用线程安全 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evbuffer_enable_locking</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>lock</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 获取锁 */</span></div></div><div><div><div>11</div></div><div><span>void</span><span> </span><span>evbuffer_lock</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>void</span><span> </span><span>evbuffer_unlock</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 基本示例<a href="#32-基本示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>buf) {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create evbuffer</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>9</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Created evbuffer, length: </span><span>%zu</span><span>\n</span><span>"</span><span>, </span><span>evbuffer_get_length</span><span>(buf));</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、数据添加操作<a href="#四数据添加操作"><span>#</span></a></h2><section><h3>4.1 基本添加<a href="#41-基本添加"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加数据到缓冲区末尾 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evbuffer_add</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>datlen</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 添加格式化字符串 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evbuffer_add_printf</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>fmt</span><span>,</span><span> ...</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>evbuffer_add_vprintf</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>fmt</span><span>,</span><span> va_list </span><span>ap</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 添加另一个evbuffer的所有数据（并清空源缓冲区） */</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>evbuffer_add_buffer</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 添加另一个evbuffer的数据（不清空源缓冲区） */</span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>evbuffer_prepend_buffer</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 在缓冲区开头添加数据 */</span></div></div><div><div><div>15</div></div><div><span>int</span><span> </span><span>evbuffer_prepend</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>size</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 添加示例<a href="#42-添加示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>// 添加字符串</span></div></div><div><div><div>8</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Hello, "</span><span>, </span><span>7</span><span>);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"World!"</span><span>, </span><span>6</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 添加格式化字符串</span></div></div><div><div><div>12</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>" Number: </span><span>%d</span><span>"</span><span>, </span><span>42</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Length: </span><span>%zu</span><span>\n</span><span>"</span><span>, </span><span>evbuffer_get_length</span><span>(buf));</span></div></div><div><div><div>15</div></div><div><span>    </span><span>// 输出: Length: 21</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.3 添加文件（零拷贝）<a href="#43-添加文件零拷贝"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加文件内容（零拷贝）</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param fd        文件描述符</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param offset    起始偏移</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param length    长度，-1表示到文件末尾</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>evbuffer_add_file</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>ev_off_t</span><span><span> </span><span>offset</span><span>,</span><span> </span></span><span>ev_off_t</span><span><span> </span><span>length</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 添加文件引用（不复制数据） */</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>evbuffer_add_file_segment</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evbuffer_file_segment </span><span>*</span><span><span>seg</span><span>,</span><span> </span></span><span>ev_off_t</span><span><span> </span><span>offset</span><span>,</span><span> </span></span><span>ev_off_t</span><span><span> </span><span>length</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 创建文件段 */</span></div></div><div><div><div>13</div></div><div><span>struct</span><span> evbuffer_file_segment </span><span>*</span><span>evbuffer_file_segment_new</span><span>(</span></div></div><div><div><div>14</div></div><div><span>    </span><span>int</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>ev_off_t</span><span><span> </span><span>offset</span><span>,</span><span> </span></span><span>ev_off_t</span><span><span> </span><span>length</span><span>,</span><span> </span></span><span>unsigned</span><span><span> </span><span>flags</span><span>);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 释放文件段 */</span></div></div><div><div><div>17</div></div><div><span>void</span><span> </span><span>evbuffer_file_segment_free</span><span>(</span><span>struct</span><span> evbuffer_file_segment </span><span>*</span><span><span>seg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、数据读取操作<a href="#五数据读取操作"><span>#</span></a></h2><section><h3>5.1 基本读取<a href="#51-基本读取"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 从缓冲区读取数据（并移除）</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @return 实际读取的字节数</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>4</div></div><div><span>size_t</span><span> </span><span>evbuffer_remove</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>datlen</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 从缓冲区读取数据到另一个evbuffer */</span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>evbuffer_remove_buffer</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>size_t</span><span><span> </span><span>datlen</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 拷贝数据（不移除） */</span></div></div><div><div><div>11</div></div><div><span>ev_ssize_t</span><span> </span><span>evbuffer_copyout</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>datlen</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>/* 拷贝数据到另一个evbuffer（不移除） */</span></div></div><div><div><div>14</div></div><div><span>int</span><span> </span><span>evbuffer_copyout_to</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>size_t</span><span><span> </span><span>datlen</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 丢弃数据 */</span></div></div><div><div><div>18</div></div><div><span>int</span><span> </span><span>evbuffer_drain</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>len</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 读取示例<a href="#52-读取示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 添加数据</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Hello, World!"</span><span>, </span><span>13</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 读取数据</span></div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>data</span><span>[</span><span>20</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>size_t</span><span> n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(buf, data, </span><span>sizeof</span><span>(data) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>14</div></div><div><span>    </span><span>data</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Read: </span><span>%s</span><span> (</span><span>%zu</span><span> bytes)</span><span>\n</span><span>"</span><span>, data, n);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// 再次添加</span></div></div><div><div><div>18</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Test Data"</span><span>, </span><span>9</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 拷贝不移除</span></div></div><div><div><div>21</div></div><div><span>    </span><span>char</span><span> </span><span>copy</span><span>[</span><span>10</span><span>];</span></div></div><div><div><div>22</div></div><div><span>    </span><span>evbuffer_copyout</span><span>(buf, copy, </span><span>4</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>copy</span><span>[</span><span>4</span><span>] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>24</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Copy: </span><span>%s</span><span>, remaining: </span><span>%zu</span><span>\n</span><span>"</span><span>, copy, </span><span>evbuffer_get_length</span><span>(buf));</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、行读取操作<a href="#六行读取操作"><span>#</span></a></h2><section><h3>6.1 按行读取<a href="#61-按行读取"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 读取一行并移除</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param n_eol_style 行结束符类型</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @return 读取的行（需要调用者free），NULL表示没有完整行</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span>char</span><span> </span><span>*</span><span>evbuffer_readln</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span><span> </span></span><span>size_t</span><span> </span><span>*</span><span><span>n_read_out</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>enum</span><span><span> evbuffer_eol_style </span><span>n_eol_style</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 行结束符类型 */</span></div></div><div><div><div>9</div></div><div><span>enum</span><span> evbuffer_eol_style {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>EVBUFFER_EOL_ANY,</span><span>       // 任意换行符</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>EVBUFFER_EOL_CRLF,</span><span>      // \r\n</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>EVBUFFER_EOL_CRLF_STRICT,</span><span> // 严格的\r\n</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>EVBUFFER_EOL_LF,</span><span>        // \n</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>EVBUFFER_EOL_NUL</span><span>        // \0</span></div></div><div><div><div>15</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.2 行读取示例<a href="#62-行读取示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 添加多行数据</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Line 1</span><span>\n</span><span>Line 2</span><span>\r\n</span><span>Line 3</span><span>\n</span><span>"</span><span>, </span><span>21</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 逐行读取</span></div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>line;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>size_t</span><span> len;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>while</span><span> ((line </span><span>=</span><span> </span><span>evbuffer_readln</span><span>(buf, </span><span>&amp;</span><span>len, EVBUFFER_EOL_ANY)) </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Line: </span><span>%s</span><span> (len=</span><span>%zu</span><span>)</span><span>\n</span><span>"</span><span>, line, len);</span></div></div><div><div><div>17</div></div><div><span>        </span><span>free</span><span>(line);</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、搜索操作<a href="#七搜索操作"><span>#</span></a></h2><section><h3>7.1 搜索API<a href="#71-搜索api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* evbuffer指针结构体 */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evbuffer_ptr {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>ev_ssize_t</span><span> pos;</span><span>         // 位置，-1表示无效</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>void</span><span> </span><span>*</span><span>chain;</span></div></div><div><div><div>7</div></div><div><span>        </span><span>size_t</span><span> pos_in_chain;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>} internal_;</span></div></div><div><div><div>9</div></div><div><span>};</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 搜索字符串 */</span></div></div><div><div><div>12</div></div><div><span>struct</span><span> evbuffer_ptr </span><span>evbuffer_search</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>what</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> evbuffer_ptr </span><span>*</span><span><span>start</span><span>);</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>/* 搜索字符范围 */</span></div></div><div><div><div>16</div></div><div><span>struct</span><span> evbuffer_ptr </span><span>evbuffer_search_range</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>what</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> evbuffer_ptr </span><span>*</span><span><span>start</span><span>,</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> evbuffer_ptr </span><span>*</span><span><span>end</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>/* 搜索行结束符 */</span></div></div><div><div><div>21</div></div><div><span>enum</span><span> evbuffer_eol_style;</span></div></div><div><div><div>22</div></div><div><span>struct</span><span> evbuffer_ptr </span><span>evbuffer_search_eol</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> evbuffer_ptr </span><span>*</span><span><span>start</span><span>,</span><span> </span></span><span>size_t</span><span> </span><span>*</span><span><span>eol_len_out</span><span>,</span></span></div></div><div><div><div>24</div></div><div><span>    </span><span>enum</span><span><span> evbuffer_eol_style </span><span>eol_style</span><span>);</span></span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>/* 移动指针 */</span></div></div><div><div><div>27</div></div><div><span>int</span><span> </span><span>evbuffer_ptr_set</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer_ptr </span><span>*</span><span><span>pos</span><span>,</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>size_t</span><span><span> </span><span>position</span><span>,</span><span> </span></span><span>enum</span><span><span> evbuffer_ptr_how </span><span>how</span><span>);</span></span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>enum</span><span> evbuffer_ptr_how {</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>EVBUFFER_PTR_SET,</span><span>   // 设置绝对位置</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>EVBUFFER_PTR_ADD</span><span>    // 相对当前位置移动</span></div></div><div><div><div>33</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>7.2 搜索示例<a href="#72-搜索示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Hello, World! Hello, libevent!"</span><span>, </span><span>30</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 搜索"Hello"</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evbuffer_ptr ptr </span><span>=</span><span> </span><span>evbuffer_search</span><span>(buf, </span><span>"Hello"</span><span>, </span><span>5</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>ptr</span><span>.</span><span>pos</span><span> </span><span>!=</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Found 'Hello' at position </span><span>%zd</span><span>\n</span><span>"</span><span>, </span><span>ptr</span><span>.</span><span>pos</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 从上次位置之后继续搜索</span></div></div><div><div><div>16</div></div><div><span>    </span><span>evbuffer_ptr_set</span><span>(buf, </span><span>&amp;</span><span>ptr, </span><span>5</span><span>, EVBUFFER_PTR_ADD);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>ptr </span><span>=</span><span> </span><span>evbuffer_search</span><span>(buf, </span><span>"Hello"</span><span>, </span><span>5</span><span>, </span><span>&amp;</span><span>ptr);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (</span><span>ptr</span><span>.</span><span>pos</span><span> </span><span>!=</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Found second 'Hello' at position </span><span>%zd</span><span>\n</span><span>"</span><span>, </span><span>ptr</span><span>.</span><span>pos</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、迭代器操作<a href="#八迭代器操作"><span>#</span></a></h2><section><h3>8.1 迭代器API<a href="#81-迭代器api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建迭代器 */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evbuffer_iovec {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>iov_base;</span><span>    // 数据指针</span></div></div><div><div><div>4</div></div><div><span>    </span><span>size_t</span><span> iov_len;</span><span>    // 数据长度</span></div></div><div><div><div>5</div></div><div><span>};</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 预留空间（用于直接写入） */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evbuffer_reserve_space</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>ev_ssize_t</span><span><span> </span><span>size</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> evbuffer_iovec </span><span>*</span><span><span>vec</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n_vecs</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 提交预留空间 */</span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>evbuffer_commit_space</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> evbuffer_iovec </span><span>*</span><span><span>vec</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n_vecs</span><span>);</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>/* 获取数据指针（用于直接读取） */</span></div></div><div><div><div>16</div></div><div><span>int</span><span> </span><span>evbuffer_peek</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>ev_ssize_t</span><span><span> </span><span>len</span><span>,</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> evbuffer_ptr </span><span>*</span><span><span>start_at</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer_iovec </span><span>*</span><span><span>vec</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n_vecs</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>8.2 直接写入示例<a href="#82-直接写入示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 预留空间</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> evbuffer_iovec </span><span>vec</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>10</div></div><div><span>    </span><span>int</span><span> n </span><span>=</span><span> </span><span>evbuffer_reserve_space</span><span>(buf, </span><span>1024</span><span>, vec, </span><span>2</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 直接写入数据</span></div></div><div><div><div>13</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n; i</span><span>++</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"Direct write data"</span><span>;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>strlen</span><span>(msg);</span></div></div><div><div><div>16</div></div><div><span>        </span><span>if</span><span> (len </span><span>&gt;</span><span> </span><span>vec</span><span>[i].</span><span>iov_len</span><span>) len </span><span>=</span><span> </span><span>vec</span><span>[i].</span><span>iov_len</span><span>;</span></div></div><div><div><div>17</div></div><div><span>        </span><span>memcpy</span><span>(</span><span>vec</span><span>[i].</span><span>iov_base</span><span>, msg, len);</span></div></div><div><div><div>18</div></div><div><span>        </span><span>vec</span><span>[i].</span><span>iov_len</span><span> </span><span>=</span><span> len;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 提交数据</span></div></div><div><div><div>22</div></div><div><span>    </span><span>evbuffer_commit_space</span><span>(buf, vec, n);</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Buffer length: </span><span>%zu</span><span>\n</span><span>"</span><span>, </span><span>evbuffer_get_length</span><span>(buf));</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>九、文件描述符I/O<a href="#九文件描述符io"><span>#</span></a></h2><section><h3>9.1 直接读写文件描述符<a href="#91-直接读写文件描述符"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 从文件描述符读取数据到缓冲区 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evbuffer_read</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>howmuch</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 将缓冲区数据写入文件描述符 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evbuffer_write</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>);</span></span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>evbuffer_write_atmost</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>ev_ssize_t</span><span><span> </span><span>howmuch</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>9.2 文件I/O示例<a href="#92-文件io示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 从标准输入读取</span></div></div><div><div><div>9</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Enter some text (Ctrl+D to end):</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>int</span><span> n </span><span>=</span><span> </span><span>evbuffer_read</span><span>(buf, STDIN_FILENO, </span><span>-</span><span>1</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span> (n </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Read </span><span>%d</span><span> bytes</span><span>\n</span><span>"</span><span>, n);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>        </span><span>// 写入标准输出</span></div></div><div><div><div>16</div></div><div><span>        </span><span>evbuffer_write</span><span>(buf, STDOUT_FILENO);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十、回调函数<a href="#十回调函数"><span>#</span></a></h2><section><h3>10.1 回调API<a href="#101-回调api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 回调函数类型 */</span></div></div><div><div><div>2</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evbuffer_cb_func)(</span><span>struct</span><span> evbuffer </span><span>*</span><span>buffer,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>size_t</span><span> old_len, </span><span>size_t</span><span> new_len, </span><span>void</span><span> </span><span>*</span><span>arg);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 添加回调 */</span></div></div><div><div><div>6</div></div><div><span>struct</span><span> evbuffer_cb_entry </span><span>*</span><span>evbuffer_add_cb</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>evbuffer_cb_func </span><span>cb</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>cbarg</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 移除回调 */</span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>evbuffer_remove_cb_entry</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> evbuffer_cb_entry </span><span>*</span><span><span>ent</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>evbuffer_remove_cb</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span><span> evbuffer_cb_func </span><span>cb</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>cbarg</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 设置回调标志 */</span></div></div><div><div><div>15</div></div><div><span>#define</span><span> </span><span>EVBUFFER_CB_ENABLED</span><span> </span><span>1</span></div></div><div><div><div>16</div></div><div><span>int</span><span> </span><span>evbuffer_cb_set_flags</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> evbuffer_cb_entry </span><span>*</span><span><span>cb</span><span>,</span><span> </span></span><span>ev_uint32_t</span><span><span> </span><span>flags</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span>int</span><span> </span><span>evbuffer_cb_clear_flags</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> evbuffer_cb_entry </span><span>*</span><span><span>cb</span><span>,</span><span> </span></span><span>ev_uint32_t</span><span><span> </span><span>flags</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>10.2 回调示例<a href="#102-回调示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>buffer_cb</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buffer</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>old_len</span><span>,</span></span></div></div><div><div><div>5</div></div><div><span>                      </span><span>size_t</span><span><span> </span><span>new_len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>name </span><span>=</span><span> (</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>)arg;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (new_len </span><span>&gt;</span><span> old_len) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>printf</span><span>(</span><span>"[</span><span>%s</span><span>] Added </span><span>%zu</span><span> bytes (total: </span><span>%zu</span><span>)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>               </span></span><span>name, new_len </span><span>-</span><span> old_len, new_len);</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (new_len </span><span>&lt;</span><span> old_len) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>printf</span><span>(</span><span>"[</span><span>%s</span><span>] Removed </span><span>%zu</span><span> bytes (total: </span><span>%zu</span><span>)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>13</div></div><div><span><span>               </span></span><span>name, old_len </span><span>-</span><span> new_len, new_len);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 添加回调</span></div></div><div><div><div>21</div></div><div><span>    </span><span>evbuffer_add_cb</span><span>(buf, buffer_cb, </span><span>"MyBuffer"</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>// 操作缓冲区</span></div></div><div><div><div>24</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Hello"</span><span>, </span><span>5</span><span>);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>" World"</span><span>, </span><span>6</span><span>);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>char</span><span> </span><span>tmp</span><span>[</span><span>10</span><span>];</span></div></div><div><div><div>28</div></div><div><span>    </span><span>evbuffer_remove</span><span>(buf, tmp, </span><span>5</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>31</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十一、冻结操作<a href="#十一冻结操作"><span>#</span></a></h2><section><h3>11.1 冻结API<a href="#111-冻结api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 冻结缓冲区前端（禁止从头部读取/删除） */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evbuffer_freeze</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>start</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 冻结缓冲区后端（禁止向尾部添加） */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evbuffer_unfreeze</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>start</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>11.2 冻结示例<a href="#112-冻结示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Header"</span><span>, </span><span>6</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, </span><span>"Body"</span><span>, </span><span>4</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>// 冻结前端，保护头部数据</span></div></div><div><div><div>11</div></div><div><span>    </span><span>evbuffer_freeze</span><span>(buf, </span><span>1</span><span>);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>// 尝试从前端读取会失败</span></div></div><div><div><div>14</div></div><div><span>    </span><span>char</span><span> </span><span>tmp</span><span>[</span><span>10</span><span>];</span></div></div><div><div><div>15</div></div><div><span>    </span><span>size_t</span><span> n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(buf, tmp, </span><span>6</span><span>);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Remove from front: </span><span>%zu</span><span> (should be 0)</span><span>\n</span><span>"</span><span>, n);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 解冻</span></div></div><div><div><div>19</div></div><div><span>    </span><span>evbuffer_unfreeze</span><span>(buf, </span><span>1</span><span>);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 现在可以读取</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(buf, tmp, </span><span>6</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Remove after unfreeze: </span><span>%zu</span><span>\n</span><span>"</span><span>, n);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十二、其他实用函数<a href="#十二其他实用函数"><span>#</span></a></h2><section><h3>12.1 信息获取<a href="#121-信息获取"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取缓冲区总长度 */</span></div></div><div><div><div>2</div></div><div><span>size_t</span><span> </span><span>evbuffer_get_length</span><span>(</span><span>const</span><span> </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取连续内存块数量 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evbuffer_get_contiguous_space</span><span>(</span><span>const</span><span> </span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 拉取数据到连续内存（不复制，仅重组） */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evbuffer_pullup</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>ev_ssize_t</span><span><span> </span><span>size</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>12.2 扩展操作<a href="#122-扩展操作"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加引用（不复制数据）</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param data      数据指针</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param datlen    数据长度</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param cleanupfn 清理函数</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param cleanupfn_arg 清理函数参数</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>evbuffer_add_reference</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>datlen</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>cleanupfn</span><span>)(</span></span><span>void</span><span> </span><span>*</span><span><span>extra</span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>cleanupfn_arg</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 添加引用示例 */</span></div></div><div><div><div>12</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>my_cleanup</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> (</span><span>char</span><span> </span><span>*</span><span>)arg;</span></div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Cleaning up: </span><span>%s</span><span>\n</span><span>"</span><span>, data);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>void</span><span> </span><span>add_ref_example</span><span>() {</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>external_data </span><span>=</span><span> </span><span>strdup</span><span>(</span><span>"External data"</span><span>);</span></div></div><div><div><div>22</div></div><div><span>    </span><span>evbuffer_add_reference</span><span>(buf, external_data, </span><span>strlen</span><span>(external_data),</span></div></div><div><div><div>23</div></div><div><span><span>                          </span></span><span>my_cleanup, external_data);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>// 当数据从缓冲区移除时，会调用my_cleanup</span></div></div><div><div><div>26</div></div><div><span>    </span><span>evbuffer_drain</span><span>(buf, </span><span>evbuffer_get_length</span><span>(buf));</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十三、完整示例：HTTP请求解析<a href="#十三完整示例http请求解析"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>char</span><span> </span><span>method</span><span>[</span><span>16</span><span>];</span></div></div><div><div><div>8</div></div><div><span>    </span><span>char</span><span> </span><span>path</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>9</div></div><div><span>    </span><span>char</span><span> </span><span>version</span><span>[</span><span>16</span><span>];</span></div></div><div><div><div>10</div></div><div><span>    </span><span>char</span><span> </span><span>headers</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>11</div></div><div><span>    </span><span>char</span><span> </span><span>body</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>12</div></div><div><span>} </span><span>http_request_t</span><span>;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>int</span><span> </span><span>parse_http_request</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>http_request_t</span><span> </span><span>*</span><span><span>req</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>line;</span></div></div><div><div><div>16</div></div><div><span>    </span><span>size_t</span><span> len;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 读取请求行</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>line </span><span>=</span><span> </span><span>evbuffer_readln</span><span>(buf, </span><span>&amp;</span><span>len, EVBUFFER_EOL_CRLF);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>line) </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>sscanf</span><span>(line, </span><span>"</span><span>%15s</span><span> </span><span>%255s</span><span> </span><span>%15s</span><span>"</span><span>, </span><span>req</span><span>-&gt;</span><span>method</span><span>, </span><span>req</span><span>-&gt;</span><span>path</span><span>, </span><span>req</span><span>-&gt;</span><span>version</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>free</span><span>(line);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>// 读取头部</span></div></div><div><div><div>26</div></div><div><span>    </span><span>while</span><span> ((line </span><span>=</span><span> </span><span>evbuffer_readln</span><span>(buf, </span><span>&amp;</span><span>len, EVBUFFER_EOL_CRLF)) </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>if</span><span> (len </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>28</div></div><div><span>            </span><span>free</span><span>(line);</span></div></div><div><div><div>29</div></div><div><span>            </span><span>break</span><span>;</span><span>  // 空行表示头部结束</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>31</div></div><div><span>        </span><span>strncat</span><span>(</span><span>req</span><span>-&gt;</span><span>headers</span><span>, line, </span><span>sizeof</span><span>(</span><span>req</span><span>-&gt;</span><span>headers</span><span>) </span><span>-</span><span> </span><span>strlen</span><span>(</span><span>req</span><span>-&gt;</span><span>headers</span><span>) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>32</div></div><div><span>        </span><span>strncat</span><span>(</span><span>req</span><span>-&gt;</span><span>headers</span><span>, </span><span>"</span><span>\n</span><span>"</span><span>, </span><span>sizeof</span><span>(</span><span>req</span><span>-&gt;</span><span>headers</span><span>) </span><span>-</span><span> </span><span>strlen</span><span>(</span><span>req</span><span>-&gt;</span><span>headers</span><span>) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>33</div></div><div><span>        </span><span>free</span><span>(line);</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>// 读取body（如果有）</span></div></div><div><div><div>37</div></div><div><span>    </span><span>size_t</span><span> body_len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(buf);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>if</span><span> (body_len </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>39</div></div><div><span>        </span><span>size_t</span><span> n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(buf, </span><span>req</span><span>-&gt;</span><span>body</span><span>,</span></div></div><div><div><div>40</div></div><div><span><span>                                   </span></span><span>body_len </span><span>&lt;</span><span> </span><span>sizeof</span><span>(</span><span>req</span><span>-&gt;</span><span>body</span><span>) </span><span>-</span><span> </span><span>1</span><span> </span><span>?</span><span> body_len </span><span>:</span><span> </span><span>sizeof</span><span>(</span><span>req</span><span>-&gt;</span><span>body</span><span>) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>41</div></div><div><span>        </span><span>req</span><span>-&gt;</span><span>body</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>45</div></div><div><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>48</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>49</div></div><div><span>    </span><span>http_request_t</span><span> req </span><span>=</span><span> {</span><span>0</span><span>};</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>// 模拟HTTP请求</span></div></div><div><div><div>52</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>request </span><span>=</span></div></div><div><div><div>53</div></div><div><span>        </span><span>"GET /index.html HTTP/1.1</span><span>\r\n</span><span>"</span></div></div><div><div><div>54</div></div><div><span>        </span><span>"Host: example.com</span><span>\r\n</span><span>"</span></div></div><div><div><div>55</div></div><div><span>        </span><span>"User-Agent: TestClient</span><span>\r\n</span><span>"</span></div></div><div><div><div>56</div></div><div><span>        </span><span>"</span><span>\r\n</span><span>"</span><span>;</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>evbuffer_add</span><span>(buf, request, </span><span>strlen</span><span>(request));</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>if</span><span> (</span><span>parse_http_request</span><span>(buf, </span><span>&amp;</span><span>req) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>61</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Method: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>req</span><span>.</span><span>method</span><span>);</span></div></div><div><div><div>62</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Path: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>req</span><span>.</span><span>path</span><span>);</span></div></div><div><div><div>63</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Version: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>req</span><span>.</span><span>version</span><span>);</span></div></div><div><div><div>64</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Headers:</span><span>\n</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>req</span><span>.</span><span>headers</span><span>);</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>69</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十四、最佳实践<a href="#十四最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>大数据传输</strong></td><td>使用evbuffer_add_file实现零拷贝</td></tr><tr><td><strong>行协议解析</strong></td><td>使用evbuffer_readln逐行处理</td></tr><tr><td><strong>高性能写入</strong></td><td>使用evbuffer_reserve_space直接写入</td></tr><tr><td><strong>内存敏感</strong></td><td>及时evbuffer_drain释放已处理数据</td></tr><tr><td><strong>线程安全</strong></td><td>使用evbuffer_enable_locking</td></tr><tr><td><strong>数据保护</strong></td><td>使用evbuffer_freeze保护关键数据</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-evdns/</id>
      <title type="text">🌐 evdns异步DNS - libevent源码深度解析系列（十九）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-evdns/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🌐 evdns异步DNS<a href="#-evdns异步dns"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p><code>evdns</code> 是libevent提供的异步DNS解析库，它允许应用程序在不阻塞的情况下进行DNS查询。evdns支持正向解析（域名到IP）、反向解析（IP到域名）、以及运行DNS服务器。</p></section><section><h2>二、evdns_base结构体<a href="#二evdns_base结构体"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> evdns_base {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>event_base;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> evdns_server_port </span><span>**</span><span>server_ports;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>int</span><span> n_server_ports;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> evdns_request </span><span>**</span><span>reqs;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> n_reqs;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> nameserver </span><span>**</span><span>servers;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>int</span><span> n_servers;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> timeval global_nameserver_probe_initial_timeout;</span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> timeval global_nameserver_probe_period;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>16</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>三、创建与销毁<a href="#三创建与销毁"><span>#</span></a></h2><section><h3>3.1 创建evdns_base<a href="#31-创建evdns_base"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建evdns_base</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base            event_base</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param init_nameservers 是否初始化系统DNS服务器</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*                         1: 从/etc/resolv.conf读取</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*                         0: 不初始化，需要手动添加</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>struct</span><span> evdns_base </span><span>*</span><span>evdns_base_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>init_nameservers</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 释放evdns_base</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>* @param fail_requests  是否让pending的请求失败</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>*                       1: 请求回调会被调用并返回错误</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>*                       0: 请求回调不会被调用</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>14</div></div><div><span>void</span><span> </span><span>evdns_base_free</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>fail_requests</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 基本示例<a href="#32-基本示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>dns_base) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create dns base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"evdns_base created successfully</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>17</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、DNS服务器配置<a href="#四dns服务器配置"><span>#</span></a></h2><section><h3>4.1 添加DNS服务器<a href="#41-添加dns服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加DNS服务器（IPv4地址） */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evdns_base_nameserver_add</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>unsigned</span><span> </span><span>long</span><span><span> </span><span>address</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 添加DNS服务器（支持IPv6） */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evdns_base_nameserver_ip_add</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>ip_as_string</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 添加Unix域套接字DNS服务器 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evdns_base_nameserver_sockaddr_add</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa</span><span>,</span><span> </span></span><span>ev_socklen_t</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>unsigned</span><span><span> </span><span>flags</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 清除所有DNS服务器 */</span></div></div><div><div><div>12</div></div><div><span>void</span><span> </span><span>evdns_base_clear_nameservers_and_suspend</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 恢复DNS解析 */</span></div></div><div><div><div>15</div></div><div><span>int</span><span> </span><span>evdns_base_resume</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 设置DNS服务器探测超时 */</span></div></div><div><div><div>18</div></div><div><span>void</span><span> </span><span>evdns_base_set_nameserver_probe_timeout</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>timeout</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 配置示例<a href="#42-配置示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 不自动初始化，手动添加DNS服务器</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>0</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 添加Google DNS (8.8.8.8)</span></div></div><div><div><div>13</div></div><div><span>    </span><span>evdns_base_nameserver_add</span><span>(dns_base, </span><span>inet_addr</span><span>(</span><span>"8.8.8.8"</span><span>));</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 添加Cloudflare DNS (1.1.1.1)</span></div></div><div><div><div>16</div></div><div><span>    </span><span>evdns_base_nameserver_ip_add</span><span>(dns_base, </span><span>"1.1.1.1"</span><span>);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 添加IPv6 DNS服务器</span></div></div><div><div><div>19</div></div><div><span>    </span><span>evdns_base_nameserver_ip_add</span><span>(dns_base, </span><span>"[2001:4860:4860::8888]:53"</span><span>);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>printf</span><span>(</span><span>"DNS servers configured</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、正向DNS解析<a href="#五正向dns解析"><span>#</span></a></h2><section><h3>5.1 解析API<a href="#51-解析api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* DNS回调函数类型</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param result   DNS结果码</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param type     记录类型 (DNS_IPv4_A, DNS_IPv6_AAAA等)</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param count    返回的地址数量</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param ttl      生存时间</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* @param addresses 地址数组</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* @param arg      用户参数</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>9</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evdns_callback_type)(</span><span>int</span><span> result, </span><span>char</span><span> type, </span><span>int</span><span> count,</span></div></div><div><div><div>10</div></div><div><span>                                     </span><span>int</span><span> ttl, </span><span>void</span><span> </span><span>*</span><span>addresses, </span><span>void</span><span> </span><span>*</span><span>arg);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 异步解析IPv4地址</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>* @param name      域名</span></div></div><div><div><div>14</div></div><div><span><span> </span></span><span>* @param flags     解析标志</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>* @param callback  回调函数</span></div></div><div><div><div>16</div></div><div><span><span> </span></span><span>* @param arg       用户参数</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>* @return          evdns_request指针，可用于取消请求</span></div></div><div><div><div>18</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>19</div></div><div><span>struct</span><span> evdns_request </span><span>*</span><span>evdns_base_resolve_ipv4</span><span>(</span></div></div><div><div><div>20</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>evdns_callback_type </span><span>callback</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>/* 异步解析IPv6地址 */</span></div></div><div><div><div>24</div></div><div><span>struct</span><span> evdns_request </span><span>*</span><span>evdns_base_resolve_ipv6</span><span>(</span></div></div><div><div><div>25</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>evdns_callback_type </span><span>callback</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>/* 解析任意记录类型 */</span></div></div><div><div><div>29</div></div><div><span>struct</span><span> evdns_request </span><span>*</span><span>evdns_base_resolve</span><span>(</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>evdns_callback_type </span><span>callback</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 解析标志<a href="#52-解析标志"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>DNS_QUERY_NO_SEARCH</span><span>   </span><span><span>0x</span><span>01</span></span><span>  // 不使用搜索列表</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>DNS_IPv4_A</span><span>            </span><span>1</span><span>     // A记录</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>DNS_IPv6_AAAA</span><span>         </span><span>28</span><span>    // AAAA记录</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>DNS_PTR</span><span>               </span><span>12</span><span>    // PTR记录</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>DNS_CNAME</span><span>             </span><span>5</span><span>     // CNAME记录</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>DNS_MX</span><span>                </span><span>15</span><span>    // MX记录</span></div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>DNS_TXT</span><span>               </span><span>16</span><span>    // TXT记录</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.3 DNS结果码<a href="#53-dns结果码"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>DNS_ERR_NONE</span><span>          </span><span>0</span><span>   // 成功</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>DNS_ERR_FORMAT</span><span>        </span><span>1</span><span>   // 查询格式错误</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>DNS_ERR_SERVERFAILED</span><span>  </span><span>2</span><span>   // 服务器错误</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>DNS_ERR_NOTEXIST</span><span>      </span><span>3</span><span>   // 域名不存在</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>DNS_ERR_NOTIMPL</span><span>       </span><span>4</span><span>   // 未实现</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>DNS_ERR_REFUSED</span><span>       </span><span>5</span><span>   // 拒绝</span></div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>DNS_ERR_TRUNCATED</span><span>     </span><span>6</span><span>   // 响应被截断</span></div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>DNS_ERR_UNKNOWN</span><span>       </span><span>7</span><span>   // 未知错误</span></div></div><div><div><div>9</div></div><div><span>#define</span><span> </span><span>DNS_ERR_TIMEOUT</span><span>       </span><span>8</span><span>   // 超时</span></div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>DNS_ERR_SHUTDOWN</span><span>      </span><span>9</span><span>   // DNS服务关闭</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>DNS_ERR_CANCEL</span><span>        </span><span>10</span><span>  // 请求被取消</span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>DNS_ERR_NODATA</span><span>        </span><span>11</span><span>  // 无数据</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.4 正向解析示例<a href="#54-正向解析示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>dns_callback</span><span>(</span><span>int</span><span><span> </span><span>result</span><span>,</span><span> </span></span><span>char</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>count</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span>                         </span><span>void</span><span> </span><span>*</span><span><span>addresses</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>hostname </span><span>=</span><span> (</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>)arg;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (result </span><span>==</span><span> DNS_ERR_NONE) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Resolved </span><span>%s</span><span>:</span><span>\n</span><span>"</span><span>, hostname);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>        </span><span>if</span><span> (type </span><span>==</span><span> DNS_IPv4_A) {</span></div></div><div><div><div>15</div></div><div><span>            </span><span>struct</span><span> in_addr </span><span>*</span><span>addrs </span><span>=</span><span> (</span><span>struct</span><span> in_addr </span><span>*</span><span>)addresses;</span></div></div><div><div><div>16</div></div><div><span>            </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> count; i</span><span>++</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>                </span><span>printf</span><span>(</span><span>"  IPv4: </span><span>%s</span><span> (TTL: </span><span>%d</span><span>)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>18</div></div><div><span>                       </span><span>inet_ntoa</span><span>(</span><span>addrs</span><span>[i]), ttl);</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (type </span><span>==</span><span> DNS_IPv6_AAAA) {</span></div></div><div><div><div>21</div></div><div><span>            </span><span>char</span><span> </span><span>addr_str</span><span>[INET6_ADDRSTRLEN];</span></div></div><div><div><div>22</div></div><div><span>            </span><span>struct</span><span> in6_addr </span><span>*</span><span>addrs </span><span>=</span><span> (</span><span>struct</span><span> in6_addr </span><span>*</span><span>)addresses;</span></div></div><div><div><div>23</div></div><div><span>            </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> count; i</span><span>++</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>                </span><span>inet_ntop</span><span>(AF_INET6, </span><span>&amp;</span><span>addrs</span><span>[i], addr_str, </span><span>sizeof</span><span>(addr_str));</span></div></div><div><div><div>25</div></div><div><span>                </span><span>printf</span><span>(</span><span>"  IPv6: </span><span>%s</span><span> (TTL: </span><span>%d</span><span>)</span><span>\n</span><span>"</span><span>, addr_str, ttl);</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>29</div></div><div><span>        </span><span>printf</span><span>(</span><span>"DNS lookup failed for </span><span>%s</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>30</div></div><div><span><span>               </span></span><span>hostname, </span><span>evdns_err_to_string</span><span>(result));</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>// 退出事件循环</span></div></div><div><div><div>34</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>*</span><span>argv</span><span>[]</span><span>) {</span></div></div><div><div><div>38</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>hostname </span><span>=</span><span> argc </span><span>&gt;</span><span> </span><span>1</span><span> </span><span>?</span><span> </span><span>argv</span><span>[</span><span>1</span><span>] </span><span>:</span><span> </span><span>"www.google.com"</span><span>;</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>41</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Resolving </span><span>%s</span><span>...</span><span>\n</span><span>"</span><span>, hostname);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// 解析IPv4</span></div></div><div><div><div>46</div></div><div><span>    </span><span>evdns_base_resolve_ipv4</span><span>(dns_base, hostname, </span><span>0</span><span>, dns_callback, (</span><span>void</span><span> </span><span>*</span><span>)hostname);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>51</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>54</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、反向DNS解析<a href="#六反向dns解析"><span>#</span></a></h2><section><h3>6.1 反向解析API<a href="#61-反向解析api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 反向解析IPv4地址 */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evdns_request </span><span>*</span><span>evdns_base_resolve_reverse</span><span>(</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> in_addr </span><span>*</span><span><span>in</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>evdns_callback_type </span><span>callback</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 反向解析IPv6地址 */</span></div></div><div><div><div>7</div></div><div><span>struct</span><span> evdns_request </span><span>*</span><span>evdns_base_resolve_reverse_ipv6</span><span>(</span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> in6_addr </span><span>*</span><span><span>in</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>evdns_callback_type </span><span>callback</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 反向解析示例<a href="#62-反向解析示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>reverse_callback</span><span>(</span><span>int</span><span><span> </span><span>result</span><span>,</span><span> </span></span><span>char</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>count</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>                             </span><span>void</span><span> </span><span>*</span><span><span>addresses</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>if</span><span> (result </span><span>==</span><span> DNS_ERR_NONE) {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>printf</span><span>(</span><span>"PTR records:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>char</span><span> </span><span>**</span><span>names </span><span>=</span><span> (</span><span>char</span><span> </span><span>**</span><span>)addresses;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> count; i</span><span>++</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>            </span><span>printf</span><span>(</span><span>"  </span><span>%s</span><span> (TTL: </span><span>%d</span><span>)</span><span>\n</span><span>"</span><span>, </span><span>names</span><span>[i], ttl);</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Reverse lookup failed: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evdns_err_to_string</span><span>(result));</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>*</span><span>argv</span><span>[]</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>ip_str </span><span>=</span><span> argc </span><span>&gt;</span><span> </span><span>1</span><span> </span><span>?</span><span> </span><span>argv</span><span>[</span><span>1</span><span>] </span><span>:</span><span> </span><span>"8.8.8.8"</span><span>;</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>27</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> in_addr addr;</span></div></div><div><div><div>30</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, ip_str, </span><span>&amp;</span><span>addr);</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Reverse lookup for </span><span>%s</span><span>...</span><span>\n</span><span>"</span><span>, ip_str);</span></div></div><div><div><div>33</div></div><div><span>    </span><span>evdns_base_resolve_reverse</span><span>(dns_base, </span><span>&amp;</span><span>addr, </span><span>0</span><span>, reverse_callback, base);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>41</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、取消DNS请求<a href="#七取消dns请求"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 取消DNS请求 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evdns_cancel_request</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> evdns_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>取消示例<a href="#取消示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>struct</span><span> evdns_request </span><span>*</span><span>pending_req </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>dns_callback</span><span>(</span><span>int</span><span><span> </span><span>result</span><span>,</span><span> </span></span><span>char</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>count</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>                         </span><span>void</span><span> </span><span>*</span><span><span>addresses</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>if</span><span> (result </span><span>==</span><span> DNS_ERR_CANCEL) {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request was cancelled</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (result </span><span>==</span><span> DNS_ERR_NONE) {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>printf</span><span>(</span><span>"DNS resolved successfully</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>printf</span><span>(</span><span>"DNS error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evdns_err_to_string</span><span>(result));</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timeout, cancelling DNS request...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>if</span><span> (pending_req) {</span></div></div><div><div><div>26</div></div><div><span>        </span><span>evdns_cancel_request</span><span>(</span><span>NULL</span><span>, pending_req);</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>pending_req </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>// 发起DNS请求</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>pending_req </span><span>=</span><span> </span><span>evdns_base_resolve_ipv4</span><span>(dns_base, </span><span>"www.example.com"</span><span>,</span></div></div><div><div><div>37</div></div><div><span>                                          </span><span>0</span><span>, dns_callback, base);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>// 设置1秒超时</span></div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, </span><span>0</span><span>, timer_callback, base);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>42</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>51</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、DNS服务器<a href="#八dns服务器"><span>#</span></a></h2><section><h3>8.1 创建DNS服务器<a href="#81-创建dns服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* DNS请求回调函数类型 */</span></div></div><div><div><div>2</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evdns_request_callback_fn_type)(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span>, </span><span>void</span><span> </span><span>*</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 创建DNS服务器端口 */</span></div></div><div><div><div>5</div></div><div><span>struct</span><span> evdns_server_port </span><span>*</span><span>evdns_add_server_port</span><span>(</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>socket</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>evdns_request_callback_fn_type </span><span>cb</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 创建DNS服务器端口（带地址） */</span></div></div><div><div><div>10</div></div><div><span>struct</span><span> evdns_server_port </span><span>*</span><span>evdns_add_server_port_with_listener</span><span>(</span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>evdns_request_callback_fn_type </span><span>cb</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 释放DNS服务器端口 */</span></div></div><div><div><div>15</div></div><div><span>void</span><span> </span><span>evdns_close_server_port</span><span>(</span><span>struct</span><span> evdns_server_port </span><span>*</span><span><span>port</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>8.2 DNS服务器请求处理<a href="#82-dns服务器请求处理"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取请求中的问题数量 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evdns_server_request_get_requesting_addr</span><span>(</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa</span><span>,</span><span> </span></span><span>socklen_t</span><span> </span><span>*</span><span><span>len</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 获取问题 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>evdns_server_request_get_requesting_nameserver_addr</span><span>(</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa</span><span>,</span><span> </span></span><span>socklen_t</span><span> </span><span>*</span><span><span>len</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 回复A记录 */</span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>evdns_server_request_add_a_reply</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>addrs</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>/* 回复AAAA记录 */</span></div></div><div><div><div>14</div></div><div><span>int</span><span> </span><span>evdns_server_request_add_aaaa_reply</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>addrs</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 回复PTR记录 */</span></div></div><div><div><div>18</div></div><div><span>int</span><span> </span><span>evdns_server_request_add_ptr_reply</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> in_addr </span><span>*</span><span><span>in</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>);</span></span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>/* 回复CNAME记录 */</span></div></div><div><div><div>22</div></div><div><span>int</span><span> </span><span>evdns_server_request_add_cname_reply</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>name</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>cname</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>);</span></span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>/* 发送回复 */</span></div></div><div><div><div>26</div></div><div><span>int</span><span> </span><span>evdns_server_request_respond</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>err</span><span>);</span></span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>/* 丢弃请求 */</span></div></div><div><div><div>29</div></div><div><span>int</span><span> </span><span>evdns_server_request_drop</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>8.3 DNS服务器示例<a href="#83-dns服务器示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns_struct.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>dns_server_callback</span><span>(</span><span>struct</span><span> evdns_server_request </span><span>*</span><span><span>request</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>printf</span><span>(</span><span>"DNS request received</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 获取问题</span></div></div><div><div><div>13</div></div><div><span>    </span><span>int</span><span> n_questions </span><span>=</span><span> </span><span>request</span><span>-&gt;</span><span>n_questions</span><span>;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n_questions; i</span><span>++</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>struct</span><span> evdns_server_question </span><span>*</span><span>q </span><span>=</span><span> </span><span>request</span><span>-&gt;</span><span>questions</span><span>[i];</span></div></div><div><div><div>17</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Question </span><span>%d</span><span>: name=</span><span>%s</span><span>, type=</span><span>%d</span><span>, class=</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>18</div></div><div><span><span>               </span></span><span>i, </span><span>q</span><span>-&gt;</span><span>name</span><span>, </span><span>q</span><span>-&gt;</span><span>type</span><span>, </span><span>q</span><span>-&gt;</span><span>dns_question_class</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>        </span><span>// 简单处理：对于特定域名返回固定IP</span></div></div><div><div><div>21</div></div><div><span>        </span><span>if</span><span> (</span><span>q</span><span>-&gt;</span><span>type</span><span> </span><span>==</span><span> EVDNS_TYPE_A </span><span>&amp;&amp;</span><span> </span><span>strcmp</span><span>(</span><span>q</span><span>-&gt;</span><span>name</span><span>, </span><span>"test.local."</span><span>) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>22</div></div><div><span>            </span><span>struct</span><span> in_addr addr;</span></div></div><div><div><div>23</div></div><div><span>            </span><span>inet_pton</span><span>(AF_INET, </span><span>"192.168.1.100"</span><span>, </span><span>&amp;</span><span>addr);</span></div></div><div><div><div>24</div></div><div><span>            </span><span>evdns_server_request_add_a_reply</span><span>(request, </span><span>q</span><span>-&gt;</span><span>name</span><span>, </span><span>1</span><span>, </span><span>&amp;</span><span>addr, </span><span>60</span><span>);</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>q</span><span>-&gt;</span><span>type</span><span> </span><span>==</span><span> EVDNS_TYPE_AAAA </span><span>&amp;&amp;</span><span> </span><span>strcmp</span><span>(</span><span>q</span><span>-&gt;</span><span>name</span><span>, </span><span>"test.local."</span><span>) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>26</div></div><div><span>            </span><span>struct</span><span> in6_addr addr;</span></div></div><div><div><div>27</div></div><div><span>            </span><span>inet_pton</span><span>(AF_INET6, </span><span>"::1"</span><span>, </span><span>&amp;</span><span>addr);</span></div></div><div><div><div>28</div></div><div><span>            </span><span>evdns_server_request_add_aaaa_reply</span><span>(request, </span><span>q</span><span>-&gt;</span><span>name</span><span>, </span><span>1</span><span>, </span><span>&amp;</span><span>addr, </span><span>60</span><span>);</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>30</div></div><div><span>            </span><span>// 其他请求返回NXDOMAIN</span></div></div><div><div><div>31</div></div><div><span>            </span><span>evdns_server_request_respond</span><span>(request, DNS_ERR_NOTEXIST);</span></div></div><div><div><div>32</div></div><div><span>            </span><span>return</span><span>;</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>evdns_server_request_respond</span><span>(request, DNS_ERR_NONE);</span></div></div><div><div><div>37</div></div><div><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>// 创建UDP socket</span></div></div><div><div><div>43</div></div><div><span>    </span><span>int</span><span> fd </span><span>=</span><span> </span><span>socket</span><span>(AF_INET, SOCK_DGRAM, </span><span>0</span><span>);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>evutil_make_socket_nonblocking</span><span>(fd);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>int</span><span> reuse </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>47</div></div><div><span>    </span><span>setsockopt</span><span>(fd, SOL_SOCKET, SO_REUSEADDR, </span><span>&amp;</span><span>reuse, </span><span>sizeof</span><span>(reuse));</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>50</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>51</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>52</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>5353</span><span>);</span><span>  // 使用5353端口</span></div></div><div><div><div>53</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>bind</span><span>(fd, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>// 创建DNS服务器</span></div></div><div><div><div>58</div></div><div><span>    </span><span>struct</span><span> evdns_server_port </span><span>*</span><span>dns_port </span><span>=</span><span> </span><span>evdns_add_server_port</span><span>(</span></div></div><div><div><div>59</div></div><div><span><span>        </span></span><span>base, fd, </span><span>0</span><span>, dns_server_callback, </span><span>NULL</span><span>);</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>printf</span><span>(</span><span>"DNS server listening on port 5353</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Try: nslookup -port=5353 test.local 127.0.0.1</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>evdns_close_server_port</span><span>(dns_port);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>evutil_closesocket</span><span>(fd);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>71</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>九、DNS配置选项<a href="#九dns配置选项"><span>#</span></a></h2><section><h3>9.1 配置API<a href="#91-配置api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置搜索域名 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evdns_base_search_add</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>domain</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 清除搜索域名 */</span></div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>evdns_base_search_clear</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 设置搜索域名数量限制 */</span></div></div><div><div><div>8</div></div><div><span>void</span><span> </span><span>evdns_base_search_ndots_set</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ndots</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 设置是否使用TCP */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>evdns_base_set_option</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>option</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span>                          </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>val</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 常用选项 */</span></div></div><div><div><div>15</div></div><div><span>// "timeout"        - 超时时间（秒）</span></div></div><div><div><div>16</div></div><div><span>// "max-timeouts"   - 最大超时次数</span></div></div><div><div><div>17</div></div><div><span>// "attempts"       - 重试次数</span></div></div><div><div><div>18</div></div><div><span>// "randomize-case" - 随机化大小写（DNS安全）</span></div></div><div><div><div>19</div></div><div><span>// "use-vc"         - 使用TCP</span></div></div><div><div><div>20</div></div><div><span>// "ndots"          - 点的数量阈值</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>9.2 配置示例<a href="#92-配置示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>0</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 添加DNS服务器</span></div></div><div><div><div>10</div></div><div><span>    </span><span>evdns_base_nameserver_add</span><span>(dns_base, </span><span>inet_addr</span><span>(</span><span>"8.8.8.8"</span><span>));</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 设置选项</span></div></div><div><div><div>13</div></div><div><span>    </span><span>evdns_base_set_option</span><span>(dns_base, </span><span>"timeout"</span><span>, </span><span>"5"</span><span>);</span><span>      // 5秒超时</span></div></div><div><div><div>14</div></div><div><span>    </span><span>evdns_base_set_option</span><span>(dns_base, </span><span>"attempts"</span><span>, </span><span>"3"</span><span>);</span><span>     // 重试3次</span></div></div><div><div><div>15</div></div><div><span>    </span><span>evdns_base_set_option</span><span>(dns_base, </span><span>"use-vc"</span><span>, </span><span>NULL</span><span>);</span><span>      // 使用TCP</span></div></div><div><div><div>16</div></div><div><span>    </span><span>evdns_base_set_option</span><span>(dns_base, </span><span>"randomize-case"</span><span>, </span><span>"1"</span><span>);</span><span> // 启用DNS安全</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 添加搜索域名</span></div></div><div><div><div>19</div></div><div><span>    </span><span>evdns_base_search_add</span><span>(dns_base, </span><span>"example.com"</span><span>);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>evdns_base_search_add</span><span>(dns_base, </span><span>"local"</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// 设置ndots</span></div></div><div><div><div>23</div></div><div><span>    </span><span>evdns_base_search_ndots_set</span><span>(dns_base, </span><span>2</span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"DNS configuration complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十、错误处理<a href="#十错误处理"><span>#</span></a></h2><section><h3>10.1 错误字符串<a href="#101-错误字符串"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 将DNS错误码转换为字符串 */</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evdns_err_to_string</span><span>(</span><span>int</span><span><span> </span><span>err</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>10.2 错误处理示例<a href="#102-错误处理示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>void</span><span> </span><span>print_dns_error</span><span>(</span><span>int</span><span><span> </span><span>err</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>switch</span><span> (err) {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>case</span><span> DNS_ERR_NONE:</span></div></div><div><div><div>7</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Success</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>9</div></div><div><span>        </span><span>case</span><span> DNS_ERR_FORMAT:</span></div></div><div><div><div>10</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Query format error</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>12</div></div><div><span>        </span><span>case</span><span> DNS_ERR_SERVERFAILED:</span></div></div><div><div><div>13</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Server failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>14</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>case</span><span> DNS_ERR_NOTEXIST:</span></div></div><div><div><div>16</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Domain name does not exist</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>17</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>18</div></div><div><span>        </span><span>case</span><span> DNS_ERR_TIMEOUT:</span></div></div><div><div><div>19</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Query timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>20</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>21</div></div><div><span>        </span><span>case</span><span> DNS_ERR_CANCEL:</span></div></div><div><div><div>22</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Request cancelled</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>23</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>24</div></div><div><span>        </span><span>default</span><span>:</span></div></div><div><div><div>25</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Unknown error: </span><span>%s</span><span>\n</span><span>"</span><span><span>,</span><span> </span></span><span>evdns_err_to_string</span><span><span>(</span><span>err</span><span>));</span></span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十一、完整示例：异步HTTP客户端DNS解析<a href="#十一完整示例异步http客户端dns解析"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>hostname;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>int</span><span> port;</span></div></div><div><div><div>14</div></div><div><span>} </span><span>http_context_t</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>http_context_t</span><span> </span><span>*</span><span>ctx </span><span>=</span><span> arg;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>20</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected to </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>ctx</span><span>-&gt;</span><span>hostname</span><span>, </span><span>ctx</span><span>-&gt;</span><span>port</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>        </span><span>// 发送HTTP请求</span></div></div><div><div><div>23</div></div><div><span>        </span><span>char</span><span> </span><span>request</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>24</div></div><div><span>        </span><span>snprintf</span><span>(request, </span><span>sizeof</span><span>(request),</span></div></div><div><div><div>25</div></div><div><span>                 </span><span>"GET / HTTP/1.1</span><span>\r\n</span><span>"</span></div></div><div><div><div>26</div></div><div><span>                 </span><span>"Host: </span><span>%s</span><span>\r\n</span><span>"</span></div></div><div><div><div>27</div></div><div><span>                 </span><span>"Connection: close</span><span>\r\n</span><span>"</span></div></div><div><div><div>28</div></div><div><span>                 </span><span>"</span><span>\r\n</span><span>"</span><span>, </span><span>ctx</span><span>-&gt;</span><span>hostname</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, request, </span><span>strlen</span><span>(request));</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>32</div></div><div><span>        </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>33</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Connection error</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>36</div></div><div><span>        </span><span>event_base_loopexit</span><span>(</span><span>ctx</span><span>-&gt;</span><span>base</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>41</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>42</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>45</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>46</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>dns_callback</span><span>(</span><span>int</span><span><span> </span><span>result</span><span>,</span><span> </span></span><span>char</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>count</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>,</span></span></div></div><div><div><div>51</div></div><div><span>                         </span><span>void</span><span> </span><span>*</span><span><span>addresses</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>52</div></div><div><span>    </span><span>http_context_t</span><span> </span><span>*</span><span>ctx </span><span>=</span><span> arg;</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>if</span><span> (result </span><span>!=</span><span> DNS_ERR_NONE </span><span>||</span><span> count </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>55</div></div><div><span>        </span><span>printf</span><span>(</span><span>"DNS lookup failed for </span><span>%s</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>56</div></div><div><span>               </span><span>ctx</span><span>-&gt;</span><span>hostname</span><span>, </span><span>evdns_err_to_string</span><span>(result));</span></div></div><div><div><div>57</div></div><div><span>        </span><span>event_base_loopexit</span><span>(</span><span>ctx</span><span>-&gt;</span><span>base</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>58</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>struct</span><span> in_addr </span><span>*</span><span>addrs </span><span>=</span><span> (</span><span>struct</span><span> in_addr </span><span>*</span><span>)addresses;</span></div></div><div><div><div>62</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Resolved </span><span>%s</span><span> to </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>ctx</span><span>-&gt;</span><span>hostname</span><span>, </span><span>inet_ntoa</span><span>(</span><span>addrs</span><span>[</span><span>0</span><span>]));</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>// 创建连接</span></div></div><div><div><div>65</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(</span><span>ctx</span><span>-&gt;</span><span>base</span><span>, </span><span>-</span><span>1</span><span>,</span></div></div><div><div><div>66</div></div><div><span><span>                                                      </span></span><span>BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, http_read_cb, </span><span>NULL</span><span>, http_event_cb, ctx);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>71</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>72</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>73</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>ctx</span><span>-&gt;</span><span>port</span><span>);</span></div></div><div><div><div>74</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span> </span><span>=</span><span> </span><span>addrs</span><span>[</span><span>0</span><span>];</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>bufferevent_socket_connect</span><span>(bev, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>77</div></div><div><span>}</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>*</span><span>argv</span><span>[]</span><span>) {</span></div></div><div><div><div>80</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>hostname </span><span>=</span><span> argc </span><span>&gt;</span><span> </span><span>1</span><span> </span><span>?</span><span> </span><span>argv</span><span>[</span><span>1</span><span>] </span><span>:</span><span> </span><span>"www.example.com"</span><span>;</span></div></div><div><div><div>81</div></div><div><span>    </span><span>int</span><span> port </span><span>=</span><span> argc </span><span>&gt;</span><span> </span><span>2</span><span> </span><span>?</span><span> </span><span>atoi</span><span>(</span><span>argv</span><span>[</span><span>2</span><span>]) </span><span>:</span><span> </span><span>80</span><span>;</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>84</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>    </span><span>http_context_t</span><span> ctx </span><span>=</span><span> {</span></div></div><div><div><div>87</div></div><div><span><span>        </span></span><span>.base </span><span>=</span><span> base,</span></div></div><div><div><div>88</div></div><div><span><span>        </span></span><span>.dns_base </span><span>=</span><span> dns_base,</span></div></div><div><div><div>89</div></div><div><span><span>        </span></span><span>.hostname </span><span>=</span><span> hostname,</span></div></div><div><div><div>90</div></div><div><span><span>        </span></span><span>.port </span><span>=</span><span> port</span></div></div><div><div><div>91</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>92</div></div><div>
</div></div><div><div><div>93</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Resolving </span><span>%s</span><span>...</span><span>\n</span><span>"</span><span>, hostname);</span></div></div><div><div><div>94</div></div><div><span>    </span><span>evdns_base_resolve_ipv4</span><span>(dns_base, hostname, </span><span>0</span><span>, dns_callback, </span><span>&amp;</span><span>ctx);</span></div></div><div><div><div>95</div></div><div>
</div></div><div><div><div>96</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>99</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>102</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十二、最佳实践<a href="#十二最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>生产环境</strong></td><td>配置多个DNS服务器，提高可用性</td></tr><tr><td><strong>超时设置</strong></td><td>根据网络环境调整超时时间</td></tr><tr><td><strong>错误处理</strong></td><td>实现重试机制和降级策略</td></tr><tr><td><strong>DNS缓存</strong></td><td>应用层实现缓存，减少DNS查询</td></tr><tr><td><strong>IPv6支持</strong></td><td>同时解析IPv4和IPv6地址</td></tr><tr><td><strong>DNS安全</strong></td><td>启用randomize-case防止DNS欺骗</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-event/</id>
      <title type="text">🎭 event详解 - libevent源码深度解析系列（六）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-event/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📦 event详解<a href="#-event详解"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>event是libevent的基本单元。事件添加到event_base中之后，如果触发条件发生（例如，文件描述符的状态发生改变、时间超时等），则事件就会被激活，然后事件中的回调函数就会被执行。如果事件被配置为了持久形的（EV_PERSIST），那么事件仍然在event_base中，继续等待下一次的条件被触发，否则事件就会从event_base中删除。</p></section><section><h2>二、event结构体<a href="#二event结构体"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>// 外部操作ev_evcallback时，是基于宏定义操作的</span></div></div><div><div><div>3</div></div><div><span>    </span><span>#define</span><span> </span><span>ev_pri</span><span> </span><span>ev_evcallback</span><span>.</span><span>evcb_pri</span></div></div><div><div><div>4</div></div><div><span>    </span><span>#define</span><span> </span><span>ev_flags</span><span> </span><span>ev_evcallback</span><span>.</span><span>evcb_flags</span></div></div><div><div><div>5</div></div><div><span>    </span><span>#define</span><span> </span><span>ev_closure</span><span> </span><span>ev_evcallback</span><span>.</span><span>evcb_closure</span></div></div><div><div><div>6</div></div><div><span>    </span><span>#define</span><span> </span><span>ev_callback</span><span> </span><span>ev_evcallback</span><span>.</span><span>evcb_cb_union</span><span>.</span><span>evcb_callback</span></div></div><div><div><div>7</div></div><div><span>    </span><span>#define</span><span> </span><span>ev_arg</span><span> </span><span>ev_evcallback</span><span>.</span><span>evcb_arg</span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event_callback ev_evcallback;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>/* for managing timeouts */</span></div></div><div><div><div>11</div></div><div><span>    </span><span>union</span><span> {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>TAILQ_ENTRY</span><span>(event) ev_next_with_common_timeout;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>int</span><span> min_heap_idx;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>} ev_timeout_pos;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>evutil_socket_t</span><span> ev_fd;</span><span>      // 文件句柄</span></div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>ev_base;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>union</span><span> {</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>/* used for io events */</span></div></div><div><div><div>21</div></div><div><span>        </span><span>struct</span><span> {</span></div></div><div><div><div>22</div></div><div><span>            </span><span>LIST_ENTRY</span><span> (event) ev_io_next;</span></div></div><div><div><div>23</div></div><div><span>            </span><span>struct</span><span> timeval ev_timeout;</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>} ev_io;</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>/* used by signal events */</span></div></div><div><div><div>27</div></div><div><span>        </span><span>struct</span><span> {</span></div></div><div><div><div>28</div></div><div><span>            </span><span>LIST_ENTRY</span><span> (event) ev_signal_next;</span></div></div><div><div><div>29</div></div><div><span>            </span><span>short</span><span> ev_ncalls;</span></div></div><div><div><div>30</div></div><div><span>            </span><span>short</span><span> </span><span>*</span><span>ev_pncalls;</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>} ev_signal;</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>} ev_;</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>short</span><span> ev_events;</span><span>            // 事件集合</span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> timeval ev_timeout;</span></div></div><div><div><div>36</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>三、事件的创建<a href="#三事件的创建"><span>#</span></a></h2><section><h3>3.1 回调函数类型<a href="#31-回调函数类型"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>fd</span><span>    An fd or signal</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>events</span><span> One or more EV_* flags</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>arg</span><span>   A user-supplied argument</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>event_callback_fn)(</span><span>evutil_socket_t</span><span>, </span><span>short</span><span>, </span><span>void</span><span> </span><span>*</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 创建函数<a href="#32-创建函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建通用事件处理器</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base     指定新创建的事件处理器从属的Reactor</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param fd       指定与事件处理器关联的句柄。创建I/O事件处理器时，传递文件描述符；</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*                 创建信号事件处理器时，传递信号值；创建定时事件处理器时传递-1</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param events   指定事件类型，详细见EV_XXX</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* @param cb       指定目标事件对应的回调函数</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* @param arg      Reactor传递给回调函数的参数</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>9</div></div><div><span>struct</span><span> event </span><span>*</span><span>event_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span>,</span><span> </span></span><span>short</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>                        </span></span><span>event_callback_fn</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span>);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 设置event参数，函数内部同时会设置事件的base */</span></div></div><div><div><div>13</div></div><div><span>void</span><span> </span><span>event_set</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span>,</span><span> </span></span><span>short</span><span>,</span></div></div><div><div><div>14</div></div><div><span>               </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span>)(</span><span>evutil_socket_t</span><span><span>,</span><span> </span></span><span>short</span><span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 同时设置事件的event_base</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>* 不要对已经在event_base中的事件调用event_assign()，这可能会导致错误</span></div></div><div><div><div>18</div></div><div><span><span> </span></span><span>* 如果已经添加进来了，需要调用event_assign()之前先调用event_del()</span></div></div><div><div><div>19</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>20</div></div><div><span>int</span><span> </span><span>event_assign</span><span>(</span><span>struct</span><span> event</span><span>*</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>struct</span><span> event_base </span><span>*</span><span><span> </span><span>base</span><span>,</span></span></div></div><div><div><div>21</div></div><div><span>                 </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>what</span><span>,</span></span></div></div><div><div><div>22</div></div><div><span>                 </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>callback</span><span>)(</span></span><span>evutil_socket_t</span><span><span>,</span><span> </span></span><span>short</span><span><span>,</span><span> </span></span><span>void*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span> </span><span>arg</span><span>);</span></span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>/* 释放事件资源 */</span></div></div><div><div><div>25</div></div><div><span>void</span><span> </span><span>event_free</span><span>(</span><span>struct</span><span> event </span><span>*</span><span>);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>/* 返回sizeof(struct event) */</span></div></div><div><div><div>28</div></div><div><span>size_t</span><span> </span><span>event_get_struct_event_size</span><span>(</span><span>void</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.3 添加和删除事件<a href="#33-添加和删除事件"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 如果 tv 为 NULL，添加的事件不会超时。否则，tv以秒和微秒指定超时值 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>event_add</span><span>(</span><span>struct</span><span> event</span><span>*</span><span><span> </span><span>ev</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span> </span><span>tv</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>int</span><span> </span><span>event_del</span><span>(</span><span>struct</span><span> event</span><span>*</span><span><span> </span><span>ev</span><span>);</span></span></div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>event_remove_timer</span><span>(</span><span>struct</span><span> event</span><span>*</span><span><span> </span><span>ev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>四、事件的信息获取<a href="#四事件的信息获取"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 获取信号事件的信号值</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>event_get_signal</span><span><span>(</span><span>ev</span><span>) ((</span></span><span>int</span><span>)</span><span>event_get_fd</span><span><span>(ev)</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 获取信号事件的信号值，或者IO事件的描述符</span></div></div><div><div><div>5</div></div><div><span>evutil_socket_t</span><span> </span><span>event_get_fd</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 获取事件所属的event_base</span></div></div><div><div><div>8</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_get_base</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 获取事件类型（EV_READ, EV_WRITE等）</span></div></div><div><div><div>11</div></div><div><span>short</span><span> </span><span>event_get_events</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 获取回调函数</span></div></div><div><div><div>14</div></div><div><span>event_callback_fn </span><span>event_get_callback</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 获取回调参数</span></div></div><div><div><div>17</div></div><div><span>void</span><span> </span><span>*</span><span>event_get_callback_arg</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 获取libevent版本</span></div></div><div><div><div>20</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>event_get_version</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>21</div></div><div><span>ev_uint32_t</span><span> </span><span>event_get_version_number</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 检测事件是否被初始化</span></div></div><div><div><div>24</div></div><div><span>int</span><span> </span><span>event_initialized</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>/* 检测事件是否处于pending状态</span></div></div><div><div><div>27</div></div><div><span><span> </span></span><span>* @param ev      事件</span></div></div><div><div><div>28</div></div><div><span><span> </span></span><span>* @param events  请求的事件类型：EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL</span></div></div><div><div><div>29</div></div><div><span><span> </span></span><span>* @param tv      如果不为NULL，且事件有超时，设置超时时间</span></div></div><div><div><div>30</div></div><div><span><span> </span></span><span>* @return        如果事件pending返回true，否则返回0</span></div></div><div><div><div>31</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>32</div></div><div><span>int</span><span> </span><span>event_pending</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>/* 提取一个给定事件的所有参数 */</span></div></div><div><div><div>35</div></div><div><span>void</span><span> </span><span>event_get_assignment</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>event</span><span>,</span></span></div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>**</span><span><span>base_out</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span> </span><span>*</span><span><span>fd_out</span><span>,</span><span> </span></span><span>short</span><span> </span><span>*</span><span><span>events_out</span><span>,</span></span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>event_callback_fn </span><span>*</span><span><span>callback_out</span><span>,</span><span> </span></span><span>void</span><span> </span><span>**</span><span><span>arg_out</span><span>);</span></span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>/* 获取当前正在运行的事件，线程不安全 */</span></div></div><div><div><div>40</div></div><div><span>struct</span><span> event</span><span>*</span><span> </span><span>event_base_get_running_event</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span> </span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>五、手动激活事件<a href="#五手动激活事件"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* EV_READ、EV_WRITE、EV_TIMEOUT的组合 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>event_active</span><span>(</span><span>struct</span><span> event</span><span>*</span><span><span> </span><span>ev</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>what</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>ncalls</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>注意</strong>：尽量不要在事件回调本身里激活自身，否则会一直循环激活。</p></section><section><h2>六、定时器事件<a href="#六定时器事件"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* </span><span>@name</span><span> evtimer_* macros</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* Aliases for working with one-shot timer events</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* If you need EV_PERSIST timer use event_*() functions.</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>evtimer_assign</span><span><span>(</span><span>ev</span><span>, </span><span>b</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>\</span></div></div><div><div><div>7</div></div><div><span>    </span><span>event_assign</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>b</span><span>),</span><span> </span></span><span>-</span><span>1</span><span><span>,</span><span> </span></span><span>0</span><span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>evtimer_new</span><span><span>(</span><span>b</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>)        </span></span><span>event_new</span><span><span>(</span><span>(</span><span>b</span><span>),</span><span> </span></span><span>-</span><span>1</span><span><span>,</span><span> </span></span><span>0</span><span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div><div><div><div>9</div></div><div><span>#define</span><span> </span><span>evtimer_add</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>)            </span></span><span>event_add</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>evtimer_del</span><span><span>(</span><span>ev</span><span>)                </span></span><span>event_del</span><span>(ev)</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>evtimer_pending</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>)        </span></span><span>event_pending</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> EV_TIMEOUT</span><span>,</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>evtimer_initialized</span><span><span>(</span><span>ev</span><span>)        </span></span><span>event_initialized</span><span>(ev)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>七、信号事件<a href="#七信号事件"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* </span><span>@name</span><span> evsignal_* macros</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* Aliases for working with signal events</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>evsignal_add</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>)           </span></span><span>event_add</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>evsignal_assign</span><span><span>(</span><span>ev</span><span>, </span><span>b</span><span>, </span><span>x</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>\</span></div></div><div><div><div>7</div></div><div><span>    </span><span>event_assign</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>b</span><span>),</span><span> </span><span>(</span><span>x</span><span>),</span><span> EV_SIGNAL</span></span><span>|</span><span><span>EV_PERSIST</span><span>,</span><span> cb</span><span>,</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>evsignal_new</span><span><span>(</span><span>b</span><span>, </span><span>x</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>\</span></div></div><div><div><div>9</div></div><div><span>    </span><span>event_new</span><span><span>(</span><span>(</span><span>b</span><span>),</span><span> </span><span>(</span><span>x</span><span>),</span><span> EV_SIGNAL</span></span><span>|</span><span><span>EV_PERSIST</span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>evsignal_del</span><span><span>(</span><span>ev</span><span>)               </span></span><span>event_del</span><span>(ev)</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>evsignal_pending</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>)       </span></span><span>event_pending</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> EV_SIGNAL</span><span>,</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>evsignal_initialized</span><span><span>(</span><span>ev</span><span>)       </span></span><span>event_initialized</span><span>(ev)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>使用信号时的注意事项<a href="#使用信号时的注意事项"><span>#</span></a></h3><p>当前版本的信号大多数运行在后台，每个进程只能有一个event_base可以同时监控信号，如果同时添加两个event到event_base，即使信号是不同的，也只会有一个event_base会接收到信号。</p></section></section><section><h2>八、事件优先级<a href="#八事件优先级"><span>#</span></a></h2><p>当多个event同时触发，libevent并没有定义回调函数执行要遵循的任何顺序。可以用优先级来决定当多个事件同时发生时的回调函数执行顺序。</p><p>默认情况下，Libevent为所有事件分配中间优先级，除非它们的优先级被明确设置。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_priority_set</span><span>(</span><span>struct</span><span> event</span><span>*</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>priority</span><span>);</span></span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>event_get_priority</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/** Largest number of priorities that Libevent can support. */</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>EVENT_MAX_PRIORITIES</span><span> </span><span>256</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 设置优先级的最大数量 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>event_base_priority_init</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>,</span><span> </span></span><span>int</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 获取优先级的最大数量 */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>event_base_get_npriorities</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>eb</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>九、事件的生命周期<a href="#九事件的生命周期"><span>#</span></a></h2><p>事件的生命周期包括创建、添加、触发、移除和销毁等阶段：</p><ol>
<li><strong>创建</strong>：通过 <code>event_new</code> 或 <code>event_assign</code> 创建事件</li>
<li><strong>添加</strong>：通过 <code>event_add</code> 将事件添加到事件基中</li>
<li><strong>触发</strong>：事件条件满足时，<code>libevent</code> 自动调用回调函数，也可以通过 <code>event_active</code> 手动触发</li>
<li><strong>移除</strong>：通过 <code>event_del</code> 将事件从事件基中移除</li>
<li><strong>销毁</strong>：通过 <code>event_free</code> 销毁事件</li>
</ol></section><section><h2>十、使用示例<a href="#十使用示例"><span>#</span></a></h2><section><h3>10.1 基本事件使用<a href="#101-基本事件使用"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>void</span><span> </span><span>callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event triggered, events=</span><span>%d</span><span>\n</span><span>"</span><span>, events);</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 创建事件</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>ev </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_TIMEOUT, callback, </span><span>NULL</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>// 添加事件，1秒后触发</span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_add</span><span>(ev, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 开始事件循环</span></div></div><div><div><div>19</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 清理</span></div></div><div><div><div>22</div></div><div><span>    </span><span>event_free</span><span>(ev);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>10.2 持久事件<a href="#102-持久事件"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>void</span><span> </span><span>callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Persistent event triggered</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 创建持久事件</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>ev </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_TIMEOUT </span><span>|</span><span> EV_PERSIST, callback, </span><span>NULL</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span><span>  // 每1秒触发一次</span></div></div><div><div><div>15</div></div><div><span>    </span><span>event_add</span><span>(ev, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>event_free</span><span>(ev);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-event-base/</id>
      <title type="text">🎯 event_base详解 - libevent源码深度解析系列（五）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-event-base/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🏗️ event_base详解<a href="#️-event_base详解"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>最重要的 <code>event_base</code>，就是一个 Reactor框架，我们都是需要先申请 <code>event_base</code> 结构体。对于一个 <code>event_base</code> 结构来说，它会保存一系列的 event 事件并且以轮训的方式去关注每一个事件，去查看哪一个事件是就绪的，事件就绪之后，就会调用事件回调。</p><p>我们可以取消(disable)指定的method通过环境变量，比如设置 <code>EVENT_NOQUEUE</code> 环境变量。也可以通过调用 <code>event_config_avoid_method</code> 函数来关闭相应的method。</p></section><section><h2>二、事件类型定义<a href="#二事件类型定义"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>EV_TIMEOUT</span><span>  </span><span><span>0x</span><span>01</span></span><span>    // 定时事件</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>EV_READ</span><span>     </span><span><span>0x</span><span>02</span></span><span>    // 可读事件</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>EV_WRITE</span><span>    </span><span><span>0x</span><span>04</span></span><span>    // 可写事件</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>EV_SIGNAL</span><span>   </span><span><span>0x</span><span>08</span></span><span>    // 信号事件</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>EV_PERSIST</span><span>  </span><span><span>0x</span><span>10</span></span><span>    // 永久事件，EV_PERSIST的作用是：事件被触发后，自动重新对这个event调用event_add函数</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>EV_ET</span><span>       </span><span><span>0x</span><span>20</span></span><span>    // 边缘触发事件，需要I/O复用系统调用支持，比如epoll</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>三、event_base结构体<a href="#三event_base结构体"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>/* eventop 实际上就是对系统提供的I/O复用接口进行封装（select、poll、epoll、kqueue...）</span></div></div><div><div><div>3</div></div><div><span><span>       </span></span><span>这个赋值是在创建event_base的时候 event_base_new -&gt; event_base_new_with_config，</span></div></div><div><div><div>4</div></div><div><span><span>       </span></span><span>在这个函数中从 eventops 结构体数组中选出合适的I/O复用机制 */</span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> eventop </span><span>*</span><span>evsel;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>/* 上面选出合适的I/O复用机制之后，通过base-&gt;evsel-&gt;init生成 evbase</span></div></div><div><div><div>8</div></div><div><span><span>       </span></span><span>所以可以说evsel 和 evbase 的关系就是c++中类和类的实例的关系 */</span></div></div><div><div><div>9</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>evbase;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>/* 使用changelist后，对事件的修改不会立即作用到epoll上，而是把这些修改保存到一个list上，</span></div></div><div><div><div>12</div></div><div><span><span>       </span></span><span>在作用到epoll之前，这些修改可以抵消，合并 */</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event_changelist changelist;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> eventop </span><span>*</span><span>evsigsel;</span></div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> evsig_info sig;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>int</span><span> virtual_event_count;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>int</span><span> virtual_event_count_max;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>int</span><span> event_count;</span><span>              // 事件总数</span></div></div><div><div><div>21</div></div><div><span>    </span><span>int</span><span> event_count_max;</span></div></div><div><div><div>22</div></div><div><span>    </span><span>int</span><span> event_count_active;</span><span>       // 活跃事件数</span></div></div><div><div><div>23</div></div><div><span>    </span><span>int</span><span> event_count_active_max;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>int</span><span> event_gotterm;</span><span>            // 设置在处理完事件后是否应该终止循环</span></div></div><div><div><div>26</div></div><div><span>    </span><span>int</span><span> event_break;</span><span>              // 设置是否应该立即终止循环</span></div></div><div><div><div>27</div></div><div><span>    </span><span>int</span><span> event_continue;</span><span>           // 设置是否应该立即启动一个新的循环实例</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>int</span><span> event_running_priority;</span></div></div><div><div><div>30</div></div><div><span>    </span><span>int</span><span> running_loop;</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>/* 触发的事件列表，后面会遍历这个列表，执行其中的回调 */</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> evcallback_list </span><span>*</span><span>activequeues;</span></div></div><div><div><div>34</div></div><div><span>    </span><span>int</span><span> nactivequeues;</span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> evcallback_list active_later_queue;</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> common_timeout_list </span><span>**</span><span>common_timeout_queues;</span></div></div><div><div><div>38</div></div><div><span>    </span><span>int</span><span> n_common_timeouts;</span></div></div><div><div><div>39</div></div><div><span>    </span><span>int</span><span> n_common_timeouts_allocated;</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>struct</span><span> event_io_map io;</span><span>       // Mapping from file descriptors to events</span></div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> event_signal_map sigmap;</span><span> // Mapping from signal numbers to events</span></div></div><div><div><div>43</div></div><div><span>    </span><span>struct</span><span> min_heap timeheap;</span><span>     // Priority queue of events with timeouts</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>struct</span><span> timeval tv_cache;</span></div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> evutil_monotonic_timer monotonic_timer;</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>#ifndef</span><span> </span><span>EVENT__DISABLE_THREAD_SUPPORT</span></div></div><div><div><div>49</div></div><div><span>    </span><span>unsigned</span><span> </span><span>long</span><span> th_owner_id;</span></div></div><div><div><div>50</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>th_base_lock;</span></div></div><div><div><div>51</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>current_event_cond;</span></div></div><div><div><div>52</div></div><div><span>    </span><span>int</span><span> current_event_waiters;</span></div></div><div><div><div>53</div></div><div><span>#endif</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>struct</span><span> event_callback </span><span>*</span><span>current_event;</span></div></div><div><div><div>56</div></div><div><span>    </span><span>enum</span><span> event_base_config_flag flags;</span></div></div><div><div><div>57</div></div><div><span>    </span><span>// ... 其他字段</span></div></div><div><div><div>58</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>四、eventop结构体<a href="#四eventop结构体"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> eventop {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>name;</span><span>            // 后端名称</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>/* 创建一个新结构，包含运行后端所需的任何信息 */</span></div></div><div><div><div>4</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>(</span><span>*</span><span>init)(</span><span>struct</span><span> event_base </span><span>*</span><span>);</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>/* 在给定的 fd 或信号上启用读/写 */</span></div></div><div><div><div>6</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>add)(</span><span>struct</span><span> event_base </span><span>*</span><span>, </span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> old, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>fdinfo);</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>/* 禁用事件 */</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>del)(</span><span>struct</span><span> event_base </span><span>*</span><span>, </span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> old, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>fdinfo);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>/* 事件派遣分发 */</span></div></div><div><div><div>10</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>dispatch)(</span><span>struct</span><span> event_base </span><span>*</span><span>, </span><span>struct</span><span> timeval </span><span>*</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>/* 释放资源 */</span></div></div><div><div><div>12</div></div><div><span>    </span><span>void</span><span> (</span><span>*</span><span>dealloc)(</span><span>struct</span><span> event_base </span><span>*</span><span>);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>int</span><span> need_reinit;</span><span>             // 是否需要重新初始化</span></div></div><div><div><div>14</div></div><div><span>    </span><span>enum</span><span> event_method_feature features;</span><span>  // 支持的特性</span></div></div><div><div><div>15</div></div><div><span>    </span><span>size_t</span><span> fdinfo_len;</span></div></div><div><div><div>16</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h3>4.1 epoll后端示例<a href="#41-epoll后端示例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>struct</span><span> eventop epollops </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"epoll"</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>epoll_init,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>epoll_nochangelist_add,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>epoll_nochangelist_del,</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>epoll_dispatch,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>epoll_dealloc,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>1</span><span>,</span><span> /* need reinit */</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>EV_FEATURE_ET</span><span>|</span><span>EV_FEATURE_O1</span><span>|</span><span>EV_FEATURE_EARLY_CLOSE,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>0</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>五、event_base API<a href="#五event_base-api"><span>#</span></a></h2><section><h3>5.1 创建event_base<a href="#51-创建event_base"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 简单创建</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_base_new</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 带配置创建</span></div></div><div><div><div>5</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_base_new_with_config</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 旧版本兼容</span></div></div><div><div><div>8</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_init</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 释放</span></div></div><div><div><div>11</div></div><div><span>void</span><span> </span><span>event_base_free</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.1.1 event_base_new 源码解析<a href="#511-event_base_new-源码解析"><span>#</span></a></h3><p><code>event_base_new</code> 函数的核心实现流程：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_base_new</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>5</div></div><div><span>    </span><span>return</span><span> base;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>event_base_new_with_config</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 1. 分配event_base结构</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>base </span><span>=</span><span> </span><span>calloc</span><span>(</span><span>1</span><span>, </span><span>sizeof</span><span>(</span><span>struct</span><span> event_base));</span></div></div><div><div><div>13</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 2. 初始化基本字段</span></div></div><div><div><div>16</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>evsel</span><span> </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>evbase</span><span> </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// 3. 选择合适的I/O多路复用后端</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>event_base_choose_method</span><span>(base, cfg)) {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>22</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>// 4. 初始化其他组件</span></div></div><div><div><div>26</div></div><div><span>    </span><span>if</span><span> (</span><span>event_base_init_common_timeout_lists</span><span>(base)) {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>28</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>// 5. 初始化信号处理</span></div></div><div><div><div>32</div></div><div><span>    </span><span>if</span><span> (</span><span>evsig_init</span><span>(base)) {</span></div></div><div><div><div>33</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>34</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>return</span><span> base;</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.1.2 后端选择机制<a href="#512-后端选择机制"><span>#</span></a></h3><p><code>event_base_choose_method</code> 函数会从 <code>eventops</code> 数组中选择最合适的后端：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>event_base_choose_method</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> eventop </span><span>*</span><span>best_method </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>int</span><span> best_priority </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>// 遍历所有可用的后端</span></div></div><div><div><div>6</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> </span><span>struct</span><span> eventop </span><span>**</span><span>method </span><span>=</span><span> eventops; </span><span>*</span><span>method; method</span><span>++</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>// 检查是否满足配置要求</span></div></div><div><div><div>8</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span>event_config_is_method_disabled</span><span>(cfg, (</span><span>*</span><span>method)-&gt;</span><span>name</span><span>)) {</span></div></div><div><div><div>9</div></div><div><span>            </span><span>// 选择优先级最高的方法</span></div></div><div><div><div>10</div></div><div><span>            </span><span>if</span><span> (</span><span>!</span><span>best_method </span><span>||</span><span> (</span><span>*</span><span>method)-&gt;</span><span>features</span><span> </span><span>&gt;</span><span> best_priority) {</span></div></div><div><div><div>11</div></div><div><span><span>                </span></span><span>best_method </span><span>=</span><span> </span><span>*</span><span>method;</span></div></div><div><div><div>12</div></div><div><span><span>                </span></span><span>best_priority </span><span>=</span><span> (</span><span>*</span><span>method)-&gt;</span><span>features</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>best_method) </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// 初始化选中的后端</span></div></div><div><div><div>20</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>evsel</span><span> </span><span>=</span><span> best_method;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>evbase</span><span> </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>evsel</span><span>-&gt;</span><span>init</span><span>(base);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.1.3 后端优先级<a href="#513-后端优先级"><span>#</span></a></h3>

<table><thead><tr><th>后端</th><th>优先级</th><th>特性</th></tr></thead><tbody><tr><td>epoll</td><td>最高</td><td>EV_FEATURE_ETEV_FEATURE_O1EV_FEATURE_EARLY_CLOSE</td></tr><tr><td>kqueue</td><td>高</td><td>EV_FEATURE_ETEV_FEATURE_O1EV_FEATURE_EARLY_CLOSE</td></tr><tr><td>poll</td><td>中</td><td>基本功能</td></tr><tr><td>select</td><td>低</td><td>基本功能</td></tr></tbody></table></section><section><h3>5.1.4 创建流程图示<a href="#514-创建流程图示"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                event_base_new()                   │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│ 1. 创建event_config                               │</span></div></div><div><div><div>5</div></div><div><span>│ 2. 调用event_base_new_with_config()               │</span></div></div><div><div><div>6</div></div><div><span>│    ├─ 分配event_base结构                          │</span></div></div><div><div><div>7</div></div><div><span>│    ├─ 选择I/O多路复用后端                         │</span></div></div><div><div><div>8</div></div><div><span>│    │  ├─ 遍历eventops数组                        │</span></div></div><div><div><div>9</div></div><div><span>│    │  ├─ 检查配置要求                            │</span></div></div><div><div><div>10</div></div><div><span>│    │  └─ 选择优先级最高的方法                      │</span></div></div><div><div><div>11</div></div><div><span>│    ├─ 初始化后端                                  │</span></div></div><div><div><div>12</div></div><div><span>│    ├─ 初始化通用超时列表                          │</span></div></div><div><div><div>13</div></div><div><span>│    └─ 初始化信号处理                              │</span></div></div><div><div><div>14</div></div><div><span>│ 3. 释放event_config                              │</span></div></div><div><div><div>15</div></div><div><span>│ 4. 返回event_base指针                             │</span></div></div><div><div><div>16</div></div><div><span>└─────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><code>event_base_new</code> 从全局变量 <code>eventops</code> 中选出操作系统支持的最快方法：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>const</span><span> </span><span>struct</span><span> eventop </span><span>*</span><span>eventops</span><span>[]</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>#ifdef</span><span> </span><span>EVENT__HAVE_EVENT_PORTS</span></div></div><div><div><div>3</div></div><div><span>    </span><span>&amp;</span><span>evportops,</span></div></div><div><div><div>4</div></div><div><span>#endif</span></div></div><div><div><div>5</div></div><div><span>#ifdef</span><span> </span><span>EVENT__HAVE_WORKING_KQUEUE</span></div></div><div><div><div>6</div></div><div><span>    </span><span>&amp;</span><span>kqops,</span></div></div><div><div><div>7</div></div><div><span>#endif</span></div></div><div><div><div>8</div></div><div><span>#ifdef</span><span> </span><span>EVENT__HAVE_EPOLL</span></div></div><div><div><div>9</div></div><div><span>    </span><span>&amp;</span><span>epollops,</span></div></div><div><div><div>10</div></div><div><span>#endif</span></div></div><div><div><div>11</div></div><div><span>#ifdef</span><span> </span><span>EVENT__HAVE_DEVPOLL</span></div></div><div><div><div>12</div></div><div><span>    </span><span>&amp;</span><span>devpollops,</span></div></div><div><div><div>13</div></div><div><span>#endif</span></div></div><div><div><div>14</div></div><div><span>#ifdef</span><span> </span><span>EVENT__HAVE_POLL</span></div></div><div><div><div>15</div></div><div><span>    </span><span>&amp;</span><span>pollops,</span></div></div><div><div><div>16</div></div><div><span>#endif</span></div></div><div><div><div>17</div></div><div><span>#ifdef</span><span> </span><span>EVENT__HAVE_SELECT</span></div></div><div><div><div>18</div></div><div><span>    </span><span>&amp;</span><span>selectops,</span></div></div><div><div><div>19</div></div><div><span>#endif</span></div></div><div><div><div>20</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>21</div></div><div><span>    </span><span>&amp;</span><span>win32ops,</span></div></div><div><div><div>22</div></div><div><span>#endif</span></div></div><div><div><div>23</div></div><div><span>    </span><span>NULL</span></div></div><div><div><div>24</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 运行事件循环<a href="#52-运行事件循环"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 阻塞等待事件的到来，如果列表中没有事件，或者事件中调用</span></div></div><div><div><div>2</div></div><div><span><span>   </span></span><span>event_base_loopbreak() 或者 event_base_loopexit()，此函数将会退出阻塞 */</span></div></div><div><div><div>3</div></div><div><span>int</span><span> </span><span>event_base_dispatch</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 更细粒度控制的事件循环 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>event_base_loop</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h4>5.2.1 event_base_loop 源码解析<a href="#521-event_base_loop-源码解析"><span>#</span></a></h4><p><code>event_base_loop</code> 函数是事件循环的核心实现：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_loop</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>int</span><span> res, done;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> timeval tv;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> timeval </span><span>*</span><span>tv_p;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>// 检查是否已经在运行</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>running_loop</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>running_loop</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>done </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>while</span><span> (</span><span>!</span><span>done) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>// 1. 计算超时时间</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>tv_p </span><span>=</span><span> </span><span>&amp;</span><span>tv;</span></div></div><div><div><div>17</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span>N_ACTIVE_CALLBACKS</span><span>(base) </span><span>&amp;&amp;</span><span> </span><span>!</span><span>(flags </span><span>&amp;</span><span> EVLOOP_NONBLOCK)) {</span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>timeout </span><span>=</span><span> </span><span>event_base_get_next_timeout</span><span>(base, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>20</div></div><div><span>            </span><span>// 如果有活跃回调或非阻塞模式，不等待</span></div></div><div><div><div>21</div></div><div><span>            </span><span>evutil_timerclear</span><span>(</span><span>&amp;</span><span>tv);</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>        </span><span>// 2. 处理待处理的事件变更</span></div></div><div><div><div>25</div></div><div><span>        </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>changelist</span><span>) {</span></div></div><div><div><div>26</div></div><div><span>            </span><span>event_process_changelist</span><span>(base);</span></div></div><div><div><div>27</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>        </span><span>// 3. 调用后端的dispatch方法</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>res </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>evsel</span><span>-&gt;</span><span>dispatch</span><span>(base, tv_p);</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>        </span><span>// 4. 处理定时器事件</span></div></div><div><div><div>33</div></div><div><span>        </span><span>event_process_active</span><span>(base);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>        </span><span>// 5. 检查是否需要退出</span></div></div><div><div><div>36</div></div><div><span>        </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>event_gotterm</span><span>) {</span></div></div><div><div><div>37</div></div><div><span><span>            </span></span><span>done </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>event_break</span><span>) {</span></div></div><div><div><div>39</div></div><div><span><span>            </span></span><span>done </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> ((flags </span><span>&amp;</span><span> EVLOOP_ONCE) </span><span>&amp;&amp;</span><span> </span><span>!</span><span>N_ACTIVE_CALLBACKS</span><span>(base)) {</span></div></div><div><div><div>41</div></div><div><span><span>            </span></span><span>done </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> ((flags </span><span>&amp;</span><span> EVLOOP_NONBLOCK) </span><span>&amp;&amp;</span><span> </span><span>!</span><span>N_ACTIVE_CALLBACKS</span><span>(base)) {</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>done </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>running_loop</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>48</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>5.2.2 事件循环流程<a href="#522-事件循环流程"><span>#</span></a></h4><ol>
<li><strong>计算超时时间</strong>：根据定时器事件计算下次超时时间</li>
<li><strong>处理事件变更</strong>：应用之前积累的事件添加/删除操作</li>
<li><strong>等待事件</strong>：调用后端的dispatch方法等待事件触发</li>
<li><strong>处理活跃事件</strong>：执行触发的事件回调</li>
<li><strong>检查退出条件</strong>：根据标志和状态决定是否退出循环</li>
</ol></section><section><h4>5.2.3 事件循环图示<a href="#523-事件循环图示"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│             event_base_loop()                     │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│ 1. 检查是否已在运行                               │</span></div></div><div><div><div>5</div></div><div><span>│ 2. 进入主循环                                     │</span></div></div><div><div><div>6</div></div><div><span>│    ├─ 计算下次超时时间                             │</span></div></div><div><div><div>7</div></div><div><span>│    ├─ 处理待处理的事件变更                          │</span></div></div><div><div><div>8</div></div><div><span>│    ├─ 调用后端dispatch等待事件                      │</span></div></div><div><div><div>9</div></div><div><span>│    ├─ 处理定时器事件                                │</span></div></div><div><div><div>10</div></div><div><span>│    ├─ 处理活跃事件                                  │</span></div></div><div><div><div>11</div></div><div><span>│    └─ 检查退出条件                                  │</span></div></div><div><div><div>12</div></div><div><span>│ 3. 退出循环                                        │</span></div></div><div><div><div>13</div></div><div><span>└─────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>循环标志<a href="#循环标志"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>EVLOOP_ONCE</span><span>            </span><span><span>0x</span><span>01</span></span><span>  // 触发一次后退出</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>EVLOOP_NONBLOCK</span><span>        </span><span><span>0x</span><span>02</span></span><span>  // 非阻塞模式</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>EVLOOP_NO_EXIT_ON_EMPTY</span><span> </span><span><span>0x</span><span>04</span></span><span> // 无事件时不退出</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>5.3 停止事件循环<a href="#53-停止事件循环"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 让 event_base 在给定时间之后停止循环</span></div></div><div><div><div>2</div></div><div><span><span>   </span></span><span>如果tv参数NULL，event_base会立即停止循环 */</span></div></div><div><div><div>3</div></div><div><span>int</span><span> </span><span>event_base_loopexit</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* event_base 立即退出循环，不会等待正在执行的事件完成 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>event_base_loopbreak</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 检查退出原因 */</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>event_base_got_exit</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span><span>  // 因为loopexit退出</span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>event_base_got_break</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span><span> // 因为loopbreak退出</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 如果事件处理完后loop会退出，在回调中执行此函数则loop不会退出 */</span></div></div><div><div><div>13</div></div><div><span>int</span><span> </span><span>event_base_loopcontinue</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.4 获取event_base信息<a href="#54-获取event_base信息"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取Libevent使用的内核I/O复用机制 */</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>event_base_get_method</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取支持的方法列表 */</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>**</span><span>event_get_supported_methods</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 获取事件数量 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>event_base_get_num_events</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>unsigned</span><span> </span><span>int</span><span><span> </span><span>type</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>// type: EVENT_BASE_COUNT_ACTIVE, EVENT_BASE_COUNT_VIRTUAL, EVENT_BASE_COUNT_ADDED</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 获取最大事件数量 */</span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>event_base_get_max_events</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>unsigned</span><span> </span><span>int</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>clear</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、event_config配置<a href="#六event_config配置"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_config </span><span>*</span><span>event_config_new</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>event_config_free</span><span>(</span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 设置不使用指定的I/O复用机制 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>event_config_avoid_method</span><span>(</span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>method</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>6.1 特性设置<a href="#61-特性设置"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> event_method_feature {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>EV_FEATURE_ET </span><span>=</span><span> </span><span><span>0x</span><span>01</span></span><span>,</span><span>          // 要求支持边沿触发</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>EV_FEATURE_O1 </span><span>=</span><span> </span><span><span>0x</span><span>02</span></span><span>,</span><span>          // 要求O(1)复杂度</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>EV_FEATURE_FDS </span><span>=</span><span> </span><span><span>0x</span><span>04</span></span><span>,</span><span>         // 要求支持任意文件描述符</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>EV_FEATURE_EARLY_CLOSE </span><span>=</span><span> </span><span><span>0x</span><span>08</span></span><span>  // 要求支持EV_CLOSED</span></div></div><div><div><div>6</div></div><div><span>};</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>event_config_require_features</span><span>(</span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>feature</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>event_base_get_features</span><span>(</span><span>const</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 标志设置<a href="#62-标志设置"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> event_base_config_flag {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>EVENT_BASE_FLAG_NOLOCK </span><span>=</span><span> </span><span><span>0x</span><span>01</span></span><span>,</span><span>              // 不分配锁</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>EVENT_BASE_FLAG_IGNORE_ENV </span><span>=</span><span> </span><span><span>0x</span><span>02</span></span><span>,</span><span>          // 忽略环境变量</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>EVENT_BASE_FLAG_STARTUP_IOCP </span><span>=</span><span> </span><span><span>0x</span><span>04</span></span><span>,</span><span>        // Windows IOCP</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>EVENT_BASE_FLAG_NO_CACHE_TIME </span><span>=</span><span> </span><span><span>0x</span><span>08</span></span><span>,</span><span>       // 不缓存时间</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST </span><span>=</span><span> </span><span><span>0x</span><span>10</span></span><span>,</span><span>// 使用epoll changelist</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>EVENT_BASE_FLAG_PRECISE_TIMER </span><span>=</span><span> </span><span><span>0x</span><span>20</span></span><span>        // 精确计时器</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>event_config_set_flag</span><span>(</span><span>struct</span><span> event_config </span><span>*</span><span><span>cfg</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flag</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>七、使用示例<a href="#七使用示例"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 简单创建</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 带配置创建</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_config_require_features</span><span>(cfg, EV_FEATURE_ET </span><span>|</span><span> EV_FEATURE_O1);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>event_config_avoid_method</span><span>(cfg, </span><span>"select"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base2 </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Using method: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>event_base_get_method</span><span>(base));</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>event_base_free</span><span>(base2);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-event-count/</id>
      <title type="text">📊 事件数量分析 - libevent源码深度解析系列（十七）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-event-count/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📊 事件数量分析<a href="#-事件数量分析"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent内部维护了事件数量的统计信息，这对于监控和调试非常有用。本文分析事件数量的含义和使用方法。</p></section><section><h2>二、事件数量类型<a href="#二事件数量类型"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 事件数量类型 */</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>EVENT_BASE_COUNT_ACTIVE</span><span>   </span><span><span>0x</span><span>01</span></span><span>  // 活跃事件数量</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>EVENT_BASE_COUNT_VIRTUAL</span><span>  </span><span><span>0x</span><span>02</span></span><span>  // 虚拟事件数量</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>EVENT_BASE_COUNT_ADDED</span><span>    </span><span><span>0x</span><span>04</span></span><span>  // 已添加事件数量</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>2.1 各类型说明<a href="#21-各类型说明"><span>#</span></a></h3>

<table><thead><tr><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><strong>ACTIVE</strong></td><td>当前处于活跃状态的事件数量，即已触发但尚未处理的事件</td></tr><tr><td><strong>VIRTUAL</strong></td><td>虚拟事件数量，用于内部机制（如定时器、信号等）</td></tr><tr><td><strong>ADDED</strong></td><td>已添加到event_base的事件总数</td></tr></tbody></table></section></section><section><h2>三、API<a href="#三api"><span>#</span></a></h2><section><h3>3.1 获取事件数量<a href="#31-获取事件数量"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* 获取事件数量</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>base</span><span>  event_base</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>type</span><span>  事件类型（EVENT_BASE_COUNT_*的组合）</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* </span><span>@return</span><span> 事件数量</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>event_base_get_num_events</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>unsigned</span><span> </span><span>int</span><span><span> </span><span>type</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/**</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>* 获取最大事件数量</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>base</span><span>  event_base</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>type</span><span>  事件类型</span></div></div><div><div><div>13</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>clear</span><span> 是否清除最大值</span></div></div><div><div><div>14</div></div><div><span><span> </span></span><span>* </span><span>@return</span><span> 最大事件数量</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>16</div></div><div><span>int</span><span> </span><span>event_base_get_max_events</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>unsigned</span><span> </span><span>int</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>clear</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、event_base结构体中的计数器<a href="#四event_base结构体中的计数器"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>3</div></div><div><span>    </span><span>int</span><span> virtual_event_count;</span><span>        // 虚拟事件计数</span></div></div><div><div><div>4</div></div><div><span>    </span><span>int</span><span> virtual_event_count_max;</span><span>    // 虚拟事件最大计数</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>int</span><span> event_count;</span><span>                // 事件总数</span></div></div><div><div><div>7</div></div><div><span>    </span><span>int</span><span> event_count_max;</span><span>            // 事件最大总数</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>int</span><span> event_count_active;</span><span>         // 活跃事件数</span></div></div><div><div><div>10</div></div><div><span>    </span><span>int</span><span> event_count_active_max;</span><span>     // 活跃事件最大数</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>12</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>五、事件计数更新时机<a href="#五事件计数更新时机"><span>#</span></a></h2><section><h3>5.1 事件添加时<a href="#51-事件添加时"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_add_nolock_</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>tv_is_absolute</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>ev</span><span>-&gt;</span><span>ev_base</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 增加事件计数</span></div></div><div><div><div>5</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>event_count</span><span>++</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>event_count</span><span> </span><span>&gt;</span><span> </span><span>base</span><span>-&gt;</span><span>event_count_max</span><span>)</span></div></div><div><div><div>7</div></div><div><span>        </span><span>base</span><span>-&gt;</span><span>event_count_max</span><span> </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>event_count</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 如果是定时器，增加虚拟事件计数</span></div></div><div><div><div>10</div></div><div><span>    </span><span>if</span><span> (tv </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>base</span><span>-&gt;</span><span>virtual_event_count</span><span>++</span><span>;</span></div></div><div><div><div>12</div></div><div><span>        </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>virtual_event_count</span><span> </span><span>&gt;</span><span> </span><span>base</span><span>-&gt;</span><span>virtual_event_count_max</span><span>)</span></div></div><div><div><div>13</div></div><div><span>            </span><span>base</span><span>-&gt;</span><span>virtual_event_count_max</span><span> </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>virtual_event_count</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 事件删除时<a href="#52-事件删除时"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_del_nolock_</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>blocking</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>ev</span><span>-&gt;</span><span>ev_base</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 减少事件计数</span></div></div><div><div><div>5</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>event_count</span><span>--</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>// 如果是定时器，减少虚拟事件计数</span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (</span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;</span><span> EVLIST_TIMEOUT) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>base</span><span>-&gt;</span><span>virtual_event_count</span><span>--</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 如果在活跃队列中，减少活跃事件计数</span></div></div><div><div><div>13</div></div><div><span>    </span><span>if</span><span> (</span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;</span><span> EVLIST_ACTIVE) {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>base</span><span>-&gt;</span><span>event_count_active</span><span>--</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.3 事件激活时<a href="#53-事件激活时"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>event_active_nolock_</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>res</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>ncalls</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>ev</span><span>-&gt;</span><span>ev_base</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 增加活跃事件计数</span></div></div><div><div><div>5</div></div><div><span>    </span><span>base</span><span>-&gt;</span><span>event_count_active</span><span>++</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>event_count_active</span><span> </span><span>&gt;</span><span> </span><span>base</span><span>-&gt;</span><span>event_count_active_max</span><span>)</span></div></div><div><div><div>7</div></div><div><span>        </span><span>base</span><span>-&gt;</span><span>event_count_active_max</span><span> </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>event_count_active</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、使用示例<a href="#六使用示例"><span>#</span></a></h2><section><h3>6.1 监控事件数量<a href="#61-监控事件数量"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>print_event_stats</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>int</span><span> active </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(base, EVENT_BASE_COUNT_ACTIVE);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> virtual </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(base, EVENT_BASE_COUNT_VIRTUAL);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>int</span><span> added </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(base, EVENT_BASE_COUNT_ADDED);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event Statistics:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Active events: </span><span>%d</span><span>\n</span><span>"</span><span>, active);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Virtual events: </span><span>%d</span><span>\n</span><span>"</span><span>, virtual);</span></div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Total added events: </span><span>%d</span><span>\n</span><span>"</span><span>, added);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>static</span><span> </span><span>int</span><span> count </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>count</span><span>++</span><span>;</span></div></div><div><div><div>22</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer </span><span>%d</span><span> fired</span><span>\n</span><span>"</span><span>, count);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>print_event_stats</span><span>(base);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>if</span><span> (count </span><span>&gt;=</span><span> </span><span>5</span><span>) {</span></div></div><div><div><div>26</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Initial state:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>34</div></div><div><span>    </span><span>print_event_stats</span><span>(base);</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>// 创建多个定时器</span></div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timers</span><span>[</span><span>3</span><span>];</span></div></div><div><div><div>38</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>3</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>39</div></div><div><span>        </span><span>timers</span><span>[i] </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer_cb, base);</span></div></div><div><div><div>40</div></div><div><span>        </span><span>struct</span><span> timeval tv </span><span>=</span><span> {i </span><span>+</span><span> </span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>41</div></div><div><span>        </span><span>event_add</span><span>(</span><span>timers</span><span>[i], </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>After adding timers:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>print_event_stats</span><span>(base);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Final state:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>50</div></div><div><span>    </span><span>print_event_stats</span><span>(base);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>3</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>53</div></div><div><span>        </span><span>event_free</span><span>(</span><span>timers</span><span>[i]);</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>55</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>58</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.2 性能监控<a href="#62-性能监控"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>struct</span><span> monitor_data {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> check_interval;</span></div></div><div><div><div>9</div></div><div><span>};</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>monitor_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> monitor_data </span><span>*</span><span>data </span><span>=</span><span> arg;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>int</span><span> active </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(</span><span>data</span><span>-&gt;</span><span>base</span><span>, EVENT_BASE_COUNT_ACTIVE);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>int</span><span> added </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(</span><span>data</span><span>-&gt;</span><span>base</span><span>, EVENT_BASE_COUNT_ADDED);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>int</span><span> max_active </span><span>=</span><span> </span><span>event_base_get_max_events</span><span>(</span><span>data</span><span>-&gt;</span><span>base</span><span>, EVENT_BASE_COUNT_ACTIVE, </span><span>0</span><span>);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>printf</span><span>(</span><span>"[Monitor] Active: </span><span>%d</span><span>, Added: </span><span>%d</span><span>, Max Active: </span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>19</div></div><div><span><span>           </span></span><span>active, added, max_active);</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>// 创建监控定时器</span></div></div><div><div><div>26</div></div><div><span>    </span><span>struct</span><span> monitor_data data </span><span>=</span><span> {base, </span><span>1</span><span>};</span></div></div><div><div><div>27</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>monitor </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, monitor_cb, </span><span>&amp;</span><span>data);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>29</div></div><div><span>    </span><span>event_add</span><span>(monitor, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>// 创建一些工作定时器</span></div></div><div><div><div>32</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>5</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>33</div></div><div><span>        </span><span>struct</span><span> event </span><span>*</span><span>ev </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST,</span></div></div><div><div><div>34</div></div><div><span>            </span><span>[]</span><span>(</span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>arg) {}, </span><span>NULL</span><span>);</span></div></div><div><div><div>35</div></div><div><span>        </span><span>struct</span><span> timeval t </span><span>=</span><span> {</span><span>0</span><span>, (i </span><span>+</span><span> </span><span>1</span><span>) </span><span>*</span><span> </span><span>200000</span><span>};</span></div></div><div><div><div>36</div></div><div><span>        </span><span>event_add</span><span>(ev, </span><span>&amp;</span><span>t);</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>// 运行10秒</span></div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> timeval exit_tv </span><span>=</span><span> {</span><span>10</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>41</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>&amp;</span><span>exit_tv);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>event_free</span><span>(monitor);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、虚拟事件详解<a href="#七虚拟事件详解"><span>#</span></a></h2><p>虚拟事件是libevent内部使用的事件，主要用于：</p>

<table><thead><tr><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><strong>定时器</strong></td><td>每个定时器都是一个虚拟事件</td></tr><tr><td><strong>信号</strong></td><td>信号处理使用虚拟事件</td></tr><tr><td><strong>内部通知</strong></td><td>线程间通信等内部机制</td></tr></tbody></table><section><h3>7.1 虚拟事件计数<a href="#71-虚拟事件计数"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 定时器添加时</span></div></div><div><div><div>2</div></div><div><span>event_count_added </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>3</div></div><div><span>virtual_event_count </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 定时器触发时</span></div></div><div><div><div>6</div></div><div><span>event_count_active </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 定时器回调执行后（非EV_PERSIST）</span></div></div><div><div><div>9</div></div><div><span>event_count_added </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>10</div></div><div><span>virtual_event_count </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>11</div></div><div><span>event_count_active </span><span>=</span><span> </span><span>0</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>八、最佳实践<a href="#八最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>建议</th><th>说明</th></tr></thead><tbody><tr><td><strong>定期监控</strong></td><td>在生产环境中定期检查事件数量</td></tr><tr><td><strong>设置阈值</strong></td><td>设置合理的最大事件数阈值</td></tr><tr><td><strong>资源预警</strong></td><td>事件数量异常时发出警告</td></tr><tr><td><strong>调试辅助</strong></td><td>使用事件数量帮助调试内存泄漏</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-event-flow/</id>
      <title type="text">🔄 事件触发流程 - libevent源码深度解析系列（十五）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-event-flow/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔄 事件触发流程<a href="#-事件触发流程"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>本文详细分析libevent事件触发的内部流程，从事件添加到回调执行的完整链路。</p></section><section><h2>二、事件触发流程图<a href="#二事件触发流程图"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                        事件触发流程                                   │</span></div></div><div><div><div>3</div></div><div><span>├─────────────────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│                                                                     │</span></div></div><div><div><div>5</div></div><div><span>│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐     │</span></div></div><div><div><div>6</div></div><div><span>│  │event_add │───&gt;│epoll_ctl │───&gt;│epoll_wait│───&gt;│回调执行   │     │</span></div></div><div><div><div>7</div></div><div><span>│  └──────────┘    └──────────┘    └──────────┘    └──────────┘     │</span></div></div><div><div><div>8</div></div><div><span>│       │              │               │                │            │</span></div></div><div><div><div>9</div></div><div><span>│       ▼              ▼               ▼                ▼            │</span></div></div><div><div><div>10</div></div><div><span>│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐     │</span></div></div><div><div><div>11</div></div><div><span>│  │加入事件   │    │注册到内核 │    │等待事件   │    │处理事件   │     │</span></div></div><div><div><div>12</div></div><div><span>│  │队列       │    │           │    │就绪       │    │           │     │</span></div></div><div><div><div>13</div></div><div><span>│  └──────────┘    └──────────┘    └──────────┘    └──────────┘     │</span></div></div><div><div><div>14</div></div><div><span>│                                                                     │</span></div></div><div><div><div>15</div></div><div><span>└─────────────────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>三、核心数据结构<a href="#三核心数据结构"><span>#</span></a></h2><section><h3>3.1 event_base结构体关键字段<a href="#31-event_base结构体关键字段"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> event_base {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> eventop </span><span>*</span><span>evsel;</span><span>    // I/O复用机制（epoll/select等）</span></div></div><div><div><div>3</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>evbase;</span><span>                   // I/O复用机制的数据</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> event_io_map io;</span><span>         // fd到event的映射</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_signal_map sigmap;</span><span> // 信号到event的映射</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> min_heap timeheap;</span><span>       // 定时器小根堆</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evcallback_list </span><span>*</span><span>activequeues;</span><span>  // 活跃事件队列</span></div></div><div><div><div>11</div></div><div><span>    </span><span>int</span><span> nactivequeues;</span><span>              // 活跃队列数量</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>int</span><span> event_count;</span><span>                // 事件总数</span></div></div><div><div><div>14</div></div><div><span>    </span><span>int</span><span> event_count_active;</span><span>         // 活跃事件数</span></div></div><div><div><div>15</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>16</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 eventop结构体<a href="#32-eventop结构体"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> eventop {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>name;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>(</span><span>*</span><span>init)(</span><span>struct</span><span> event_base </span><span>*</span><span>);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>add)(</span><span>struct</span><span> event_base </span><span>*</span><span>, </span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> old, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>);</span></div></div><div><div><div>5</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>del)(</span><span>struct</span><span> event_base </span><span>*</span><span>, </span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> old, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>);</span></div></div><div><div><div>6</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>dispatch)(</span><span>struct</span><span> event_base </span><span>*</span><span>, </span><span>struct</span><span> timeval </span><span>*</span><span>);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>void</span><span> (</span><span>*</span><span>dealloc)(</span><span>struct</span><span> event_base </span><span>*</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> need_reinit;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>enum</span><span> event_method_feature features;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>size_t</span><span> fdinfo_len;</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>四、事件添加流程<a href="#四事件添加流程"><span>#</span></a></h2><section><h3>4.1 event_add函数<a href="#41-event_add函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_add</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>ev</span><span>-&gt;</span><span>ev_base</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 1. 如果有超时，添加到定时器堆</span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span> (tv </span><span>!=</span><span> </span><span>NULL</span><span> </span><span>&amp;&amp;</span><span> </span><span>!</span><span>(</span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;</span><span> EVLIST_TIMEOUT)) {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>if</span><span> (</span><span>min_heap_reserve</span><span>(</span><span>&amp;</span><span>base</span><span>-&gt;</span><span>timeheap</span><span>, </span><span>1</span><span>) </span><span>==</span><span> </span><span>-</span><span>1</span><span>)</span></div></div><div><div><div>7</div></div><div><span>            </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>8</div></div><div><span>        </span><span>min_heap_push</span><span>(</span><span>&amp;</span><span>base</span><span>-&gt;</span><span>timeheap</span><span>, ev);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 2. 如果是I/O事件，添加到I/O映射</span></div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span> ((</span><span>ev</span><span>-&gt;</span><span>ev_events</span><span> </span><span>&amp;</span><span> (EV_READ </span><span>|</span><span> EV_WRITE)) </span><span>&amp;&amp;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>!</span><span>(</span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;</span><span> (EVLIST_INSERTED </span><span>|</span><span> EVLIST_ACTIVE))) {</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>        </span><span>// 调用底层I/O复用机制的add函数</span></div></div><div><div><div>16</div></div><div><span>        </span><span>if</span><span> (</span><span>base</span><span>-&gt;</span><span>evsel</span><span>-&gt;</span><span>add</span><span>(base, </span><span>ev</span><span>-&gt;</span><span>ev_fd</span><span>,</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>old, </span><span>ev</span><span>-&gt;</span><span>ev_events</span><span>, </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_io</span><span>.</span><span>ev_timeout</span><span>) </span><span>==</span><span> </span><span>-</span><span>1</span><span>)</span></div></div><div><div><div>18</div></div><div><span>            </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>        </span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>|=</span><span> EVLIST_INSERTED;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 epoll_add函数<a href="#42-epoll_add函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>epoll_add</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>2</div></div><div><span>                     </span><span>short</span><span><span> </span><span>old</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>p</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> epoll </span><span>*</span><span>epollop </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>evbase</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> epoll_event epev;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>epev, </span><span>0</span><span>, </span><span>sizeof</span><span>(epev));</span></div></div><div><div><div>7</div></div><div><span>    </span><span>epev</span><span>.</span><span>data</span><span>.</span><span>fd</span><span> </span><span>=</span><span> fd;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> EV_READ)</span></div></div><div><div><div>10</div></div><div><span>        </span><span>epev</span><span>.</span><span>events</span><span> </span><span>|=</span><span> EPOLLIN;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> EV_WRITE)</span></div></div><div><div><div>12</div></div><div><span>        </span><span>epev</span><span>.</span><span>events</span><span> </span><span>|=</span><span> EPOLLOUT;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>int</span><span> op </span><span>=</span><span> old </span><span>?</span><span> EPOLL_CTL_MOD </span><span>:</span><span> EPOLL_CTL_ADD;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span> (</span><span>epoll_ctl</span><span>(</span><span>epollop</span><span>-&gt;</span><span>epfd</span><span>, op, fd, </span><span>&amp;</span><span>epev) </span><span>==</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、事件循环流程<a href="#五事件循环流程"><span>#</span></a></h2><section><h3>5.1 event_base_dispatch函数<a href="#51-event_base_dispatch函数"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_dispatch</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>return</span><span> </span><span>event_base_loop</span><span>(base, </span><span>0</span><span>);</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.2 event_base_loop函数<a href="#52-event_base_loop函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>event_base_loop</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flags</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>while</span><span> (</span><span>!</span><span>done) {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>// 1. 计算超时时间</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>timeout </span><span>=</span><span> </span><span>get_timeout</span><span>(base);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>        </span><span>// 2. 调用I/O复用机制等待事件</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>res </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>evsel</span><span>-&gt;</span><span>dispatch</span><span>(base, timeout);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>        </span><span>// 3. 处理定时器事件</span></div></div><div><div><div>10</div></div><div><span>        </span><span>process_timers</span><span>(base);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>        </span><span>// 4. 处理活跃事件</span></div></div><div><div><div>13</div></div><div><span>        </span><span>process_active_events</span><span>(base);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>        </span><span>if</span><span> (flags </span><span>&amp;</span><span> EVLOOP_ONCE)</span></div></div><div><div><div>16</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.3 epoll_dispatch函数<a href="#53-epoll_dispatch函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>epoll_dispatch</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> epoll </span><span>*</span><span>epollop </span><span>=</span><span> </span><span>base</span><span>-&gt;</span><span>evbase</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> epoll_event </span><span>events</span><span>[</span><span>32</span><span>];</span></div></div><div><div><div>4</div></div><div><span>    </span><span>int</span><span> timeout </span><span>=</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>// 计算超时时间（毫秒）</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (tv </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>timeout </span><span>=</span><span> </span><span>tv</span><span>-&gt;</span><span>tv_sec</span><span> </span><span>*</span><span> </span><span>1000</span><span> </span><span>+</span><span> </span><span>tv</span><span>-&gt;</span><span>tv_usec</span><span> </span><span>/</span><span> </span><span>1000</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 调用epoll_wait</span></div></div><div><div><div>12</div></div><div><span>    </span><span>int</span><span> n </span><span>=</span><span> </span><span>epoll_wait</span><span>(</span><span>epollop</span><span>-&gt;</span><span>epfd</span><span>, events, </span><span>32</span><span>, timeout);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (n </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n; i</span><span>++</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>int</span><span> fd </span><span>=</span><span> </span><span>events</span><span>[i].</span><span>data</span><span>.</span><span>fd</span><span>;</span></div></div><div><div><div>17</div></div><div><span>            </span><span>short</span><span> what </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>            </span><span>if</span><span> (</span><span>events</span><span>[i].</span><span>events</span><span> </span><span>&amp;</span><span> EPOLLIN)</span></div></div><div><div><div>20</div></div><div><span><span>                </span></span><span>what </span><span>|=</span><span> EV_READ;</span></div></div><div><div><div>21</div></div><div><span>            </span><span>if</span><span> (</span><span>events</span><span>[i].</span><span>events</span><span> </span><span>&amp;</span><span> EPOLLOUT)</span></div></div><div><div><div>22</div></div><div><span><span>                </span></span><span>what </span><span>|=</span><span> EV_WRITE;</span></div></div><div><div><div>23</div></div><div><span>            </span><span>if</span><span> (</span><span>events</span><span>[i].</span><span>events</span><span> </span><span>&amp;</span><span> EPOLLERR)</span></div></div><div><div><div>24</div></div><div><span><span>                </span></span><span>what </span><span>|=</span><span> EV_READ </span><span>|</span><span> EV_WRITE;</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>            </span><span>// 将事件加入活跃队列</span></div></div><div><div><div>27</div></div><div><span>            </span><span>event_active</span><span>(</span><span>evmap_io_get_event</span><span>(base, fd), what, </span><span>1</span><span>);</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、事件激活流程<a href="#六事件激活流程"><span>#</span></a></h2><section><h3>6.1 event_active函数<a href="#61-event_active函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>event_active</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>what</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>ncalls</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>ev</span><span>-&gt;</span><span>ev_base</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 如果已经在活跃队列中，不重复添加</span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span> (</span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;</span><span> EVLIST_ACTIVE)</span></div></div><div><div><div>6</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 设置活跃事件类型</span></div></div><div><div><div>9</div></div><div><span>    </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_io</span><span>.</span><span>ev_res</span><span> </span><span>=</span><span> what;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 加入活跃队列</span></div></div><div><div><div>12</div></div><div><span>    </span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>|=</span><span> EVLIST_ACTIVE;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>TAILQ_INSERT_TAIL</span><span>(</span><span>&amp;</span><span>base</span><span>-&gt;</span><span>activequeues</span><span>[</span><span>ev</span><span>-&gt;</span><span>ev_pri</span><span>],</span></div></div><div><div><div>14</div></div><div><span>                      </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_io</span><span>.</span><span>ev_active_next</span><span>);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.2 process_active_events函数<a href="#62-process_active_events函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>process_active_events</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>ev;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 按优先级处理活跃事件</span></div></div><div><div><div>5</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>base</span><span>-&gt;</span><span>nactivequeues</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>while</span><span> ((ev </span><span>=</span><span> </span><span>TAILQ_FIRST</span><span>(</span><span>&amp;</span><span>base</span><span>-&gt;</span><span>activequeues</span><span>[i])) </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>            </span><span>// 从活跃队列移除</span></div></div><div><div><div>8</div></div><div><span>            </span><span>TAILQ_REMOVE</span><span>(</span><span>&amp;</span><span>base</span><span>-&gt;</span><span>activequeues</span><span>[i], ev, </span><span>ev_</span><span>.</span><span>ev_io</span><span>.</span><span>ev_active_next</span><span>);</span></div></div><div><div><div>9</div></div><div><span>            </span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;=</span><span> </span><span>~</span><span>EVLIST_ACTIVE;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>            </span><span>// 执行回调</span></div></div><div><div><div>12</div></div><div><span>            </span><span>if</span><span> (</span><span>ev</span><span>-&gt;</span><span>ev_events</span><span> </span><span>&amp;</span><span> EV_SIGNAL) {</span></div></div><div><div><div>13</div></div><div><span>                </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_signal</span><span>.</span><span>ev_ncalls</span><span> </span><span>=</span><span> </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_signal</span><span>.</span><span>ev_pncalls</span><span>;</span></div></div><div><div><div>14</div></div><div><span>                </span><span>while</span><span> (</span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_signal</span><span>.</span><span>ev_ncalls</span><span>--</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>                    </span><span>ev</span><span>-&gt;</span><span>ev_callback</span><span>(</span><span>ev</span><span>-&gt;</span><span>ev_fd</span><span>, </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_signal</span><span>.</span><span>ev_res</span><span>, </span><span>ev</span><span>-&gt;</span><span>ev_arg</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>18</div></div><div><span>                </span><span>ev</span><span>-&gt;</span><span>ev_callback</span><span>(</span><span>ev</span><span>-&gt;</span><span>ev_fd</span><span>, </span><span>ev</span><span>-&gt;</span><span>ev_</span><span>.</span><span>ev_io</span><span>.</span><span>ev_res</span><span>, </span><span>ev</span><span>-&gt;</span><span>ev_arg</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>            </span><span>// 如果是持久事件，重新添加</span></div></div><div><div><div>22</div></div><div><span>            </span><span>if</span><span> (</span><span>ev</span><span>-&gt;</span><span>ev_flags</span><span> </span><span>&amp;</span><span> EVLIST_INSERTED) {</span></div></div><div><div><div>23</div></div><div><span>                </span><span>// 事件仍在插入状态，如果是EV_PERSIST，保持</span></div></div><div><div><div>24</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、完整流程图<a href="#七完整流程图"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────────────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                          完整事件流程                                  │</span></div></div><div><div><div>3</div></div><div><span>├──────────────────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│                                                                      │</span></div></div><div><div><div>5</div></div><div><span>│  1. 初始化阶段                                                        │</span></div></div><div><div><div>6</div></div><div><span>│     event_base_new()                                                 │</span></div></div><div><div><div>7</div></div><div><span>│         │                                                            │</span></div></div><div><div><div>8</div></div><div><span>│         ├── 选择I/O复用机制（epoll/select/poll等）                     │</span></div></div><div><div><div>9</div></div><div><span>│         └── 初始化数据结构                                            │</span></div></div><div><div><div>10</div></div><div><span>│                                                                      │</span></div></div><div><div><div>11</div></div><div><span>│  2. 事件注册阶段                                                      │</span></div></div><div><div><div>12</div></div><div><span>│     event_new() → event_add()                                        │</span></div></div><div><div><div>13</div></div><div><span>│         │                                                            │</span></div></div><div><div><div>14</div></div><div><span>│         ├── I/O事件 → evmap_io_add → epoll_ctl(EPOLL_CTL_ADD)        │</span></div></div><div><div><div>15</div></div><div><span>│         ├── 定时器 → min_heap_push                                   │</span></div></div><div><div><div>16</div></div><div><span>│         └── 信号 → evmap_signal_add                                  │</span></div></div><div><div><div>17</div></div><div><span>│                                                                      │</span></div></div><div><div><div>18</div></div><div><span>│  3. 事件循环阶段                                                      │</span></div></div><div><div><div>19</div></div><div><span>│     event_base_dispatch()                                            │</span></div></div><div><div><div>20</div></div><div><span>│         │                                                            │</span></div></div><div><div><div>21</div></div><div><span>│         ┌─────────────────────────────────────────┐                  │</span></div></div><div><div><div>22</div></div><div><span>│         │  while (!done) {                         │                  │</span></div></div><div><div><div>23</div></div><div><span>│         │      1. 计算超时时间                      │                  │</span></div></div><div><div><div>24</div></div><div><span>│         │      2. epoll_wait() 等待事件            │                  │</span></div></div><div><div><div>25</div></div><div><span>│         │      3. 处理定时器超时                   │                  │</span></div></div><div><div><div>26</div></div><div><span>│         │      4. 处理活跃事件                     │                  │</span></div></div><div><div><div>27</div></div><div><span>│         │  }                                      │                  │</span></div></div><div><div><div>28</div></div><div><span>│         └─────────────────────────────────────────┘                  │</span></div></div><div><div><div>29</div></div><div><span>│                                                                      │</span></div></div><div><div><div>30</div></div><div><span>│  4. 事件处理阶段                                                      │</span></div></div><div><div><div>31</div></div><div><span>│     process_active_events()                                          │</span></div></div><div><div><div>32</div></div><div><span>│         │                                                            │</span></div></div><div><div><div>33</div></div><div><span>│         ├── 按优先级遍历活跃队列                                       │</span></div></div><div><div><div>34</div></div><div><span>│         ├── 取出事件                                                  │</span></div></div><div><div><div>35</div></div><div><span>│         └── 执行回调函数                                              │</span></div></div><div><div><div>36</div></div><div><span>│                                                                      │</span></div></div><div><div><div>37</div></div><div><span>│  5. 清理阶段                                                          │</span></div></div><div><div><div>38</div></div><div><span>│     event_free() → event_base_free()                                 │</span></div></div><div><div><div>39</div></div><div><span>│                                                                      │</span></div></div><div><div><div>40</div></div><div><span>└──────────────────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>八、性能优化点<a href="#八性能优化点"><span>#</span></a></h2>

<table><thead><tr><th>优化点</th><th>说明</th></tr></thead><tbody><tr><td><strong>epoll ET模式</strong></td><td>使用边缘触发减少系统调用次数</td></tr><tr><td><strong>changelist</strong></td><td>批量处理事件修改，减少epoll_ctl调用</td></tr><tr><td><strong>优先级队列</strong></td><td>高优先级事件优先处理</td></tr><tr><td><strong>时间缓存</strong></td><td>避免频繁调用gettimeofday</td></tr><tr><td><strong>最小堆</strong></td><td>高效管理定时器</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-evutil/</id>
      <title type="text">🛠️ evutil工具函数 - libevent源码深度解析系列（二十）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-evutil/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔧 evutil工具函数<a href="#-evutil工具函数"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p><code>evutil</code> 是libevent提供的跨平台工具函数库，封装了各种操作系统差异，提供统一的API。这些函数包括socket操作、地址处理、字符串处理、时间操作、随机数生成等。</p></section><section><h2>二、Socket操作<a href="#二socket操作"><span>#</span></a></h2><section><h3>2.1 Socket创建与配置<a href="#21-socket创建与配置"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建socket */</span></div></div><div><div><div>2</div></div><div><span>evutil_socket_t</span><span> </span><span>evutil_socket_</span><span>(</span><span>int</span><span><span> </span><span>domain</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>protocol</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 创建socket pair */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evutil_socketpair</span><span>(</span><span>int</span><span><span> </span><span>domain</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>protocol</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>                      </span><span>evutil_socket_t</span><span> </span><span>pair</span><span>[</span><span>2</span><span><span>]</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 设置非阻塞 */</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sock</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 设置关闭时执行 */</span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>evutil_make_socket_closeonexec</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sock</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 关闭socket（跨平台） */</span></div></div><div><div><div>15</div></div><div><span>int</span><span> </span><span>evutil_closesocket</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sock</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 获取socket错误码 */</span></div></div><div><div><div>18</div></div><div><span>int</span><span> </span><span>evutil_socket_geterror</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sock</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>/* 设置socket重用地址 */</span></div></div><div><div><div>21</div></div><div><span>int</span><span> </span><span>evutil_socket_connect_</span><span>(</span><span>evutil_socket_t</span><span> </span><span>*</span><span><span>fd_ptr</span><span>,</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.2 Socket操作示例<a href="#22-socket操作示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 创建socket</span></div></div><div><div><div>6</div></div><div><span>    </span><span>evutil_socket_t</span><span> fd </span><span>=</span><span> </span><span>evutil_socket_</span><span>(AF_INET, SOCK_STREAM, </span><span>0</span><span>);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (fd </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>perror</span><span>(</span><span>"socket"</span><span>);</span></div></div><div><div><div>9</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 设置非阻塞</span></div></div><div><div><div>13</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_make_socket_nonblocking</span><span>(fd) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>perror</span><span>(</span><span>"nonblocking"</span><span>);</span></div></div><div><div><div>15</div></div><div><span>        </span><span>evutil_closesocket</span><span>(fd);</span></div></div><div><div><div>16</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// 设置close-on-exec</span></div></div><div><div><div>20</div></div><div><span>    </span><span>evutil_make_socket_closeonexec</span><span>(fd);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Socket created and configured</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>evutil_closesocket</span><span>(fd);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.3 Socket Pair示例<a href="#23-socket-pair示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>evutil_socket_t</span><span> </span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 创建socket pair</span></div></div><div><div><div>10</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_socketpair</span><span>(AF_UNIX, SOCK_STREAM, </span><span>0</span><span>, pair) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>perror</span><span>(</span><span>"socketpair"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 设置非阻塞</span></div></div><div><div><div>16</div></div><div><span>    </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>17</div></div><div><span>    </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// 写入数据</span></div></div><div><div><div>20</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"Hello from pair[0]"</span><span>;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>write</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>], msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>// 读取数据</span></div></div><div><div><div>24</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>64</span><span>];</span></div></div><div><div><div>25</div></div><div><span>    </span><span>ssize_t</span><span> n </span><span>=</span><span> </span><span>read</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>], buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>if</span><span> (n </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>28</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received: </span><span>%s</span><span>\n</span><span>"</span><span>, buf);</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>evutil_closesocket</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>32</div></div><div><span>    </span><span>evutil_closesocket</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、地址操作<a href="#三地址操作"><span>#</span></a></h2><section><h3>3.1 地址解析<a href="#31-地址解析"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 解析地址字符串</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param str      地址字符串，如 "192.168.1.1:8080" 或 "[::1]:8080"</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param out      输出sockaddr结构</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param outlen   输出长度</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @return         0成功，-1失败</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>evutil_parse_sockaddr_port</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>out</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span>                                </span><span>int</span><span> </span><span>*</span><span><span>outlen</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 比较两个地址 */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>evutil_sockaddr_cmp</span><span>(</span><span>const</span><span> </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa1</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa2</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span>                        </span><span>int</span><span><span> </span><span>include_port</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 获取地址字符串 */</span></div></div><div><div><div>15</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evutil_inet_ntop</span><span>(</span><span>int</span><span><span> </span><span>af</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>char</span><span> </span><span>*</span><span><span>dst</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>size</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 解析地址字符串 */</span></div></div><div><div><div>18</div></div><div><span>int</span><span> </span><span>evutil_inet_pton</span><span>(</span><span>int</span><span><span> </span><span>af</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>src</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>dst</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 地址操作示例<a href="#32-地址操作示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> len </span><span>=</span><span> </span><span>sizeof</span><span>(sin);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>// 解析地址字符串</span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_parse_sockaddr_port</span><span>(</span><span>"192.168.1.1:8080"</span><span>,</span></div></div><div><div><div>12</div></div><div><span><span>                                    </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>&amp;</span><span>len) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>char</span><span> </span><span>ip</span><span>[INET_ADDRSTRLEN];</span></div></div><div><div><div>14</div></div><div><span>        </span><span>evutil_inet_ntop</span><span>(AF_INET, </span><span>&amp;</span><span>sin</span><span>.</span><span>sin_addr</span><span>, ip, </span><span>sizeof</span><span>(ip));</span></div></div><div><div><div>15</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Parsed: </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>, ip, </span><span>ntohs</span><span>(</span><span>sin</span><span>.</span><span>sin_port</span><span>));</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 解析IPv6地址</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> sockaddr_in6 sin6;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>len </span><span>=</span><span> </span><span>sizeof</span><span>(sin6);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_parse_sockaddr_port</span><span>(</span><span>"[::1]:8080"</span><span>,</span></div></div><div><div><div>22</div></div><div><span><span>                                    </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin6, </span><span>&amp;</span><span>len) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>char</span><span> </span><span>ip</span><span>[INET6_ADDRSTRLEN];</span></div></div><div><div><div>24</div></div><div><span>        </span><span>evutil_inet_ntop</span><span>(AF_INET6, </span><span>&amp;</span><span>sin6</span><span>.</span><span>sin6_addr</span><span>, ip, </span><span>sizeof</span><span>(ip));</span></div></div><div><div><div>25</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Parsed IPv6: </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>, ip, </span><span>ntohs</span><span>(</span><span>sin6</span><span>.</span><span>sin6_port</span><span>));</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>// 比较地址</span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> sockaddr_in a1, a2;</span></div></div><div><div><div>30</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>a1, </span><span>0</span><span>, </span><span>sizeof</span><span>(a1));</span></div></div><div><div><div>31</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>a2, </span><span>0</span><span>, </span><span>sizeof</span><span>(a2));</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>a1</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>34</div></div><div><span>    </span><span>a1</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>inet_addr</span><span>(</span><span>"192.168.1.1"</span><span>);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>a1</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>80</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>a2</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>38</div></div><div><span>    </span><span>a2</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>inet_addr</span><span>(</span><span>"192.168.1.1"</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>a2</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>int</span><span> cmp </span><span>=</span><span> </span><span>evutil_sockaddr_cmp</span><span>((</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>a1,</span></div></div><div><div><div>42</div></div><div><span><span>                                   </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>a2, </span><span>0</span><span>);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Compare (ignore port): </span><span>%s</span><span>\n</span><span>"</span><span>, cmp </span><span>==</span><span> </span><span>0</span><span> </span><span>?</span><span> </span><span>"equal"</span><span> </span><span>:</span><span> </span><span>"different"</span><span>);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>cmp </span><span>=</span><span> </span><span>evutil_sockaddr_cmp</span><span>((</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>a1,</span></div></div><div><div><div>46</div></div><div><span><span>                               </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>a2, </span><span>1</span><span>);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Compare (with port): </span><span>%s</span><span>\n</span><span>"</span><span>, cmp </span><span>==</span><span> </span><span>0</span><span> </span><span>?</span><span> </span><span>"equal"</span><span> </span><span>:</span><span> </span><span>"different"</span><span>);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>50</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、字符串处理<a href="#四字符串处理"><span>#</span></a></h2><section><h3>4.1 字符串API<a href="#41-字符串api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 字符串转长整型 */</span></div></div><div><div><div>2</div></div><div><span>ev_int64_t</span><span> </span><span>evutil_strtoll</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>s</span><span>,</span><span> </span></span><span>char</span><span> </span><span>**</span><span><span>endptr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>base</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 安全的snprintf */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evutil_snprintf</span><span>(</span><span>char</span><span> </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>buflen</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>format</span><span>,</span><span> ...</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 安全的vsnprintf */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evutil_vsnprintf</span><span>(</span><span>char</span><span> </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>buflen</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>format</span><span>,</span><span> va_list </span><span>ap</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 不区分大小写的字符串比较 */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>evutil_ascii_strcasecmp</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str1</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str2</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>evutil_ascii_strncasecmp</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str1</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str2</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>n</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 字符串转小写 */</span></div></div><div><div><div>15</div></div><div><span>char</span><span> </span><span>*</span><span>evutil_ascii_strtolower</span><span>(</span><span>char</span><span> </span><span>*</span><span><span>str</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 字符串复制（跨平台） */</span></div></div><div><div><div>18</div></div><div><span>char</span><span> </span><span>*</span><span>evutil_strtoll</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>s</span><span>,</span><span> </span></span><span>char</span><span> </span><span>**</span><span><span>endptr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>base</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 字符串示例<a href="#42-字符串示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 字符串转数字</span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>num_str </span><span>=</span><span> </span><span>"12345abc"</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>end;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>ev_int64_t</span><span> num </span><span>=</span><span> </span><span>evutil_strtoll</span><span>(num_str, </span><span>&amp;</span><span>end, </span><span>10</span><span>);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Parsed: </span><span>%lld</span><span>, remaining: </span><span>%s</span><span>\n</span><span>"</span><span>, num, end);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 安全格式化</span></div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>32</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>evutil_snprintf</span><span>(buf, </span><span>sizeof</span><span>(buf), </span><span>"Value: </span><span>%d</span><span>, String: </span><span>%s</span><span>"</span><span>, </span><span>42</span><span>, </span><span>"test"</span><span>);</span></div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>%s</span><span>\n</span><span>"</span><span>, buf);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>// 不区分大小写比较</span></div></div><div><div><div>17</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>s1 </span><span>=</span><span> </span><span>"Hello"</span><span>;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>s2 </span><span>=</span><span> </span><span>"HELLO"</span><span>;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>int</span><span> cmp </span><span>=</span><span> </span><span>evutil_ascii_strcasecmp</span><span>(s1, s2);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Compare '</span><span>%s</span><span>' and '</span><span>%s</span><span>': </span><span>%s</span><span>\n</span><span>"</span><span>, s1, s2, cmp </span><span>==</span><span> </span><span>0</span><span> </span><span>?</span><span> </span><span>"equal"</span><span> </span><span>:</span><span> </span><span>"different"</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// 转小写</span></div></div><div><div><div>23</div></div><div><span>    </span><span>char</span><span> str</span><span>[]</span><span> </span><span>=</span><span> </span><span>"HELLO WORLD"</span><span>;</span></div></div><div><div><div>24</div></div><div><span>    </span><span>evutil_ascii_strtolower</span><span>(str);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Lowercase: </span><span>%s</span><span>\n</span><span>"</span><span>, str);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、时间操作<a href="#五时间操作"><span>#</span></a></h2><section><h3>5.1 时间API<a href="#51-时间api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取当前时间 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evutil_gettimeofday</span><span>(</span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>,</span><span> </span></span><span>struct</span><span> timezone </span><span>*</span><span><span>tz</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 清零时间 */</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>evutil_timerclear</span><span><span>(</span><span>tvp</span><span>)      ((tvp)-&gt;</span></span><span>tv_sec</span><span> </span><span>=</span><span> (tvp)-&gt;</span><span>tv_usec</span><span> </span><span>=</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 检查时间是否为0 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evutil_timerisset</span><span>(</span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tvp</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 比较时间 */</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>evutil_timercmp</span><span><span>(</span><span>tvp</span><span>, </span><span>uvp</span><span>, </span><span>cmp</span><span>)                     </span></span><span>\</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>(((tvp)-&gt;</span><span>tv_sec</span><span> </span><span>==</span><span> (uvp)-&gt;</span><span>tv_sec</span><span>) </span><span>?</span><span>                    </span><span>\</span></div></div><div><div><div>13</div></div><div><span><span>     </span></span><span>((tvp)-&gt;</span><span>tv_usec</span><span> </span><span>cmp</span><span> (uvp)-&gt;</span><span>tv_usec</span><span>) </span><span>:</span><span>                 </span><span>\</span></div></div><div><div><div>14</div></div><div><span><span>     </span></span><span>((tvp)-&gt;</span><span>tv_sec</span><span> </span><span>cmp</span><span><span> (uvp)</span><span>-&gt;</span></span><span>tv_sec</span><span>))</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 时间加法 */</span></div></div><div><div><div>17</div></div><div><span>void</span><span> </span><span>evutil_timeradd</span><span>(</span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>a</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>b</span><span>,</span></span></div></div><div><div><div>18</div></div><div><span>                     </span><span>struct</span><span> timeval </span><span>*</span><span><span>res</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>/* 时间减法 */</span></div></div><div><div><div>21</div></div><div><span>void</span><span> </span><span>evutil_timersub</span><span>(</span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>a</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>b</span><span>,</span></span></div></div><div><div><div>22</div></div><div><span>                     </span><span>struct</span><span> timeval </span><span>*</span><span><span>res</span><span>);</span></span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>/* 计算时间差（毫秒） */</span></div></div><div><div><div>25</div></div><div><span>ev_int64_t</span><span> </span><span>evutil_tv_to_msec</span><span>(</span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>/* 毫秒转时间 */</span></div></div><div><div><div>28</div></div><div><span>void</span><span> </span><span>evutil_msec_to_tv</span><span>(</span><span>ev_int64_t</span><span><span> </span><span>msec</span><span>,</span><span> </span></span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 时间示例<a href="#52-时间示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> timeval tv1, tv2, result;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>// 获取当前时间</span></div></div><div><div><div>8</div></div><div><span>    </span><span>evutil_gettimeofday</span><span>(</span><span>&amp;</span><span>tv1, </span><span>NULL</span><span>);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Current time: </span><span>%ld</span><span>.</span><span>%06ld</span><span>\n</span><span>"</span><span>, </span><span>tv1</span><span>.</span><span>tv_sec</span><span>, </span><span>tv1</span><span>.</span><span>tv_usec</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 时间加法</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> timeval delay </span><span>=</span><span> {</span><span>2</span><span>, </span><span>500000</span><span>};</span><span>  // 2.5秒</span></div></div><div><div><div>13</div></div><div><span>    </span><span>evutil_timeradd</span><span>(</span><span>&amp;</span><span>tv1, </span><span>&amp;</span><span>delay, </span><span>&amp;</span><span>result);</span></div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"After 2.5s: </span><span>%ld</span><span>.</span><span>%06ld</span><span>\n</span><span>"</span><span>, </span><span>result</span><span>.</span><span>tv_sec</span><span>, </span><span>result</span><span>.</span><span>tv_usec</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>// 时间比较</span></div></div><div><div><div>17</div></div><div><span>    </span><span>evutil_gettimeofday</span><span>(</span><span>&amp;</span><span>tv2, </span><span>NULL</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_timercmp</span><span>(</span><span>&amp;</span><span>tv2, </span><span>&amp;</span><span>tv1, </span><span>&gt;</span><span>)) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>printf</span><span>(</span><span>"tv2 &gt; tv1</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// 时间差</span></div></div><div><div><div>23</div></div><div><span>    </span><span>evutil_timersub</span><span>(</span><span>&amp;</span><span>tv2, </span><span>&amp;</span><span>tv1, </span><span>&amp;</span><span>result);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Time elapsed: </span><span>%ld</span><span>.</span><span>%06ld</span><span> seconds</span><span>\n</span><span>"</span><span>, </span><span>result</span><span>.</span><span>tv_sec</span><span>, </span><span>result</span><span>.</span><span>tv_usec</span><span>);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// 转换为毫秒</span></div></div><div><div><div>27</div></div><div><span>    </span><span>ev_int64_t</span><span> msec </span><span>=</span><span> </span><span>evutil_tv_to_msec</span><span>(</span><span>&amp;</span><span>result);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Time elapsed: </span><span>%lld</span><span> ms</span><span>\n</span><span>"</span><span>, msec);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>31</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、随机数<a href="#六随机数"><span>#</span></a></h2><section><h3>6.1 随机数API<a href="#61-随机数api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 初始化安全随机数生成器 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evutil_secure_rng_init</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 设置随机数设备文件 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evutil_secure_rng_set_urandom_device_file</span><span>(</span><span>char</span><span> </span><span>*</span><span><span>fname</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 获取安全随机字节 */</span></div></div><div><div><div>8</div></div><div><span>void</span><span> </span><span>evutil_secure_rng_get_bytes</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>n</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 检查是否支持安全随机数 */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>evutil_secure_rng_global_setup_locks_</span><span>(</span><span>int</span><span><span> </span><span>enable_locks</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 随机数示例<a href="#62-随机数示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 初始化安全随机数</span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_secure_rng_init</span><span>() </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to initialize secure RNG</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 生成随机数</span></div></div><div><div><div>12</div></div><div><span>    </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>bytes</span><span>[</span><span>16</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>evutil_secure_rng_get_bytes</span><span>(bytes, </span><span>sizeof</span><span>(bytes));</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Random bytes: "</span><span>);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>sizeof</span><span>(bytes); i</span><span>++</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>%02x</span><span>"</span><span>, </span><span>bytes</span><span>[i]);</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 生成随机整数</span></div></div><div><div><div>22</div></div><div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> random_int;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>evutil_secure_rng_get_bytes</span><span>(</span><span>&amp;</span><span>random_int, </span><span>sizeof</span><span>(random_int));</span></div></div><div><div><div>24</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Random int: </span><span>%u</span><span>\n</span><span>"</span><span>, random_int);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、错误处理<a href="#七错误处理"><span>#</span></a></h2><section><h3>7.1 错误API<a href="#71-错误api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取最后错误码 */</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>evutil_socket_geterror</span><span><span>(</span><span>sock</span><span>) ...</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取错误字符串 */</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evutil_socket_error_to_string</span><span>(</span><span>int</span><span><span> </span><span>err</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 设置错误码 */</span></div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>evutil_socket_seterror</span><span><span>(</span><span>err</span><span>) ...</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 错误码定义 */</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>()    errno</span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>EVUTIL_SET_SOCKET_ERROR</span><span><span>(</span><span>err</span><span>) (errno </span></span><span>=</span><span> (err))</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>7.2 错误处理示例<a href="#72-错误处理示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>evutil_socket_t</span><span> fd </span><span>=</span><span> </span><span>evutil_socket_</span><span>(AF_INET, SOCK_STREAM, </span><span>0</span><span>);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (fd </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>int</span><span> err </span><span>=</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>();</span></div></div><div><div><div>9</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Socket error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>10</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>// 尝试连接一个无效地址</span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>16</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>12345</span><span>);</span></div></div><div><div><div>17</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>inet_addr</span><span>(</span><span>"0.0.0.0"</span><span>);</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>if</span><span> (</span><span>connect</span><span>(fd, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin)) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>20</div></div><div><span>        </span><span>int</span><span> err </span><span>=</span><span> </span><span>evutil_socket_geterror</span><span>(fd);</span></div></div><div><div><div>21</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connect error (</span><span>%d</span><span>): </span><span>%s</span><span>\n</span><span>"</span><span>, err, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>evutil_closesocket</span><span>(fd);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、内存操作<a href="#八内存操作"><span>#</span></a></h2><section><h3>8.1 内存API<a href="#81-内存api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 内存对齐分配 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>*</span><span>evutil_memalign_</span><span>(</span><span>size_t</span><span><span> </span><span>alignment</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>size</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 内存比较（恒定时间） */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evutil_memcmp_</span><span>(</span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>a</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>b</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>len</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 安全内存清零 */</span></div></div><div><div><div>8</div></div><div><span>void</span><span> </span><span>evutil_memclear_</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>mem</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>len</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>九、文件描述符操作<a href="#九文件描述符操作"><span>#</span></a></h2><section><h3>9.1 文件描述符API<a href="#91-文件描述符api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置FD_CLOEXEC */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evutil_make_fd_closeonexec</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 复制文件描述符 */</span></div></div><div><div><div>5</div></div><div><span>evutil_socket_t</span><span> </span><span>evutil_dupfd_</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 设置文件描述符标志 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evutil_fd_flag_set</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>flag</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>on</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>十、网络相关常量<a href="#十网络相关常量"><span>#</span></a></h2><section><h3>10.1 常用宏定义<a href="#101-常用宏定义"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 地址族 */</span></div></div><div><div><div>2</div></div><div><span>#ifndef</span><span> </span><span>AF_INET6</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>AF_INET6</span><span> </span><span>10</span></div></div><div><div><div>4</div></div><div><span>#endif</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* socket类型 */</span></div></div><div><div><div>7</div></div><div><span>#ifndef</span><span> </span><span>SOCK_NONBLOCK</span></div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>SOCK_NONBLOCK</span><span> </span><span>0</span></div></div><div><div><div>9</div></div><div><span>#endif</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>#ifndef</span><span> </span><span>SOCK_CLOEXEC</span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>SOCK_CLOEXEC</span><span> </span><span>0</span></div></div><div><div><div>13</div></div><div><span>#endif</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>/* 错误码 */</span></div></div><div><div><div>16</div></div><div><span>#ifndef</span><span> </span><span>EWOULDBLOCK</span></div></div><div><div><div>17</div></div><div><span>#define</span><span> </span><span>EWOULDBLOCK</span><span> EAGAIN</span></div></div><div><div><div>18</div></div><div><span>#endif</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>#ifndef</span><span> </span><span>EINPROGRESS</span></div></div><div><div><div>21</div></div><div><span>#define</span><span> </span><span>EINPROGRESS</span><span> WSAEINPROGRESS</span></div></div><div><div><div>22</div></div><div><span>#endif</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十一、跨平台宏<a href="#十一跨平台宏"><span>#</span></a></h2><section><h3>11.1 平台检测<a href="#111-平台检测"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>2</div></div><div><span>    </span><span>// Windows平台</span></div></div><div><div><div>3</div></div><div><span>    </span><span>#define</span><span> </span><span>EVUTIL_SOCKET_IS_INVALID</span><span><span>(</span><span>s</span><span>) ((s) </span></span><span>==</span><span> INVALID_SOCKET)</span></div></div><div><div><div>4</div></div><div><span>    </span><span>#define</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>() </span><span>WSAGetLastError</span><span>()</span></div></div><div><div><div>5</div></div><div><span>#else</span></div></div><div><div><div>6</div></div><div><span>    </span><span>// Unix/Linux平台</span></div></div><div><div><div>7</div></div><div><span>    </span><span>#define</span><span> </span><span>EVUTIL_SOCKET_IS_INVALID</span><span><span>(</span><span>s</span><span>) ((s) </span></span><span>&lt;</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>8</div></div><div><span>    </span><span>#define</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>() (errno)</span></div></div><div><div><div>9</div></div><div><span>#endif</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>十二、完整示例：跨平台TCP客户端<a href="#十二完整示例跨平台tcp客户端"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>SERVER_IP</span><span> </span><span>"127.0.0.1"</span></div></div><div><div><div>9</div></div><div><span>#define</span><span> </span><span>SERVER_PORT</span><span> </span><span>8080</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>14</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>16</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received: </span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected to server</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>23</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, </span><span>"Hello, Server!</span><span>\n</span><span>"</span><span>, </span><span>15</span><span>);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>26</div></div><div><span>            </span><span>int</span><span> err </span><span>=</span><span> </span><span>evutil_socket_geterror</span><span>(</span><span>bufferevent_getfd</span><span>(bev));</span></div></div><div><div><div>27</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>30</div></div><div><span>        </span><span>event_base_loopexit</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg, </span><span>NULL</span><span>);</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, </span><span>-</span><span>1</span><span>, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>// 使用evutil解析地址</span></div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> sockaddr_storage ss;</span></div></div><div><div><div>40</div></div><div><span>    </span><span>int</span><span> ss_len </span><span>=</span><span> </span><span>sizeof</span><span>(ss);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>char</span><span> </span><span>addr_str</span><span>[</span><span>64</span><span>];</span></div></div><div><div><div>42</div></div><div><span>    </span><span>evutil_snprintf</span><span>(addr_str, </span><span>sizeof</span><span>(addr_str), </span><span>"</span><span>%s</span><span>:</span><span>%d</span><span>"</span><span>, SERVER_IP, SERVER_PORT);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>if</span><span> (</span><span>evutil_parse_sockaddr_port</span><span>(addr_str, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>ss, </span><span>&amp;</span><span>ss_len) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>45</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Invalid address: </span><span>%s</span><span>\n</span><span>"</span><span>, addr_str);</span></div></div><div><div><div>46</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connecting to </span><span>%s</span><span>...</span><span>\n</span><span>"</span><span>, addr_str);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, base);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>if</span><span> (</span><span>bufferevent_socket_connect</span><span>(bev, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>ss, ss_len) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>55</div></div><div><span>        </span><span>int</span><span> err </span><span>=</span><span> </span><span>evutil_socket_geterror</span><span>(</span><span>bufferevent_getfd</span><span>(bev));</span></div></div><div><div><div>56</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connect failed: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>57</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>58</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>59</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>65</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>66</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十三、工具函数速查表<a href="#十三工具函数速查表"><span>#</span></a></h2>

<table><thead><tr><th>类别</th><th>函数</th><th>说明</th></tr></thead><tbody><tr><td><strong>Socket</strong></td><td><code>evutil_socket_</code></td><td>创建socket</td></tr><tr><td></td><td><code>evutil_socketpair</code></td><td>创建socket pair</td></tr><tr><td></td><td><code>evutil_make_socket_nonblocking</code></td><td>设置非阻塞</td></tr><tr><td></td><td><code>evutil_closesocket</code></td><td>关闭socket</td></tr><tr><td><strong>地址</strong></td><td><code>evutil_parse_sockaddr_port</code></td><td>解析地址</td></tr><tr><td></td><td><code>evutil_sockaddr_cmp</code></td><td>比较地址</td></tr><tr><td></td><td><code>evutil_inet_ntop</code></td><td>地址转字符串</td></tr><tr><td></td><td><code>evutil_inet_pton</code></td><td>字符串转地址</td></tr><tr><td><strong>字符串</strong></td><td><code>evutil_snprintf</code></td><td>安全格式化</td></tr><tr><td></td><td><code>evutil_strtoll</code></td><td>字符串转数字</td></tr><tr><td></td><td><code>evutil_ascii_strcasecmp</code></td><td>不区分大小写比较</td></tr><tr><td><strong>时间</strong></td><td><code>evutil_gettimeofday</code></td><td>获取时间</td></tr><tr><td></td><td><code>evutil_timeradd</code></td><td>时间加法</td></tr><tr><td></td><td><code>evutil_timersub</code></td><td>时间减法</td></tr><tr><td></td><td><code>evutil_timercmp</code></td><td>时间比较</td></tr><tr><td><strong>随机数</strong></td><td><code>evutil_secure_rng_init</code></td><td>初始化随机数</td></tr><tr><td></td><td><code>evutil_secure_rng_get_bytes</code></td><td>获取随机字节</td></tr><tr><td><strong>错误</strong></td><td><code>evutil_socket_geterror</code></td><td>获取错误码</td></tr><tr><td></td><td><code>evutil_socket_error_to_string</code></td><td>错误码转字符串</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-faq/</id>
      <title type="text">❓ 常见问题FAQ - libevent源码深度解析系列（二十六）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-faq/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>❓ 常见问题FAQ<a href="#-常见问题faq"><span>#</span></a></h1><section><h2>一、编译与安装问题<a href="#一编译与安装问题"><span>#</span></a></h2><section><h3>Q1: 找不到libevent头文件<a href="#q1-找不到libevent头文件"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>fatal error: event2/event.h: No such file or directory</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># Ubuntu/Debian</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>apt-get</span><span> </span><span>install</span><span> </span><span>libevent-dev</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># CentOS/RHEL</span></div></div><div><div><div>5</div></div><div><span>sudo</span><span> </span><span>yum</span><span> </span><span>install</span><span> </span><span>libevent-devel</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># macOS</span></div></div><div><div><div>8</div></div><div><span>brew</span><span> </span><span>install</span><span> </span><span>libevent</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 编译时指定路径</span></div></div><div><div><div>11</div></div><div><span>gcc</span><span> </span><span>-I/usr/local/include</span><span> </span><span>-L/usr/local/lib</span><span> </span><span>-levent</span><span> </span><span>main.c</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Q2: 链接时找不到libevent库<a href="#q2-链接时找不到libevent库"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>undefined reference to `event_base_new'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译时添加 -levent</span></div></div><div><div><div>2</div></div><div><span>gcc</span><span> </span><span>main.c</span><span> </span><span>-levent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 或使用 pkg-config</span></div></div><div><div><div>5</div></div><div><span>gcc</span><span> </span><span>main.c</span><span> $(</span><span>pkg-config</span><span> </span><span>--libs</span><span> </span><span>libevent</span><span>)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 动态库路径问题</span></div></div><div><div><div>8</div></div><div><span>export</span><span> </span><span>LD_LIBRARY_PATH</span><span><span>=</span><span>/</span></span><span>usr</span><span>/</span><span>local</span><span>/</span><span>lib</span><span>:</span><span>$LD_LIBRARY_PATH</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 或添加到 /etc/ld.so.conf.d/</span></div></div><div><div><div>11</div></div><div><span>echo</span><span> </span><span>"/usr/local/lib"</span><span> | </span><span>sudo</span><span> </span><span>tee</span><span> </span><span>/etc/ld.so.conf.d/libevent.conf</span></div></div><div><div><div>12</div></div><div><span>sudo</span><span> </span><span>ldconfig</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Q3: OpenSSL支持未编译<a href="#q3-openssl支持未编译"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>bufferevent_openssl_socket_new: undefined reference</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 重新编译libevent时启用OpenSSL</span></div></div><div><div><div>2</div></div><div><span>./configure</span><span> </span><span>--enable-openssl</span></div></div><div><div><div>3</div></div><div><span>make</span><span> &amp;&amp; </span><span>sudo</span><span> </span><span>make</span><span> </span><span>install</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 编译时同时链接OpenSSL</span></div></div><div><div><div>6</div></div><div><span>gcc</span><span> </span><span>main.c</span><span> </span><span>-levent</span><span> </span><span>-lssl</span><span> </span><span>-lcrypto</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>二、事件循环问题<a href="#二事件循环问题"><span>#</span></a></h2><section><h3>Q4: event_base_dispatch不返回<a href="#q4-event_base_dispatch不返回"><span>#</span></a></h3><p><strong>问题现象：</strong>
程序卡在<code>event_base_dispatch()</code>无法退出。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 方案1: 使用 event_base_loopbreak</span></div></div><div><div><div>2</div></div><div><span>event_base_loopbreak</span><span><span>(</span><span>base</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 方案2: 使用 event_base_loopexit</span></div></div><div><div><div>5</div></div><div><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>0</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>6</div></div><div><span>event_base_loopexit</span><span><span>(</span><span>base</span><span>,</span><span> </span></span><span>&amp;</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 方案3: 使用非阻塞循环</span></div></div><div><div><div>9</div></div><div><span>while</span><span> (running) {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_NONBLOCK);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// 执行其他任务</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 方案4: 通过信号触发退出</span></div></div><div><div><div>15</div></div><div><span>void</span><span> </span><span>signal_handler</span><span>(</span><span>int</span><span><span> </span><span>sig</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(base);</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div><span>signal</span><span><span>(</span><span>SIGINT</span><span>,</span><span> signal_handler</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q5: 定时器不准确<a href="#q5-定时器不准确"><span>#</span></a></h3><p><strong>问题现象：</strong>
定时器触发时间与预期不符。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 方案1: 使用 EV_PERSIST 持续定时</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span><span>(</span><span>base</span><span>,</span><span> </span></span><span>-</span><span>1</span><span><span>,</span><span> EV_PERSIST</span><span>,</span><span> timer_cb</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>3</div></div><div><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span><span>  // 1秒</span></div></div><div><div><div>4</div></div><div><span>event_add</span><span><span>(</span><span>timer</span><span>,</span><span> </span></span><span>&amp;</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 方案2: 每次重新添加（更精确）</span></div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> arg;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>// 处理...</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 重新添加</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>13</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 方案3: 禁用时间缓存</span></div></div><div><div><div>17</div></div><div><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>18</div></div><div><span>event_config_set_flag</span><span><span>(</span><span>cfg</span><span>,</span><span> EVENT_BASE_FLAG_NO_CACHE_TIME</span><span>);</span></span></div></div><div><div><div>19</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span><span>(</span><span>cfg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q6: 事件丢失<a href="#q6-事件丢失"><span>#</span></a></h3><p><strong>问题现象：</strong>
某些事件没有被触发。</p><p><strong>解决方案：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 检查事件是否活跃</span></div></div><div><div><div>2</div></div><div><span>if</span><span> (</span><span>!</span><span>event_pending</span><span><span>(</span><span>ev</span><span>,</span><span> EV_READ</span><span>,</span><span> </span></span><span>NULL</span><span>)) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>event_add</span><span>(ev, </span><span>NULL</span><span>);</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 确保事件已启用</span></div></div><div><div><div>7</div></div><div><span>event_add</span><span><span>(</span><span>ev</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 检查是否被删除</span></div></div><div><div><div>10</div></div><div><span>if</span><span> (</span><span>event_get_events</span><span><span>(</span><span>ev</span><span>) </span></span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event is not active</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、bufferevent问题<a href="#三bufferevent问题"><span>#</span></a></h2><section><h3>Q7: 数据读取不完整<a href="#q7-数据读取不完整"><span>#</span></a></h3><p><strong>问题现象：</strong>
读取的数据比预期少。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>    </span><span>// 方案1: 循环读取直到缓冲区空</span></div></div><div><div><div>5</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>6</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>process_data</span><span>(buf, n);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 方案2: 检查缓冲区长度</span></div></div><div><div><div>12</div></div><div><span>    </span><span>size_t</span><span> total </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(input);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>if</span><span> (total </span><span>&lt;</span><span> EXPECTED_SIZE) {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>return</span><span>;</span><span>  // 等待更多数据</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// 方案3: 使用水位控制</span></div></div><div><div><div>18</div></div><div><span>    </span><span>bufferevent_setwatermark</span><span>(bev, EV_READ, EXPECTED_SIZE, </span><span>0</span><span>);</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q8: 写入数据丢失<a href="#q8-写入数据丢失"><span>#</span></a></h3><p><strong>问题现象：</strong>
写入的数据对方没有收到。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 方案1: 检查写入结果</span></div></div><div><div><div>2</div></div><div><span>int</span><span> ret </span><span>=</span><span> </span><span>bufferevent_write</span><span><span>(</span><span>bev</span><span>,</span><span> data</span><span>,</span><span> len</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>if</span><span> (ret </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>perror</span><span>(</span><span>"bufferevent_write failed"</span><span>);</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 方案2: 使用写回调确认</span></div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>write_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Data written successfully</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>bufferevent_setcb</span><span><span>(</span><span>bev</span><span>,</span><span> read_cb</span><span>,</span><span> write_cb</span><span>,</span><span> event_cb</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>// 方案3: 检查输出缓冲区</span></div></div><div><div><div>15</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span><span>(</span><span>bev</span><span>);</span></span></div></div><div><div><div>16</div></div><div><span>if</span><span> (</span><span>evbuffer_get_length</span><span><span>(</span><span>output</span><span>) </span></span><span>&gt;</span><span> MAX_BUFFER) {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Output buffer full, pausing</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>bufferevent_disable</span><span>(bev, EV_WRITE);</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q9: 连接超时处理<a href="#q9-连接超时处理"><span>#</span></a></h3><p><strong>问题现象：</strong>
连接长时间没有响应。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 设置连接超时</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>10</span><span>, </span><span>0</span><span>};</span><span>  // 10秒</span></div></div><div><div><div>3</div></div><div><span>bufferevent_set_timeouts</span><span><span>(</span><span>bev</span><span>,</span><span> </span></span><span>&amp;</span><span><span>tv</span><span>,</span><span> </span></span><span>&amp;</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_TIMEOUT) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed/error</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>13</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q10: 内存泄漏<a href="#q10-内存泄漏"><span>#</span></a></h3><p><strong>问题现象：</strong>
程序内存持续增长。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 确保释放所有资源</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>cleanup_connection</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>// 先禁用事件</span></div></div><div><div><div>4</div></div><div><span>    </span><span>bufferevent_disable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>// 清空缓冲区</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evbuffer_drain</span><span>(input, </span><span>evbuffer_get_length</span><span>(input));</span></div></div><div><div><div>10</div></div><div><span>    </span><span>evbuffer_drain</span><span>(output, </span><span>evbuffer_get_length</span><span>(output));</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 释放bufferevent</span></div></div><div><div><div>13</div></div><div><span>    </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 2. 使用 valgrind 检测</span></div></div><div><div><div>17</div></div><div><span>// valgrind --leak-check=full ./your_program</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 3. 检查回调是否正确设置</span></div></div><div><div><div>20</div></div><div><span>bufferevent_setcb</span><span><span>(</span><span>bev</span><span>,</span><span> read_cb</span><span>,</span><span> </span></span><span>NULL</span><span><span>,</span><span> event_cb</span><span>,</span><span> arg</span><span>);</span></span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>// 4. 确保用户数据正确释放</span></div></div><div><div><div>23</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>24</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>buffer;</span></div></div><div><div><div>25</div></div><div><span>    </span><span>size_t</span><span> len;</span></div></div><div><div><div>26</div></div><div><span>} </span><span>user_data_t</span><span>;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>user_data_t</span><span> </span><span>*</span><span>data </span><span>=</span><span> arg;</span></div></div><div><div><div>30</div></div><div><span>    </span><span>free</span><span>(</span><span>data</span><span>-&gt;</span><span>buffer</span><span>);</span></div></div><div><div><div>31</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>32</div></div><div><span>    </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>33</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、多线程问题<a href="#四多线程问题"><span>#</span></a></h2><section><h3>Q11: 多线程崩溃<a href="#q11-多线程崩溃"><span>#</span></a></h3><p><strong>问题现象：</strong>
多线程环境下程序崩溃。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 初始化线程支持</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/thread.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>5</div></div><div><span>    </span><span>evthread_use_windows_threads</span><span>();</span></div></div><div><div><div>6</div></div><div><span>#else</span></div></div><div><div><div>7</div></div><div><span>    </span><span>evthread_use_pthreads</span><span>();</span></div></div><div><div><div>8</div></div><div><span>#endif</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 2. 创建线程安全的event_base</span></div></div><div><div><div>11</div></div><div><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>12</div></div><div><span>event_config_set_flag</span><span><span>(</span><span>cfg</span><span>,</span><span> </span></span><span>0</span><span>);</span><span>  // 不使用NOLOCK</span></div></div><div><div><div>13</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span><span>(</span><span>cfg</span><span>);</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 3. 使用锁保护共享资源</span></div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>pthread_mutex_t</span><span> g_mutex </span><span>=</span><span> PTHREAD_MUTEX_INITIALIZER;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>void</span><span> </span><span>thread_safe_operation</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>    </span><span>pthread_mutex_lock</span><span>(</span><span>&amp;</span><span>g_mutex);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>// 操作共享资源</span></div></div><div><div><div>21</div></div><div><span>    </span><span>pthread_mutex_unlock</span><span>(</span><span>&amp;</span><span>g_mutex);</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>// 4. 跨线程操作</span></div></div><div><div><div>25</div></div><div><span>void</span><span> </span><span>notify_main_thread</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>26</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(base);</span><span>  // 线程安全</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q12: 跨线程事件触发<a href="#q12-跨线程事件触发"><span>#</span></a></h3><p><strong>问题现象：</strong>
需要从其他线程触发事件。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/thread.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 方案1: 使用 event_active</span></div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>struct</span><span> event </span><span>*</span><span>g_event </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>void</span><span> </span><span>trigger_from_other_thread</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>event_active</span><span>(g_event, EV_READ, </span><span>0</span><span>);</span><span>  // 线程安全</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 方案2: 使用 self-pipe</span></div></div><div><div><div>12</div></div><div><span>int</span><span> </span><span>notify_pipe</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>void</span><span> </span><span>setup_notify</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>evutil_socketpair</span><span>(AF_UNIX, SOCK_STREAM, </span><span>0</span><span>, notify_pipe);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>ev </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>notify_pipe</span><span>[</span><span>0</span><span>],</span></div></div><div><div><div>18</div></div><div><span><span>                                  </span></span><span>EV_READ </span><span>|</span><span> EV_PERSIST, notify_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>event_add</span><span>(ev, </span><span>NULL</span><span>);</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>void</span><span> </span><span>notify_from_thread</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>    </span><span>write</span><span>(</span><span>notify_pipe</span><span>[</span><span>1</span><span>], </span><span>"x"</span><span>, </span><span>1</span><span>);</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>// 方案3: 使用 event_base_once</span></div></div><div><div><div>27</div></div><div><span>void</span><span> </span><span>thread_safe_callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>// 在主线程执行</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>void</span><span> </span><span>schedule_on_main_thread</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>32</div></div><div><span>    </span><span>event_base_once</span><span>(base, </span><span>-</span><span>1</span><span>, EV_TIMEOUT, thread_safe_callback, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>33</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、网络问题<a href="#五网络问题"><span>#</span></a></h2><section><h3>Q13: 连接被拒绝<a href="#q13-连接被拒绝"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Connection refused</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>int</span><span> err </span><span>=</span><span> </span><span>evutil_socket_geterror</span><span>(</span><span>bufferevent_getfd</span><span>(bev));</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>        </span><span>if</span><span> (err </span><span>==</span><span> ECONNREFUSED) {</span></div></div><div><div><div>6</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Connection refused, retrying...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>7</div></div><div><span>            </span><span>// 重试逻辑</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (err </span><span>==</span><span> ETIMEDOUT) {</span></div></div><div><div><div>9</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Connection timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>11</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Connection error: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>12</div></div><div><span>                   </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// 检查服务器是否监听</span></div></div><div><div><div>18</div></div><div><span>// netstat -tlnp | grep &lt;port&gt;</span></div></div><div><div><div>19</div></div><div><span>// lsof -i :&lt;port&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q14: 端口被占用<a href="#q14-端口被占用"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Address already in use</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 方案1: 设置 SO_REUSEADDR</span></div></div><div><div><div>2</div></div><div><span>int</span><span> reuse </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>3</div></div><div><span>setsockopt</span><span><span>(</span><span>fd</span><span>,</span><span> SOL_SOCKET</span><span>,</span><span> SO_REUSEADDR</span><span>,</span><span> </span></span><span>&amp;</span><span><span>reuse</span><span>,</span><span> </span></span><span>sizeof</span><span><span>(</span><span>reuse</span><span>));</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 方案2: 使用 evconnlistener 的 REUSEABLE 标志</span></div></div><div><div><div>6</div></div><div><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>base</span><span>,</span><span> accept_cb</span><span>,</span><span> arg</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span><span> LEV_OPT_REUSEABLE</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>-</span><span>1</span><span><span>,</span><span> </span><span>(</span></span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span><span>sin</span><span>,</span><span> </span></span><span>sizeof</span><span><span>(</span><span>sin</span><span>));</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 方案3: 查找占用进程</span></div></div><div><div><div>12</div></div><div><span>// lsof -i :&lt;port&gt;</span></div></div><div><div><div>13</div></div><div><span>// kill &lt;pid&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Q15: DNS解析失败<a href="#q15-dns解析失败"><span>#</span></a></h3><p><strong>问题现象：</strong>
DNS查询返回错误。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>dns_cb</span><span>(</span><span>int</span><span><span> </span><span>result</span><span>,</span><span> </span></span><span>char</span><span><span> </span><span>type</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>count</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ttl</span><span>,</span></span></div></div><div><div><div>2</div></div><div><span>                   </span><span>void</span><span> </span><span>*</span><span><span>addresses</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span> (result </span><span>==</span><span> DNS_ERR_NONE) {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>// 成功</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>printf</span><span>(</span><span>"DNS error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evdns_err_to_string</span><span>(result));</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>        </span><span>// 常见错误处理</span></div></div><div><div><div>9</div></div><div><span>        </span><span>switch</span><span> (result) {</span></div></div><div><div><div>10</div></div><div><span>            </span><span>case</span><span> DNS_ERR_TIMEOUT:</span></div></div><div><div><div>11</div></div><div><span>                </span><span>printf</span><span>(</span><span>"DNS timeout, retrying...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>13</div></div><div><span>            </span><span>case</span><span> DNS_ERR_NOTEXIST:</span></div></div><div><div><div>14</div></div><div><span>                </span><span>printf</span><span>(</span><span>"Domain does not exist</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>15</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>16</div></div><div><span>            </span><span>case</span><span> DNS_ERR_SERVERFAILED:</span></div></div><div><div><div>17</div></div><div><span>                </span><span>printf</span><span>(</span><span>"DNS server failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>// 配置多个DNS服务器</span></div></div><div><div><div>24</div></div><div><span>evdns_base_nameserver_ip_add</span><span><span>(</span><span>dns_base</span><span>,</span><span> </span></span><span>"8.8.8.8"</span><span>);</span></div></div><div><div><div>25</div></div><div><span>evdns_base_nameserver_ip_add</span><span><span>(</span><span>dns_base</span><span>,</span><span> </span></span><span>"1.1.1.1"</span><span>);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>// 设置超时</span></div></div><div><div><div>28</div></div><div><span>evdns_base_set_option</span><span><span>(</span><span>dns_base</span><span>,</span><span> </span></span><span>"timeout"</span><span><span>,</span><span> </span></span><span>"5"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、SSL/TLS问题<a href="#六ssltls问题"><span>#</span></a></h2><section><h3>Q16: SSL握手失败<a href="#q16-ssl握手失败"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>SSL handshake failed</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>unsigned</span><span> </span><span>long</span><span> err;</span></div></div><div><div><div>4</div></div><div><span>        </span><span>while</span><span> ((err </span><span>=</span><span> </span><span>bufferevent_get_openssl_error</span><span>(bev))) {</span></div></div><div><div><div>5</div></div><div><span>            </span><span>char</span><span> </span><span>msg</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>6</div></div><div><span>            </span><span>ERR_error_string_n</span><span>(err, msg, </span><span>sizeof</span><span>(msg));</span></div></div><div><div><div>7</div></div><div><span>            </span><span>fprintf</span><span>(stderr, </span><span>"SSL error: </span><span>%s</span><span>\n</span><span>"</span><span>, msg);</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// 常见原因及解决</span></div></div><div><div><div>13</div></div><div><span>// 1. 证书问题</span></div></div><div><div><div>14</div></div><div><span>SSL_CTX_load_verify_locations</span><span><span>(</span><span>ctx</span><span>,</span><span> </span></span><span>"ca.crt"</span><span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 2. 协议版本不匹配</span></div></div><div><div><div>17</div></div><div><span>SSL_CTX_set_min_proto_version</span><span><span>(</span><span>ctx</span><span>,</span><span> TLS1_2_VERSION</span><span>);</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 3. 密码套件不支持</span></div></div><div><div><div>20</div></div><div><span>SSL_CTX_set_cipher_list</span><span><span>(</span><span>ctx</span><span>,</span><span> </span></span><span>"HIGH:!aNULL:!MD5"</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>// 4. SNI未设置</span></div></div><div><div><div>23</div></div><div><span>SSL_set_tlsext_host_name</span><span><span>(</span><span>ssl</span><span>,</span><span> hostname</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q17: 证书验证失败<a href="#q17-证书验证失败"><span>#</span></a></h3><p><strong>问题现象：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>certificate verify failed</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 加载正确的CA证书</span></div></div><div><div><div>2</div></div><div><span>SSL_CTX_load_verify_locations</span><span><span>(</span><span>ctx</span><span>,</span><span> </span></span><span>"/path/to/ca-bundle.crt"</span><span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 2. 使用系统CA证书</span></div></div><div><div><div>5</div></div><div><span>SSL_CTX_set_default_verify_paths</span><span><span>(</span><span>ctx</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 3. 设置验证模式</span></div></div><div><div><div>8</div></div><div><span>SSL_CTX_set_verify</span><span><span>(</span><span>ctx</span><span>,</span><span> SSL_VERIFY_PEER</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 4. 设置主机名验证</span></div></div><div><div><div>11</div></div><div><span>SSL_set1_host</span><span><span>(</span><span>ssl</span><span>,</span><span> </span></span><span>"example.com"</span><span>);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 5. 调试证书信息</span></div></div><div><div><div>14</div></div><div><span>void</span><span> </span><span>print_cert_info</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>X509 </span><span>*</span><span>cert </span><span>=</span><span> </span><span>SSL_get_peer_certificate</span><span>(ssl);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span> (cert) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>char</span><span> </span><span>*</span><span>subject </span><span>=</span><span> </span><span>X509_NAME_oneline</span><span>(</span><span>X509_get_subject_name</span><span>(cert), </span><span>NULL</span><span>, </span><span>0</span><span>);</span></div></div><div><div><div>18</div></div><div><span>        </span><span>char</span><span> </span><span>*</span><span>issuer </span><span>=</span><span> </span><span>X509_NAME_oneline</span><span>(</span><span>X509_get_issuer_name</span><span>(cert), </span><span>NULL</span><span>, </span><span>0</span><span>);</span></div></div><div><div><div>19</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Subject: </span><span>%s</span><span>\n</span><span>"</span><span>, subject);</span></div></div><div><div><div>20</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Issuer: </span><span>%s</span><span>\n</span><span>"</span><span>, issuer);</span></div></div><div><div><div>21</div></div><div><span>        </span><span>OPENSSL_free</span><span>(subject);</span></div></div><div><div><div>22</div></div><div><span>        </span><span>OPENSSL_free</span><span>(issuer);</span></div></div><div><div><div>23</div></div><div><span>        </span><span>X509_free</span><span>(cert);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、性能问题<a href="#七性能问题"><span>#</span></a></h2><section><h3>Q18: CPU占用过高<a href="#q18-cpu占用过高"><span>#</span></a></h3><p><strong>问题现象：</strong>
程序CPU使用率接近100%。</p><p><strong>解决方案：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 检查是否有空循环</span></div></div><div><div><div>2</div></div><div><span>while</span><span> (running) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>// 错误: 使用 EVLOOP_ONCE 而不是 EVLOOP_NONBLOCK</span></div></div><div><div><div>4</div></div><div><span>    </span><span>event_base_loop</span><span>(base, EVLOOP_ONCE);</span><span>  // 阻塞等待事件</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 2. 减少不必要的回调</span></div></div><div><div><div>8</div></div><div><span>bufferevent_setwatermark</span><span><span>(</span><span>bev</span><span>,</span><span> EV_READ</span><span>,</span><span> </span></span><span>1024</span><span><span>,</span><span> </span></span><span>0</span><span>);</span><span>  // 积累到1KB再触发</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 3. 使用 EV_PERSIST 避免重复添加</span></div></div><div><div><div>11</div></div><div><span>struct</span><span> event </span><span>*</span><span>ev </span><span>=</span><span> </span><span>event_new</span><span><span>(</span><span>base</span><span>,</span><span> fd</span><span>,</span><span> EV_READ </span></span><span>|</span><span><span> EV_PERSIST</span><span>,</span><span> cb</span><span>,</span><span> arg</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 4. 检查是否有大量短连接</span></div></div><div><div><div>14</div></div><div><span>// 考虑使用连接池</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>Q19: 内存占用过高<a href="#q19-内存占用过高"><span>#</span></a></h3><p><strong>问题现象：</strong>
程序内存持续增长。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 限制缓冲区大小</span></div></div><div><div><div>2</div></div><div><span>bufferevent_setwatermark</span><span><span>(</span><span>bev</span><span>,</span><span> EV_READ</span><span>,</span><span> </span></span><span>0</span><span><span>,</span><span> </span></span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>);</span><span>  // 最大1MB</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 2. 及时清理缓冲区</span></div></div><div><div><div>5</div></div><div><span>evbuffer_drain</span><span><span>(</span><span>evbuffer</span><span>,</span><span> </span></span><span>evbuffer_get_length</span><span><span>(</span><span>evbuffer</span><span>));</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 3. 限制连接数</span></div></div><div><div><div>8</div></div><div><span>if</span><span> (g_connection_count </span><span>&gt;=</span><span> MAX_CONNECTIONS) {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evutil_closesocket</span><span>(fd);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span>;</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 4. 使用内存池</span></div></div><div><div><div>14</div></div><div><span>// 参见性能调优章节</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 5. 检查内存泄漏</span></div></div><div><div><div>17</div></div><div><span>// valgrind --leak-check=full ./your_program</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>Q20: 响应延迟高<a href="#q20-响应延迟高"><span>#</span></a></h3><p><strong>问题现象：</strong>
请求处理延迟高。</p><p><strong>解决方案：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 1. 减少回调中的处理时间</span></div></div><div><div><div>2</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>// 将耗时操作放到线程池</span></div></div><div><div><div>4</div></div><div><span>    </span><span>dispatch_to_thread_pool</span><span>(process_data, bev);</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 2. 使用边缘触发（注意数据完整性）</span></div></div><div><div><div>8</div></div><div><span>// libevent默认使用水平触发</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 3. 批量处理</span></div></div><div><div><div>11</div></div><div><span>if</span><span> (</span><span>evbuffer_get_length</span><span><span>(</span><span>input</span><span>) </span></span><span>&lt;</span><span> BATCH_SIZE) {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span>;</span><span>  // 等待更多数据</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 4. 优化DNS</span></div></div><div><div><div>16</div></div><div><span>// 使用DNS缓存</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>// 5. 检查网络延迟</span></div></div><div><div><div>19</div></div><div><span>// ping, traceroute</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、调试技巧<a href="#八调试技巧"><span>#</span></a></h2><section><h3>D1: 启用详细日志<a href="#d1-启用详细日志"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>enable_debug_logging</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>event_enable_debug_mode</span><span>();</span></div></div><div><div><div>5</div></div><div><span>    </span><span>event_set_log_callback</span><span>(log_callback);</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>log_callback</span><span>(</span><span>int</span><span><span> </span><span>severity</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>msg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>level;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>switch</span><span> (severity) {</span></div></div><div><div><div>11</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_DEBUG: level </span><span>=</span><span> </span><span>"DEBUG"</span><span>; </span><span>break</span><span>;</span></div></div><div><div><div>12</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_MSG:   level </span><span>=</span><span> </span><span>"MSG"</span><span>;   </span><span>break</span><span>;</span></div></div><div><div><div>13</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_WARN:  level </span><span>=</span><span> </span><span>"WARN"</span><span>;  </span><span>break</span><span>;</span></div></div><div><div><div>14</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_ERR:   level </span><span>=</span><span> </span><span>"ERR"</span><span>;   </span><span>break</span><span>;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>default</span><span>:              level </span><span>=</span><span> </span><span>"???"</span><span>;   </span><span>break</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"[</span><span>%s</span><span>] </span><span>%s</span><span>\n</span><span>"</span><span>, level, msg);</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>D2: 使用GDB调试<a href="#d2-使用gdb调试"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译时添加调试符号</span></div></div><div><div><div>2</div></div><div><span>gcc</span><span> </span><span>-g</span><span> </span><span>-O0</span><span> </span><span>main.c</span><span> </span><span>-levent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 启动GDB</span></div></div><div><div><div>5</div></div><div><span>gdb</span><span> </span><span>./your_program</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 常用命令 break event_base_dispatch</span></div></div><div><div><div>8</div></div><div><span>(</span><span>gdb</span><span>) </span><span>break</span><span> </span><span>bufferevent_read</span></div></div><div><div><div>9</div></div><div><span>(</span><span>gdb</span><span>) </span><span>run</span></div></div><div><div><div>10</div></div><div><span>(</span><span>gdb</span><span>) </span><span>bt</span></div></div><div><div><div>11</div></div><div><span>(</span><span>gdb</span><span>) </span><span>info</span><span> </span><span>threads</span></div></div><div><div><div>12</div></div><div><span>(</span><span>gdb</span><span>) </span><span>thread</span><span> </span><span>apply</span><span> </span><span>all</span><span> </span><span>bt</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>D3: 检查事件状态<a href="#d3-检查事件状态"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>print_event_status</span><span>(</span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event fd: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>event_get_fd</span><span>(ev));</span></div></div><div><div><div>3</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event events: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>event_get_events</span><span>(ev));</span></div></div><div><div><div>4</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event pending: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>event_pending</span><span>(ev, EV_READ </span><span>|</span><span> EV_WRITE </span><span>|</span><span> EV_TIMEOUT, </span><span>NULL</span><span>));</span></div></div><div><div><div>5</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Event base: </span><span>%p</span><span>\n</span><span>"</span><span>, </span><span>event_get_base</span><span>(ev));</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>void</span><span> </span><span>print_bufferevent_status</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>printf</span><span>(</span><span>"BufferEvent fd: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>bufferevent_getfd</span><span>(bev));</span></div></div><div><div><div>10</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Input buffer: </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>           </span><span>evbuffer_get_length</span><span>(</span><span>bufferevent_get_input</span><span>(bev)));</span></div></div><div><div><div>12</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Output buffer: </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>13</div></div><div><span>           </span><span>evbuffer_get_length</span><span>(</span><span>bufferevent_get_output</span><span>(bev)));</span></div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Enabled: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>bufferevent_get_enabled</span><span>(bev));</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-http-client/</id>
      <title type="text">🌐 HTTP客户端 - libevent源码深度解析系列（二十一）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-http-client/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🌐 HTTP客户端<a href="#-http客户端"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent不仅支持HTTP服务器，还提供了完整的HTTP客户端功能。通过evhttp模块，可以方便地发送HTTP请求、处理响应，支持GET、POST等常见HTTP方法。</p></section><section><h2>二、HTTP连接对象<a href="#二http连接对象"><span>#</span></a></h2><section><h3>2.1 创建连接<a href="#21-创建连接"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建HTTP连接对象</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base      event_base</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param dns_base  DNS解析器，NULL表示阻塞解析</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param address   服务器地址</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param port      服务器端口</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>struct</span><span> evhttp_connection </span><span>*</span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> evdns_base </span><span>*</span><span><span>dns_base</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>ev_uint16_t</span><span><span> </span><span>port</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 创建HTTP连接对象（带地址族） */</span></div></div><div><div><div>12</div></div><div><span>struct</span><span> evhttp_connection </span><span>*</span><span>evhttp_connection_base_bufferevent_new</span><span>(</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> evdns_base </span><span>*</span><span><span>dns_base</span><span>,</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>ev_uint16_t</span><span><span> </span><span>port</span><span>);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 释放HTTP连接 */</span></div></div><div><div><div>17</div></div><div><span>void</span><span> </span><span>evhttp_connection_free</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>);</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>/* 设置连接超时 */</span></div></div><div><div><div>20</div></div><div><span>void</span><span> </span><span>evhttp_connection_set_timeout</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>timeout</span><span>);</span></span></div></div><div><div><div>21</div></div><div><span>void</span><span> </span><span>evhttp_connection_set_timeout_tv</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>,</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tv</span><span>);</span></span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>/* 设置重试次数 */</span></div></div><div><div><div>25</div></div><div><span>void</span><span> </span><span>evhttp_connection_set_retries</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>retry_max</span><span>);</span></span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>/* 设置最大连接数 */</span></div></div><div><div><div>28</div></div><div><span>void</span><span> </span><span>evhttp_connection_set_max_body_size</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>,</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>ev_ssize_t</span><span><span> </span><span>max_body_size</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.2 连接示例<a href="#22-连接示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 创建HTTP连接</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>base, dns_base, </span><span>"www.example.com"</span><span>, </span><span>80</span><span>);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>conn) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create connection</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>17</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 设置超时</span></div></div><div><div><div>21</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(conn, </span><span>10</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>printf</span><span>(</span><span>"HTTP connection created</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、HTTP请求对象<a href="#三http请求对象"><span>#</span></a></h2><section><h3>3.1 创建请求<a href="#31-创建请求"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建HTTP请求对象 */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evhttp_request </span><span>*</span><span>evhttp_request_new</span><span>(</span></div></div><div><div><div>3</div></div><div><span>    </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>cb</span><span>)(</span></span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 释放请求对象 */</span></div></div><div><div><div>6</div></div><div><span>void</span><span> </span><span>evhttp_request_free</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 获取请求的输出缓冲区 */</span></div></div><div><div><div>9</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>evhttp_request_get_output_buffer</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 获取请求的输入缓冲区 */</span></div></div><div><div><div>12</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>evhttp_request_get_input_buffer</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 获取请求的输出头 */</span></div></div><div><div><div>15</div></div><div><span>struct</span><span> evkeyvalq </span><span>*</span><span>evhttp_request_get_output_headers</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 获取请求的输入头 */</span></div></div><div><div><div>18</div></div><div><span>struct</span><span> evkeyvalq </span><span>*</span><span>evhttp_request_get_input_headers</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>/* 获取响应码 */</span></div></div><div><div><div>21</div></div><div><span>int</span><span> </span><span>evhttp_request_get_response_code</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>22</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evhttp_request_get_response_code_line</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>/* 获取请求的连接 */</span></div></div><div><div><div>25</div></div><div><span>struct</span><span> evhttp_connection </span><span>*</span><span>evhttp_request_get_connection</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>/* 获取请求的host */</span></div></div><div><div><div>28</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evhttp_request_get_host</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>/* 获取请求的URI */</span></div></div><div><div><div>31</div></div><div><span>enum</span><span> evhttp_cmd_type </span><span>evhttp_request_get_command</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>32</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evhttp_request_get_uri</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>/* 取消请求 */</span></div></div><div><div><div>35</div></div><div><span>void</span><span> </span><span>evhttp_cancel_request</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 请求方法<a href="#32-请求方法"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> evhttp_cmd_type {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_GET     </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_POST    </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>1</span><span>,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_HEAD    </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>2</span><span>,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_PUT     </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>3</span><span>,</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_DELETE  </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>4</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_OPTIONS </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>5</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_TRACE   </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>6</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_CONNECT </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>7</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_PATCH   </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>8</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>四、发送HTTP请求<a href="#四发送http请求"><span>#</span></a></h2><section><h3>4.1 发送请求API<a href="#41-发送请求api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 发送HTTP请求</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param evcon  HTTP连接</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param req    HTTP请求对象</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param type   请求方法</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param uri    请求URI</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* @return       0成功，-1失败</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evhttp_make_request</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>enum</span><span><span> evhttp_cmd_type </span><span>type</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>uri</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 GET请求示例<a href="#42-get请求示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_request_done</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>10</div></div><div><span>        </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>int</span><span> response_code </span><span>=</span><span> </span><span>evhttp_request_get_response_code</span><span>(req);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response code: </span><span>%d</span><span>\n</span><span>"</span><span>, response_code);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// 获取响应头</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>headers </span><span>=</span><span> </span><span>evhttp_request_get_input_headers</span><span>(req);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>content_type </span><span>=</span><span> </span><span>evhttp_find_header</span><span>(headers, </span><span>"Content-Type"</span><span>);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Content-Type: </span><span>%s</span><span>\n</span><span>"</span><span>, content_type </span><span>?</span><span> content_type </span><span>:</span><span> </span><span>"unknown"</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>// 获取响应体</span></div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evhttp_request_get_input_buffer</span><span>(req);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(buf);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response length: </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>, len);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>// 打印响应内容</span></div></div><div><div><div>28</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> </span><span>malloc</span><span>(len </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>evbuffer_copyout</span><span>(buf, data, len);</span></div></div><div><div><div>30</div></div><div><span>    </span><span>data</span><span>[len] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>31</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response body:</span><span>\n</span><span>%s</span><span>\n</span><span>"</span><span>, data);</span></div></div><div><div><div>32</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>38</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>// 创建连接</span></div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>base, dns_base, </span><span>"httpbin.org"</span><span>, </span><span>80</span><span>);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(conn, </span><span>10</span><span>);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>// 创建请求</span></div></div><div><div><div>47</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(http_request_done, base);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>// 添加请求头</span></div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>output_headers </span><span>=</span><span> </span><span>evhttp_request_get_output_headers</span><span>(req);</span></div></div><div><div><div>51</div></div><div><span>    </span><span>evhttp_add_header</span><span>(output_headers, </span><span>"Host"</span><span>, </span><span>"httpbin.org"</span><span>);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>evhttp_add_header</span><span>(output_headers, </span><span>"User-Agent"</span><span>, </span><span>"libevent-client/1.0"</span><span>);</span></div></div><div><div><div>53</div></div><div><span>    </span><span>evhttp_add_header</span><span>(output_headers, </span><span>"Accept"</span><span>, </span><span>"*/*"</span><span>);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>// 发送GET请求</span></div></div><div><div><div>56</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Sending GET request to httpbin.org/get</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>57</div></div><div><span>    </span><span>evhttp_make_request</span><span>(conn, req, EVHTTP_REQ_GET, </span><span>"/get"</span><span>);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>66</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、POST请求<a href="#五post请求"><span>#</span></a></h2><section><h3>5.1 POST请求示例<a href="#51-post请求示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_request_done</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response code: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>evhttp_request_get_response_code</span><span>(req));</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evhttp_request_get_input_buffer</span><span>(req);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(buf);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> </span><span>malloc</span><span>(len </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>evbuffer_copyout</span><span>(buf, data, len);</span></div></div><div><div><div>22</div></div><div><span>    </span><span>data</span><span>[len] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response:</span><span>\n</span><span>%s</span><span>\n</span><span>"</span><span>, data);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>31</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>base, dns_base, </span><span>"httpbin.org"</span><span>, </span><span>80</span><span>);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(conn, </span><span>10</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(http_request_done, base);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>// 设置请求头</span></div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>output_headers </span><span>=</span><span> </span><span>evhttp_request_get_output_headers</span><span>(req);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>evhttp_add_header</span><span>(output_headers, </span><span>"Host"</span><span>, </span><span>"httpbin.org"</span><span>);</span></div></div><div><div><div>42</div></div><div><span>    </span><span>evhttp_add_header</span><span>(output_headers, </span><span>"Content-Type"</span><span>, </span><span>"application/x-www-form-urlencoded"</span><span>);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>evhttp_add_header</span><span>(output_headers, </span><span>"User-Agent"</span><span>, </span><span>"libevent-client/1.0"</span><span>);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// 设置POST数据</span></div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output_buffer </span><span>=</span><span> </span><span>evhttp_request_get_output_buffer</span><span>(req);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>post_data </span><span>=</span><span> </span><span>"name=John&amp;age=30&amp;city=Beijing"</span><span>;</span></div></div><div><div><div>48</div></div><div><span>    </span><span>evbuffer_add</span><span>(output_buffer, post_data, </span><span>strlen</span><span>(post_data));</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Sending POST request to httpbin.org/post</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>51</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Post data: </span><span>%s</span><span>\n</span><span>"</span><span>, post_data);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>evhttp_make_request</span><span>(conn, req, EVHTTP_REQ_POST, </span><span>"/post"</span><span>);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>58</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>59</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>62</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 JSON POST请求<a href="#52-json-post请求"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>json_post_done</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response code: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>evhttp_request_get_response_code</span><span>(req));</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evhttp_request_get_input_buffer</span><span>(req);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(buf);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> </span><span>malloc</span><span>(len </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>evbuffer_copyout</span><span>(buf, data, len);</span></div></div><div><div><div>22</div></div><div><span>    </span><span>data</span><span>[len] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response:</span><span>\n</span><span>%s</span><span>\n</span><span>"</span><span>, data);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>31</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>base, dns_base, </span><span>"httpbin.org"</span><span>, </span><span>80</span><span>);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(conn, </span><span>10</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(json_post_done, base);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>headers </span><span>=</span><span> </span><span>evhttp_request_get_output_headers</span><span>(req);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Host"</span><span>, </span><span>"httpbin.org"</span><span>);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Content-Type"</span><span>, </span><span>"application/json"</span><span>);</span></div></div><div><div><div>42</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Accept"</span><span>, </span><span>"application/json"</span><span>);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>// JSON数据</span></div></div><div><div><div>45</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>json_data </span><span>=</span><span> </span><span>"{</span><span>\"</span><span>username</span><span>\"</span><span>:</span><span>\"</span><span>test</span><span>\"</span><span>,</span><span>\"</span><span>password</span><span>\"</span><span>:</span><span>\"</span><span>123456</span><span>\"</span><span>}"</span><span>;</span></div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>evhttp_request_get_output_buffer</span><span>(req);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>evbuffer_add</span><span>(output, json_data, </span><span>strlen</span><span>(json_data));</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Sending JSON POST request</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>50</div></div><div><span>    </span><span>printf</span><span>(</span><span>"JSON data: </span><span>%s</span><span>\n</span><span>"</span><span>, json_data);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>evhttp_make_request</span><span>(conn, req, EVHTTP_REQ_POST, </span><span>"/post"</span><span>);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>57</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>58</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、HTTP头部操作<a href="#六http头部操作"><span>#</span></a></h2><section><h3>6.1 头部API<a href="#61-头部api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加头部 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evhttp_add_header</span><span>(</span><span>struct</span><span> evkeyvalq </span><span>*</span><span><span>headers</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>key</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>value</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 查找头部 */</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evhttp_find_header</span><span>(</span><span>const</span><span> </span><span>struct</span><span> evkeyvalq </span><span>*</span><span><span>headers</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>key</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 删除头部 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evhttp_remove_header</span><span>(</span><span>struct</span><span> evkeyvalq </span><span>*</span><span><span>headers</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>key</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 清除所有头部 */</span></div></div><div><div><div>11</div></div><div><span>void</span><span> </span><span>evhttp_clear_headers</span><span>(</span><span>struct</span><span> evkeyvalq </span><span>*</span><span><span>headers</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 常用请求头<a href="#62-常用请求头"><span>#</span></a></h3>

<table><thead><tr><th>头部</th><th>说明</th></tr></thead><tbody><tr><td><code>Host</code></td><td>目标主机名</td></tr><tr><td><code>User-Agent</code></td><td>客户端标识</td></tr><tr><td><code>Accept</code></td><td>接受的内容类型</td></tr><tr><td><code>Content-Type</code></td><td>请求体类型</td></tr><tr><td><code>Content-Length</code></td><td>请求体长度</td></tr><tr><td><code>Authorization</code></td><td>认证信息</td></tr><tr><td><code>Cookie</code></td><td>Cookie数据</td></tr><tr><td><code>Connection</code></td><td>连接控制</td></tr></tbody></table></section></section><section><h2>七、HTTPS支持<a href="#七https支持"><span>#</span></a></h2><section><h3>7.1 HTTPS连接<a href="#71-https连接"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>https_request_done</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response code: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>evhttp_request_get_response_code</span><span>(req));</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evhttp_request_get_input_buffer</span><span>(req);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(buf);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> </span><span>malloc</span><span>(len </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>evbuffer_copyout</span><span>(buf, data, len);</span></div></div><div><div><div>22</div></div><div><span>    </span><span>data</span><span>[len] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Response:</span><span>\n</span><span>%s</span><span>\n</span><span>"</span><span>, data);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>// 初始化OpenSSL</span></div></div><div><div><div>31</div></div><div><span>    </span><span>SSL_library_init</span><span>();</span></div></div><div><div><div>32</div></div><div><span>    </span><span>SSL_load_error_strings</span><span>();</span></div></div><div><div><div>33</div></div><div><span>    </span><span>OpenSSL_add_all_algorithms</span><span>();</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>// 创建SSL上下文</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ssl_ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>SSLv23_client_method</span><span>());</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>// 创建bufferevent</span></div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_openssl_socket_new</span><span>(</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>base, </span><span>-</span><span>1</span><span>, </span><span>SSL_new</span><span>(ssl_ctx), BUFFEREVENT_SSL_CONNECTING,</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> BEV_OPT_DEFER_CALLBACKS);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>// 创建HTTPS连接</span></div></div><div><div><div>47</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_bufferevent_new</span><span>(</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>base, dns_base, bev, </span><span>"www.google.com"</span><span>, </span><span>443</span><span>);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(https_request_done, base);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>headers </span><span>=</span><span> </span><span>evhttp_request_get_output_headers</span><span>(req);</span></div></div><div><div><div>53</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Host"</span><span>, </span><span>"www.google.com"</span><span>);</span></div></div><div><div><div>54</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"User-Agent"</span><span>, </span><span>"libevent-https-client/1.0"</span><span>);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Sending HTTPS request to www.google.com/</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>57</div></div><div><span>    </span><span>evhttp_make_request</span><span>(conn, req, EVHTTP_REQ_GET, </span><span>"/"</span><span>);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>64</div></div><div><span>    </span><span>SSL_CTX_free</span><span>(ssl_ctx);</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>67</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、错误处理<a href="#八错误处理"><span>#</span></a></h2><section><h3>8.1 错误回调<a href="#81-错误回调"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置连接错误回调 */</span></div></div><div><div><div>2</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evhttp_conn_error_cb)(</span><span>enum</span><span> evhttp_request_error, </span><span>void</span><span> </span><span>*</span><span>);</span></div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>evhttp_connection_set_closecb</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>evcon</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>cb</span><span>)(</span></span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>cbarg</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 错误类型 */</span></div></div><div><div><div>7</div></div><div><span>enum</span><span> evhttp_request_error {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>EVREQ_HTTP_TIMEOUT,</span><span>         // 超时</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>EVREQ_HTTP_EOF,</span><span>             // 连接关闭</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>EVREQ_HTTP_INVALID_HEADER,</span><span>  // 无效头部</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>EVREQ_HTTP_BUFFER_ERROR,</span><span>    // 缓冲区错误</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>EVREQ_HTTP_REQUEST_CANCEL,</span><span>  // 请求取消</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>EVREQ_HTTP_DATA_TOO_LONG</span><span>    // 数据过长</span></div></div><div><div><div>14</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>8.2 错误处理示例<a href="#82-错误处理示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>connection_close_cb</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>conn</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connection closed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_request_done</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) {</span></div></div><div><div><div>12</div></div><div><span>        </span><span>// 获取错误类型</span></div></div><div><div><div>13</div></div><div><span>        </span><span>enum</span><span> evhttp_request_error err </span><span>=</span><span> EVREQ_HTTP_TIMEOUT;</span></div></div><div><div><div>14</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request error: </span><span>%d</span><span>\n</span><span>"</span><span>, err);</span></div></div><div><div><div>15</div></div><div><span>        </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>16</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>int</span><span> code </span><span>=</span><span> </span><span>evhttp_request_get_response_code</span><span>(req);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (code </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (code </span><span>&gt;=</span><span> </span><span>400</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>printf</span><span>(</span><span>"HTTP error: </span><span>%d</span><span>\n</span><span>"</span><span>, code);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Success: </span><span>%d</span><span>\n</span><span>"</span><span>, code);</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>event_base_loopbreak</span><span>((</span><span>struct</span><span> event_base </span><span>*</span><span>)arg);</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>base, dns_base, </span><span>"example.invalid"</span><span>, </span><span>80</span><span>);</span><span>  // 无效域名</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>// 设置关闭回调</span></div></div><div><div><div>39</div></div><div><span>    </span><span>evhttp_connection_set_closecb</span><span>(conn, connection_close_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>// 设置较短超时</span></div></div><div><div><div>42</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(conn, </span><span>3</span><span>);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(http_request_done, base);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>evhttp_make_request</span><span>(conn, req, EVHTTP_REQ_GET, </span><span>"/"</span><span>);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>51</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>九、连接池与重用<a href="#九连接池与重用"><span>#</span></a></h2><section><h3>9.1 连接重用<a href="#91-连接重用"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>int</span><span> request_count </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>const</span><span> </span><span>int</span><span> MAX_REQUESTS </span><span>=</span><span> </span><span>3</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>send_next_request</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>conn</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span>                               </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_request_done</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_request_get_connection</span><span>(req);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (req) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request </span><span>%d</span><span>: Response code </span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>20</div></div><div><span><span>               </span></span><span>request_count, </span><span>evhttp_request_get_response_code</span><span>(req));</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Request </span><span>%d</span><span> failed</span><span>\n</span><span>"</span><span>, request_count);</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>request_count</span><span>++</span><span>;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (request_count </span><span>&lt;</span><span> MAX_REQUESTS) {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>send_next_request</span><span>(conn, base);</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>30</div></div><div><span>        </span><span>evhttp_connection_free</span><span>(conn);</span></div></div><div><div><div>31</div></div><div><span>        </span><span>event_base_loopbreak</span><span>(base);</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>33</div></div><div><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>send_next_request</span><span>(</span><span>struct</span><span> evhttp_connection </span><span>*</span><span><span>conn</span><span>,</span></span></div></div><div><div><div>36</div></div><div><span>                               </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>37</div></div><div><span>    </span><span>char</span><span> </span><span>uri</span><span>[</span><span>64</span><span>];</span></div></div><div><div><div>38</div></div><div><span>    </span><span>snprintf</span><span>(uri, </span><span>sizeof</span><span>(uri), </span><span>"/get?id=</span><span>%d</span><span>"</span><span>, request_count);</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(http_request_done, base);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>headers </span><span>=</span><span> </span><span>evhttp_request_get_output_headers</span><span>(req);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Host"</span><span>, </span><span>"httpbin.org"</span><span>);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Sending request </span><span>%d</span><span> to </span><span>%s</span><span>\n</span><span>"</span><span>, request_count, uri);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>evhttp_make_request</span><span>(conn, req, EVHTTP_REQ_GET, uri);</span></div></div><div><div><div>47</div></div><div><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>51</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>// 创建一个连接，重用发送多个请求</span></div></div><div><div><div>54</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>base, dns_base, </span><span>"httpbin.org"</span><span>, </span><span>80</span><span>);</span></div></div><div><div><div>56</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(conn, </span><span>10</span><span>);</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>send_next_request</span><span>(conn, base);</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>66</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十、完整示例：HTTP下载器<a href="#十完整示例http下载器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>conn;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>FILE </span><span>*</span><span>output_file;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>url;</span></div></div><div><div><div>14</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>host;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>int</span><span> port;</span></div></div><div><div><div>16</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>path;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>size_t</span><span> total_bytes;</span></div></div><div><div><div>18</div></div><div><span>} </span><span>download_context_t</span><span>;</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>download_chunk_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>download_complete</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>download_context_t</span><span> </span><span>*</span><span>ctx </span><span>=</span><span> arg;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) {</span></div></div><div><div><div>26</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Download failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>int</span><span> code </span><span>=</span><span> </span><span>evhttp_request_get_response_code</span><span>(req);</span></div></div><div><div><div>29</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Download complete: HTTP </span><span>%d</span><span>, </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>30</div></div><div><span><span>               </span></span><span>code, </span><span>ctx</span><span>-&gt;</span><span>total_bytes</span><span>);</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>if</span><span> (</span><span>ctx</span><span>-&gt;</span><span>output_file</span><span>) {</span></div></div><div><div><div>34</div></div><div><span>        </span><span>fclose</span><span>(</span><span>ctx</span><span>-&gt;</span><span>output_file</span><span>);</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>evhttp_connection_free</span><span>(</span><span>ctx</span><span>-&gt;</span><span>conn</span><span>);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(</span><span>ctx</span><span>-&gt;</span><span>base</span><span>);</span></div></div><div><div><div>39</div></div><div><span>}</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>download_chunk_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>42</div></div><div><span>    </span><span>download_context_t</span><span> </span><span>*</span><span>ctx </span><span>=</span><span> arg;</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>req) </span><span>return</span><span>;</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evhttp_request_get_input_buffer</span><span>(req);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(buf);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>if</span><span> (len </span><span>&gt;</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span> </span><span>ctx</span><span>-&gt;</span><span>output_file</span><span>) {</span></div></div><div><div><div>50</div></div><div><span>        </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> </span><span>malloc</span><span>(len);</span></div></div><div><div><div>51</div></div><div><span>        </span><span>evbuffer_remove</span><span>(buf, data, len);</span></div></div><div><div><div>52</div></div><div><span>        </span><span>fwrite</span><span>(data, </span><span>1</span><span>, len, </span><span>ctx</span><span>-&gt;</span><span>output_file</span><span>);</span></div></div><div><div><div>53</div></div><div><span>        </span><span>free</span><span>(data);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>        </span><span>ctx</span><span>-&gt;</span><span>total_bytes</span><span> </span><span>+=</span><span> len;</span></div></div><div><div><div>56</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>\r</span><span>Downloaded: </span><span>%zu</span><span> bytes"</span><span>, </span><span>ctx</span><span>-&gt;</span><span>total_bytes</span><span>);</span></div></div><div><div><div>57</div></div><div><span>        </span><span>fflush</span><span>(stdout);</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>// 检查是否完成</span></div></div><div><div><div>61</div></div><div><span>    </span><span>if</span><span> (</span><span>evbuffer_get_length</span><span>(buf) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>62</div></div><div><span>        </span><span>download_complete</span><span>(req, arg);</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>64</div></div><div><span>}</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>start_download</span><span>(</span><span>download_context_t</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>67</div></div><div><span>    </span><span>struct</span><span> evhttp_request </span><span>*</span><span>req </span><span>=</span><span> </span><span>evhttp_request_new</span><span>(download_chunk_cb, ctx);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>struct</span><span> evkeyvalq </span><span>*</span><span>headers </span><span>=</span><span> </span><span>evhttp_request_get_output_headers</span><span>(req);</span></div></div><div><div><div>70</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Host"</span><span>, </span><span>ctx</span><span>-&gt;</span><span>host</span><span>);</span></div></div><div><div><div>71</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"User-Agent"</span><span>, </span><span>"libevent-downloader/1.0"</span><span>);</span></div></div><div><div><div>72</div></div><div><span>    </span><span>evhttp_add_header</span><span>(headers, </span><span>"Connection"</span><span>, </span><span>"close"</span><span>);</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Downloading from </span><span>%s</span><span>:</span><span>%d%s</span><span>\n</span><span>"</span><span>, </span><span>ctx</span><span>-&gt;</span><span>host</span><span>, </span><span>ctx</span><span>-&gt;</span><span>port</span><span>, </span><span>ctx</span><span>-&gt;</span><span>path</span><span>);</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>evhttp_make_request</span><span>(</span><span>ctx</span><span>-&gt;</span><span>conn</span><span>, req, EVHTTP_REQ_GET, </span><span>ctx</span><span>-&gt;</span><span>path</span><span>);</span></div></div><div><div><div>77</div></div><div><span>}</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>*</span><span>argv</span><span>[]</span><span>) {</span></div></div><div><div><div>80</div></div><div><span>    </span><span>if</span><span> (argc </span><span>&lt;</span><span> </span><span>2</span><span>) {</span></div></div><div><div><div>81</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Usage: </span><span>%s</span><span> &lt;url&gt; [output_file]</span><span>\n</span><span>"</span><span>, </span><span>argv</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>82</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Example: </span><span>%s</span><span> http://example.com/file.zip output.zip</span><span>\n</span><span>"</span><span>, </span><span>argv</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>83</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>url </span><span>=</span><span> </span><span>argv</span><span>[</span><span>1</span><span>];</span></div></div><div><div><div>87</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>output_filename </span><span>=</span><span> argc </span><span>&gt;</span><span> </span><span>2</span><span> </span><span>?</span><span> </span><span>argv</span><span>[</span><span>2</span><span>] </span><span>:</span><span> </span><span>"download.bin"</span><span>;</span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>    </span><span>// 解析URL（简化版）</span></div></div><div><div><div>90</div></div><div><span>    </span><span>char</span><span> </span><span>host</span><span>[</span><span>256</span><span>] </span><span>=</span><span> {</span><span>0</span><span>};</span></div></div><div><div><div>91</div></div><div><span>    </span><span>char</span><span> </span><span>path</span><span>[</span><span>1024</span><span>] </span><span>=</span><span> {</span><span>0</span><span>};</span></div></div><div><div><div>92</div></div><div><span>    </span><span>int</span><span> port </span><span>=</span><span> </span><span>80</span><span>;</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>host_start </span><span>=</span><span> </span><span>strstr</span><span>(url, </span><span>"://"</span><span>);</span></div></div><div><div><div>95</div></div><div><span>    </span><span>if</span><span> (host_start) {</span></div></div><div><div><div>96</div></div><div><span><span>        </span></span><span>host_start </span><span>+=</span><span> </span><span>3</span><span>;</span></div></div><div><div><div>97</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>98</div></div><div><span><span>        </span></span><span>host_start </span><span>=</span><span> url;</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>path_start </span><span>=</span><span> </span><span>strchr</span><span>(host_start, </span><span>'/'</span><span>);</span></div></div><div><div><div>102</div></div><div><span>    </span><span>if</span><span> (path_start) {</span></div></div><div><div><div>103</div></div><div><span>        </span><span>strncpy</span><span>(host, host_start, path_start </span><span>-</span><span> host_start);</span></div></div><div><div><div>104</div></div><div><span>        </span><span>strcpy</span><span>(path, path_start);</span></div></div><div><div><div>105</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>106</div></div><div><span>        </span><span>strcpy</span><span>(host, host_start);</span></div></div><div><div><div>107</div></div><div><span>        </span><span>strcpy</span><span>(path, </span><span>"/"</span><span>);</span></div></div><div><div><div>108</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>port_str </span><span>=</span><span> </span><span>strchr</span><span>(host, </span><span>':'</span><span>);</span></div></div><div><div><div>111</div></div><div><span>    </span><span>if</span><span> (port_str) {</span></div></div><div><div><div>112</div></div><div><span>        </span><span>*</span><span>port_str </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>113</div></div><div><span><span>        </span></span><span>port </span><span>=</span><span> </span><span>atoi</span><span>(port_str </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>114</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>115</div></div><div>
</div></div><div><div><div>116</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Host: </span><span>%s</span><span>, Port: </span><span>%d</span><span>, Path: </span><span>%s</span><span>\n</span><span>"</span><span>, host, port, path);</span></div></div><div><div><div>117</div></div><div>
</div></div><div><div><div>118</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>119</div></div><div><span>    </span><span>struct</span><span> evdns_base </span><span>*</span><span>dns_base </span><span>=</span><span> </span><span>evdns_base_new</span><span>(base, </span><span>1</span><span>);</span></div></div><div><div><div>120</div></div><div>
</div></div><div><div><div>121</div></div><div><span>    </span><span>download_context_t</span><span> ctx </span><span>=</span><span> {</span></div></div><div><div><div>122</div></div><div><span><span>        </span></span><span>.base </span><span>=</span><span> base,</span></div></div><div><div><div>123</div></div><div><span><span>        </span></span><span>.host </span><span>=</span><span> host,</span></div></div><div><div><div>124</div></div><div><span><span>        </span></span><span>.port </span><span>=</span><span> port,</span></div></div><div><div><div>125</div></div><div><span><span>        </span></span><span>.path </span><span>=</span><span> path,</span></div></div><div><div><div>126</div></div><div><span><span>        </span></span><span>.total_bytes </span><span>=</span><span> </span><span>0</span></div></div><div><div><div>127</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>128</div></div><div>
</div></div><div><div><div>129</div></div><div><span>    </span><span>ctx</span><span>.</span><span>output_file</span><span> </span><span>=</span><span> </span><span>fopen</span><span>(output_filename, </span><span>"wb"</span><span>);</span></div></div><div><div><div>130</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ctx</span><span>.</span><span>output_file</span><span>) {</span></div></div><div><div><div>131</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Failed to open output file: </span><span>%s</span><span>\n</span><span>"</span><span>, output_filename);</span></div></div><div><div><div>132</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>133</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>134</div></div><div>
</div></div><div><div><div>135</div></div><div><span>    </span><span>ctx</span><span>.</span><span>conn</span><span> </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(base, dns_base, host, port);</span></div></div><div><div><div>136</div></div><div><span>    </span><span>evhttp_connection_set_timeout</span><span>(</span><span>ctx</span><span>.</span><span>conn</span><span>, </span><span>30</span><span>);</span></div></div><div><div><div>137</div></div><div>
</div></div><div><div><div>138</div></div><div><span>    </span><span>start_download</span><span>(</span><span>&amp;</span><span>ctx);</span></div></div><div><div><div>139</div></div><div>
</div></div><div><div><div>140</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>141</div></div><div>
</div></div><div><div><div>142</div></div><div><span>    </span><span>evdns_base_free</span><span>(dns_base, </span><span>0</span><span>);</span></div></div><div><div><div>143</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>144</div></div><div>
</div></div><div><div><div>145</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>146</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十一、最佳实践<a href="#十一最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>连接重用</strong></td><td>同一服务器的多个请求重用连接</td></tr><tr><td><strong>超时设置</strong></td><td>根据网络环境设置合理的超时时间</td></tr><tr><td><strong>错误重试</strong></td><td>对于临时错误实现重试机制</td></tr><tr><td><strong>大文件下载</strong></td><td>使用chunk回调逐步处理，避免内存溢出</td></tr><tr><td><strong>HTTPS</strong></td><td>生产环境优先使用HTTPS</td></tr><tr><td><strong>User-Agent</strong></td><td>设置合理的User-Agent避免被拦截</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-http-server/</id>
      <title type="text">🌍 HTTP服务器 - libevent源码深度解析系列（十一）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-http-server/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🌍 HTTP服务器<a href="#-http服务器"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent提供了HTTP支持，可以方便地构建HTTP服务器和客户端。HTTP模块封装了HTTP协议的解析和响应处理，开发者只需要关注业务逻辑。</p></section><section><h2>二、创建HTTP服务器<a href="#二创建http服务器"><span>#</span></a></h2><section><h3>2.1 基本API<a href="#21-基本api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建HTTP服务器 */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evhttp </span><span>*</span><span>evhttp_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 绑定地址和端口 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evhttp_bind_socket</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>ev_uint16_t</span><span><span> </span><span>port</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 绑定socket */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evhttp_bind_socket_with_handle</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 释放HTTP服务器 */</span></div></div><div><div><div>11</div></div><div><span>void</span><span> </span><span>evhttp_free</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 请求处理<a href="#22-请求处理"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置请求处理回调 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>evhttp_set_gencb</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>,</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>cb</span><span>)(</span></span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>cbarg</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 设置特定路径的处理回调 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>evhttp_set_cb</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>path</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span>    </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>cb</span><span>)(</span></span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>cbarg</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 移除路径处理回调 */</span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>evhttp_del_cb</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>path</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、请求对象<a href="#三请求对象"><span>#</span></a></h2><section><h3>3.1 获取请求信息<a href="#31-获取请求信息"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取请求方法 */</span></div></div><div><div><div>2</div></div><div><span>enum</span><span> evhttp_cmd_type </span><span>evhttp_request_get_command</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取请求URI */</span></div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evhttp_request_get_uri</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 获取请求头 */</span></div></div><div><div><div>8</div></div><div><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>evhttp_find_header</span><span>(</span><span>const</span><span> </span><span>struct</span><span> evkeyvalq </span><span>*</span><span><span>headers</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>key</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>struct</span><span> evkeyvalq </span><span>*</span><span>evhttp_request_get_input_headers</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 获取请求体 */</span></div></div><div><div><div>12</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>evhttp_request_get_input_buffer</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 获取客户端地址 */</span></div></div><div><div><div>15</div></div><div><span>void</span><span> </span><span>evhttp_request_get_peer</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>char</span><span> </span><span>**</span><span><span>address</span><span>,</span><span> </span></span><span>ev_uint16_t</span><span> </span><span>*</span><span><span>port</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 请求方法类型<a href="#32-请求方法类型"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> evhttp_cmd_type {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_GET     </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>0</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_POST    </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>1</span><span>,</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_HEAD    </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>2</span><span>,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_PUT     </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>3</span><span>,</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_DELETE  </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>4</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_OPTIONS </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>5</span><span>,</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_TRACE   </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>6</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_CONNECT </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>7</span><span>,</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>EVHTTP_REQ_PATCH   </span><span>=</span><span> </span><span>1</span><span> </span><span>&lt;&lt;</span><span> </span><span>8</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>四、响应对象<a href="#四响应对象"><span>#</span></a></h2><section><h3>4.1 设置响应<a href="#41-设置响应"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建响应buffer */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> evbuffer </span><span>*</span><span>evbuffer_new</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>evbuffer_free</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 添加响应内容 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>evbuffer_add</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>const</span><span> </span><span>void</span><span> </span><span>*</span><span><span>data</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>datlen</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>evbuffer_add_printf</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>fmt</span><span>,</span><span> ...</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evbuffer_add_buffer</span><span>(</span><span>struct</span><span> evbuffer </span><span>*</span><span><span>dst</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>src</span><span>);</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 设置响应头 */</span></div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>evhttp_add_header</span><span>(</span><span>struct</span><span> evkeyvalq </span><span>*</span><span><span>headers</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>key</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>value</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>struct</span><span> evkeyvalq </span><span>*</span><span>evhttp_request_get_output_headers</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 设置响应状态码 */</span></div></div><div><div><div>15</div></div><div><span>void</span><span> </span><span>evhttp_send_reply</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>code</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>reason</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>16</div></div><div><span>void</span><span> </span><span>evhttp_send_reply_start</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>code</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>reason</span><span>);</span></span></div></div><div><div><div>17</div></div><div><span>void</span><span> </span><span>evhttp_send_reply_chunk</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>struct</span><span> evbuffer </span><span>*</span><span><span>buf</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span>void</span><span> </span><span>evhttp_send_reply_end</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>);</span></span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>/* 发送错误响应 */</span></div></div><div><div><div>21</div></div><div><span>void</span><span> </span><span>evhttp_send_error</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>error</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>reason</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.3 HTTP状态码<a href="#43-http状态码"><span>#</span></a></h3>

<table><thead><tr><th>状态码</th><th>说明</th></tr></thead><tbody><tr><td>HTTP_OK</td><td>200 成功</td></tr><tr><td>HTTP_NOCONTENT</td><td>204 无内容</td></tr><tr><td>HTTP_MOVEPERM</td><td>301 永久移动</td></tr><tr><td>HTTP_MOVETEMP</td><td>302 临时移动</td></tr><tr><td>HTTP_NOTMODIFIED</td><td>304 未修改</td></tr><tr><td>HTTP_BADREQUEST</td><td>400 错误请求</td></tr><tr><td>HTTP_NOTFOUND</td><td>404 未找到</td></tr><tr><td>HTTP_BADMETHOD</td><td>405 方法不允许</td></tr><tr><td>HTTP_INTERNAL</td><td>500 服务器错误</td></tr><tr><td>HTTP_SERVUNAVAIL</td><td>503 服务不可用</td></tr></tbody></table></section></section><section><h2>五、完整示例<a href="#五完整示例"><span>#</span></a></h2><section><h3>5.1 基本HTTP服务器<a href="#51-基本http服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/http_struct.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>generic_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>buf) {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>evhttp_send_error</span><span>(req, HTTP_INTERNAL, </span><span>"Failed to create buffer"</span><span>);</span></div></div><div><div><div>14</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"&lt;html&gt;&lt;body&gt;"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"&lt;h1&gt;Hello, libevent HTTP Server!&lt;/h1&gt;"</span><span>);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"&lt;p&gt;Request URI: </span><span>%s</span><span>&lt;/p&gt;"</span><span>, </span><span>evhttp_request_get_uri</span><span>(req));</span></div></div><div><div><div>20</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"&lt;/body&gt;&lt;/html&gt;"</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>evhttp_request_get_output_headers</span><span>(req), </span><span>"Content-Type"</span><span>, </span><span>"text/html"</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>evhttp_send_reply</span><span>(req, HTTP_OK, </span><span>"OK"</span><span>, buf);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>api_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>uri </span><span>=</span><span> </span><span>evhttp_request_get_uri</span><span>(req);</span></div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> evkeyvalq params;</span></div></div><div><div><div>33</div></div><div><span>    </span><span>evhttp_parse_query</span><span>(uri, </span><span>&amp;</span><span>params);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>name </span><span>=</span><span> </span><span>evhttp_find_header</span><span>(</span><span>&amp;</span><span>params, </span><span>"name"</span><span>);</span></div></div><div><div><div>36</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>age </span><span>=</span><span> </span><span>evhttp_find_header</span><span>(</span><span>&amp;</span><span>params, </span><span>"age"</span><span>);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"{</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"  </span><span>\"</span><span>status</span><span>\"</span><span>: </span><span>\"</span><span>success</span><span>\"</span><span>,</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"  </span><span>\"</span><span>name</span><span>\"</span><span>: </span><span>\"</span><span>%s</span><span>\"</span><span>,</span><span>\n</span><span>"</span><span>, name </span><span>?</span><span> name </span><span>:</span><span> </span><span>"unknown"</span><span>);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"  </span><span>\"</span><span>age</span><span>\"</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>, age </span><span>?</span><span> age </span><span>:</span><span> </span><span>"0"</span><span>);</span></div></div><div><div><div>42</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf, </span><span>"}</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>evhttp_request_get_output_headers</span><span>(req), </span><span>"Content-Type"</span><span>, </span><span>"application/json"</span><span>);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>evhttp_send_reply</span><span>(req, HTTP_OK, </span><span>"OK"</span><span>, buf);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>evhttp_clear_headers</span><span>(</span><span>&amp;</span><span>params);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>post_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>52</div></div><div><span>    </span><span>if</span><span> (</span><span>evhttp_request_get_command</span><span>(req) </span><span>!=</span><span> EVHTTP_REQ_POST) {</span></div></div><div><div><div>53</div></div><div><span>        </span><span>evhttp_send_error</span><span>(req, HTTP_BADMETHOD, </span><span>"Method not allowed"</span><span>);</span></div></div><div><div><div>54</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>evhttp_request_get_input_buffer</span><span>(req);</span></div></div><div><div><div>58</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>size_t</span><span> len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(input);</span></div></div><div><div><div>61</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>data </span><span>=</span><span> </span><span>malloc</span><span>(len </span><span>+</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>62</div></div><div><span>    </span><span>evbuffer_copyout</span><span>(input, data, len);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>data</span><span>[len] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Received POST data: </span><span>%s</span><span>\n</span><span>"</span><span>, data);</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(output, </span><span>"{</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(output, </span><span>"  </span><span>\"</span><span>received</span><span>\"</span><span>: true,</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>69</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(output, </span><span>"  </span><span>\"</span><span>length</span><span>\"</span><span>: </span><span>%zu</span><span>,</span><span>\n</span><span>"</span><span>, len);</span></div></div><div><div><div>70</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(output, </span><span>"  </span><span>\"</span><span>data</span><span>\"</span><span>: </span><span>\"</span><span>%s</span><span>\"\n</span><span>"</span><span>, data);</span></div></div><div><div><div>71</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(output, </span><span>"}</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>72</div></div><div>
</div></div><div><div><div>73</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>evhttp_request_get_output_headers</span><span>(req), </span><span>"Content-Type"</span><span>, </span><span>"application/json"</span><span>);</span></div></div><div><div><div>74</div></div><div><span>    </span><span>evhttp_send_reply</span><span>(req, HTTP_OK, </span><span>"OK"</span><span>, output);</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>free</span><span>(data);</span></div></div><div><div><div>77</div></div><div><span>    </span><span>evbuffer_free</span><span>(output);</span></div></div><div><div><div>78</div></div><div><span>}</span></div></div><div><div><div>79</div></div><div>
</div></div><div><div><div>80</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>81</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>82</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught signal, exiting...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>83</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(base);</span></div></div><div><div><div>84</div></div><div><span>}</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>**</span><span><span>argv</span><span>) {</span></span></div></div><div><div><div>87</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>88</div></div><div><span>    </span><span>struct</span><span> evhttp </span><span>*</span><span>http </span><span>=</span><span> </span><span>evhttp_new</span><span>(base);</span></div></div><div><div><div>89</div></div><div>
</div></div><div><div><div>90</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>http) {</span></div></div><div><div><div>91</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create HTTP server</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>92</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>93</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>94</div></div><div>
</div></div><div><div><div>95</div></div><div><span>    </span><span>if</span><span> (</span><span>evhttp_bind_socket</span><span>(http, </span><span>"0.0.0.0"</span><span>, </span><span>8080</span><span>) </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>96</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to bind to port 8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>97</div></div><div><span>        </span><span>evhttp_free</span><span>(http);</span></div></div><div><div><div>98</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>// 设置路由</span></div></div><div><div><div>102</div></div><div><span>    </span><span>evhttp_set_cb</span><span>(http, </span><span>"/api"</span><span>, api_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>103</div></div><div><span>    </span><span>evhttp_set_cb</span><span>(http, </span><span>"/post"</span><span>, post_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>104</div></div><div><span>    </span><span>evhttp_set_gencb</span><span>(http, generic_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>105</div></div><div>
</div></div><div><div><div>106</div></div><div><span>    </span><span>// 信号处理</span></div></div><div><div><div>107</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigint </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, base);</span></div></div><div><div><div>108</div></div><div><span>    </span><span>event_add</span><span>(sigint, </span><span>NULL</span><span>);</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span>    </span><span>printf</span><span>(</span><span>"HTTP server running on http://0.0.0.0:8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>111</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Routes:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>112</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  GET  /           - Hello page</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>113</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  GET  /api?name=xxx&amp;age=xxx - API endpoint</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>114</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  POST /post       - POST handler</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>115</div></div><div>
</div></div><div><div><div>116</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>117</div></div><div>
</div></div><div><div><div>118</div></div><div><span>    </span><span>event_free</span><span>(sigint);</span></div></div><div><div><div>119</div></div><div><span>    </span><span>evhttp_free</span><span>(http);</span></div></div><div><div><div>120</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>121</div></div><div>
</div></div><div><div><div>122</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>123</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>124</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 静态文件服务器<a href="#52-静态文件服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;sys/stat.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>WWW_ROOT</span><span> </span><span>"./www"</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>static</span><span> </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>get_content_type</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>path</span><span>) {</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>ext </span><span>=</span><span> </span><span>strrchr</span><span>(path, </span><span>'.'</span><span>);</span></div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ext) </span><span>return</span><span> </span><span>"application/octet-stream"</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".html"</span><span>) </span><span>==</span><span> </span><span>0</span><span> </span><span>||</span><span> </span><span>strcmp</span><span>(ext, </span><span>".htm"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"text/html"</span><span>;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".css"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"text/css"</span><span>;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".js"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"application/javascript"</span><span>;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".json"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"application/json"</span><span>;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".png"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"image/png"</span><span>;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".jpg"</span><span>) </span><span>==</span><span> </span><span>0</span><span> </span><span>||</span><span> </span><span>strcmp</span><span>(ext, </span><span>".jpeg"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"image/jpeg"</span><span>;</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".gif"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"image/gif"</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span> (</span><span>strcmp</span><span>(ext, </span><span>".txt"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) </span><span>return</span><span> </span><span>"text/plain"</span><span>;</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>return</span><span> </span><span>"application/octet-stream"</span><span>;</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>file_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>uri </span><span>=</span><span> </span><span>evhttp_request_get_uri</span><span>(req);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>char</span><span> </span><span>path</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>32</div></div><div><span>    </span><span>snprintf</span><span>(path, </span><span>sizeof</span><span>(path), </span><span>"</span><span>%s%s</span><span>"</span><span>, WWW_ROOT, uri);</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>// 处理目录请求</span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> stat st;</span></div></div><div><div><div>36</div></div><div><span>    </span><span>if</span><span> (</span><span>stat</span><span>(path, </span><span>&amp;</span><span>st) </span><span>==</span><span> </span><span>0</span><span> </span><span>&amp;&amp;</span><span> </span><span>S_ISDIR</span><span>(</span><span>st</span><span>.</span><span>st_mode</span><span>)) {</span></div></div><div><div><div>37</div></div><div><span>        </span><span>strcat</span><span>(path, </span><span>"/index.html"</span><span>);</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>FILE </span><span>*</span><span>fp </span><span>=</span><span> </span><span>fopen</span><span>(path, </span><span>"rb"</span><span>);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>fp) {</span></div></div><div><div><div>42</div></div><div><span>        </span><span>evhttp_send_error</span><span>(req, HTTP_NOTFOUND, </span><span>"File not found"</span><span>);</span></div></div><div><div><div>43</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>fseek</span><span>(fp, </span><span>0</span><span>, SEEK_END);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>long</span><span> size </span><span>=</span><span> </span><span>ftell</span><span>(fp);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>fseek</span><span>(fp, </span><span>0</span><span>, SEEK_SET);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>51</div></div><div><span>    </span><span>evbuffer_add_file</span><span>(buf, </span><span>fileno</span><span>(fp), </span><span>0</span><span>, size);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>evhttp_request_get_output_headers</span><span>(req),</span></div></div><div><div><div>54</div></div><div><span>                      </span><span>"Content-Type"</span><span>, </span><span>get_content_type</span><span>(path));</span></div></div><div><div><div>55</div></div><div><span>    </span><span>evhttp_send_reply</span><span>(req, HTTP_OK, </span><span>"OK"</span><span>, buf);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>58</div></div><div><span>}</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>61</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>62</div></div><div><span>    </span><span>struct</span><span> evhttp </span><span>*</span><span>http </span><span>=</span><span> </span><span>evhttp_new</span><span>(base);</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>evhttp_bind_socket</span><span>(http, </span><span>"0.0.0.0"</span><span>, </span><span>8080</span><span>);</span></div></div><div><div><div>65</div></div><div><span>    </span><span>evhttp_set_gencb</span><span>(http, file_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Static file server running on http://0.0.0.0:8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Serving files from </span><span>%s</span><span>\n</span><span>"</span><span>, WWW_ROOT);</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>    </span><span>evhttp_free</span><span>(http);</span></div></div><div><div><div>73</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>74</div></div><div>
</div></div><div><div><div>75</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>76</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、编译运行<a href="#六编译运行"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译</span></div></div><div><div><div>2</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>http_server</span><span> </span><span>http_server.c</span><span> </span><span>-levent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 运行</span></div></div><div><div><div>5</div></div><div><span>./http_server</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 测试</span></div></div><div><div><div>8</div></div><div><span>curl</span><span> </span><span>http://127.0.0.1:8080/</span></div></div><div><div><div>9</div></div><div><span>curl</span><span> </span><span>http://127.0.0.1:8080/api?name=test</span><span>&amp;</span><span>age</span><span>=</span><span>20</span></div></div><div><div><div>10</div></div><div><span>curl</span><span> </span><span>-X</span><span> </span><span>POST</span><span> </span><span>-d</span><span> </span><span>"hello"</span><span> </span><span>http://127.0.0.1:8080/post</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-installation/</id>
      <title type="text">🛠️ 安装与编译 - libevent源码深度解析系列（二）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-installation/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🛠️ 安装与编译<a href="#️-安装与编译"><span>#</span></a></h1><section><h2>一、下载源码<a href="#一下载源码"><span>#</span></a></h2><p>进入 <a href="https://libevent.org/" target="_blank">libevent 官方网站</a> 下载最新稳定版本。</p><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/4087a823bdd772afac17769229c4609b.png" alt="下载页面" /><figcaption>下载页面</figcaption></figure><p></p><p>这里选择最新版本 <a href="https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz" target="_blank">libevent-2.1.12-stable.tar.gz</a> 下载。下载后传入到linux服务器中。</p></section><section><h2>二、编译脚本<a href="#二编译脚本"><span>#</span></a></h2><p>直接编写一个shell编译脚本进行编译，方便编译参数设置。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/bin/bash</span></div></div><div><div><div>2</div></div><div><span># 获取当前目录</span></div></div><div><div><div>3</div></div><div><span>cur_dir</span><span>=</span><span>`</span><span>pwd</span><span>`</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 解压libevent</span></div></div><div><div><div>6</div></div><div><span>tar</span><span> </span><span>-xzf</span><span> </span><span>libevent-2.1.12-stable.tar.gz</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 进入源码目录</span></div></div><div><div><div>9</div></div><div><span>cd</span><span> </span><span>libevent-2.1.12-stable</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># 生成configure文件</span></div></div><div><div><div>12</div></div><div><span>./autogen.sh</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 配置编译选项</span></div></div><div><div><div>15</div></div><div><span># --prefix=$cur_dir/libevent_build 设置编译安装路径</span></div></div><div><div><div>16</div></div><div><span># --enable-shared=no 不编译动态库</span></div></div><div><div><div>17</div></div><div><span># --enable-static=yes 编译静态库</span></div></div><div><div><div>18</div></div><div><span># --disable-openssl 不启用openssl</span></div></div><div><div><div>19</div></div><div><span>./configure</span><span> </span><span>--prefix=</span><span>$cur_dir</span><span>/libevent_build</span><span> </span><span>\</span></div></div><div><div><div>20</div></div><div><span>            </span><span>--enable-shared=no</span><span> </span><span>\</span></div></div><div><div><div>21</div></div><div><span>            </span><span>--enable-static=yes</span><span> </span><span>\</span></div></div><div><div><div>22</div></div><div><span>            </span><span>--disable-openssl</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span># 编译安装</span></div></div><div><div><div>25</div></div><div><span>make</span><span> </span><span>-j</span></div></div><div><div><div>26</div></div><div><span>make</span><span> </span><span>install</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>三、执行编译<a href="#三执行编译"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>build.sh</span></div></div><div><div><div>2</div></div><div><span>./build.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>等待编译完成后，可以看到当前路径中内容如下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ls</span><span> </span><span>-hlt</span></div></div><div><div><div>2</div></div><div><span>total</span><span> </span><span>1.1M</span></div></div><div><div><div>3</div></div><div><span>drwxr-xr-x</span><span>  </span><span>5</span><span> </span><span>xutopia</span><span> </span><span>xutopia</span><span> </span><span>4.0K</span><span> </span><span>Sep</span><span>  </span><span>6</span><span> </span><span>22:50</span><span> </span><span>libevent_build</span></div></div><div><div><div>4</div></div><div><span>drwxr-xr-x</span><span> </span><span>13</span><span> </span><span>xutopia</span><span> </span><span>xutopia</span><span> </span><span>4.0K</span><span> </span><span>Sep</span><span>  </span><span>6</span><span> </span><span>22:50</span><span> </span><span>libevent-2.1.12-stable</span></div></div><div><div><div>5</div></div><div><span>-rwxr-xr-x</span><span>  </span><span>1</span><span> </span><span>xutopia</span><span> </span><span>xutopia</span><span>  </span><span>558</span><span> </span><span>Sep</span><span>  </span><span>6</span><span> </span><span>22:50</span><span> </span><span>build.sh</span></div></div><div><div><div>6</div></div><div><span>-rw-r--r--r--</span><span>  </span><span>1</span><span> </span><span>xutopia</span><span> </span><span>xutopia</span><span> </span><span>1.1M</span><span> </span><span>Sep</span><span>  </span><span>6</span><span> </span><span>22:41</span><span> </span><span>libevent-2.1.12-stable.tar.gz</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>四、程序验证<a href="#四程序验证"><span>#</span></a></h2><p>编译完成后，可以通过编译一个简单的示例程序来验证 <code>libevent</code> 是否正确安装。</p><section><h3>4.1 测试代码<a href="#41-测试代码"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* </span><span>@brief</span><span> 测试验证libevent程序</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>*          编译测试程序</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*          gcc main.c -I./libevent_build/include -o main -L./libevent_build/lib -levent</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_callback</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>event</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer run!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer_callback, base);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>timer) {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create timer event</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>28</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>29</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>// 设置定时器时间为 2 秒</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> timeval delay </span><span>=</span><span> {</span><span>2</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>34</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>delay);</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>// 启动事件循环</span></div></div><div><div><div>37</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>// 清理资源</span></div></div><div><div><div>40</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>44</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 编译命令<a href="#42-编译命令"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>gcc</span><span> </span><span>main.c</span><span> </span><span>-I./libevent_build/include</span><span> </span><span>-o</span><span> </span><span>main</span><span> </span><span>-L./libevent_build/lib</span><span> </span><span>-levent</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.3 运行结果<a href="#43-运行结果"><span>#</span></a></h3><div><figure><figcaption><span>./main</span></figcaption><pre><code><div><div><div>1</div></div><div><span>Timer</span><span> </span><span>run!</span></div></div><div><div><div>2</div></div><div><span>Timer</span><span> </span><span>run!</span></div></div><div><div><div>3</div></div><div><span>Timer</span><span> </span><span>run!</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>五、编译产物说明<a href="#五编译产物说明"><span>#</span></a></h2>

<table><thead><tr><th>库文件</th><th>说明</th></tr></thead><tbody><tr><td><code>libevent_core</code></td><td>所有核心的事件和缓冲功能，包含了所有的 event_base、evbuffer、bufferevent和工具函数</td></tr><tr><td><code>libevent_extra</code></td><td>定义了程序可能需要，也可能不需要的协议特定功能，包括 HTTP、DNS和 RPC</td></tr><tr><td><code>libevent</code></td><td>这个库因为历史原因而存在，它包含 libevent_core和 libevent_extra的内容</td></tr><tr><td><code>libevent_pthreads</code></td><td>添加基于 pthread可移植线程库的线程和锁定实现</td></tr><tr><td><code>libevent_openssl</code></td><td>为使用 bufferevent和 OpenSSL进行加密的通信提供支持</td></tr></tbody></table><hr /></section><section><h2>六、Windows平台安装<a href="#六windows平台安装"><span>#</span></a></h2><section><h3>6.1 使用vcpkg安装<a href="#61-使用vcpkg安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装vcpkg（如果尚未安装）</span></div></div><div><div><div>2</div></div><div><span><span>git clone https:</span><span>//</span></span><span>github.com</span><span><span>/</span><span>Microsoft</span><span>/</span><span>vcpkg.git</span></span></div></div><div><div><div>3</div></div><div><span>cd vcpkg</span></div></div><div><div><div>4</div></div><div><span>.\</span><span>bootstrap-vcpkg.bat</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 安装libevent</span></div></div><div><div><div>7</div></div><div><span><span>.\vcpkg install libevent:x64</span><span>-</span><span>windows</span></span></div></div><div><div><div>8</div></div><div><span>.\vcpkg integrate install</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 使用CMake编译<a href="#62-使用cmake编译"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 下载源码</span></div></div><div><div><div>2</div></div><div><span><span>git clone https:</span><span>//</span></span><span>github.com</span><span><span>/</span><span>libevent</span><span>/</span><span>libevent.git</span></span></div></div><div><div><div>3</div></div><div><span>cd libevent</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 创建构建目录</span></div></div><div><div><div>6</div></div><div><span>mkdir build &amp;&amp; cd build</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 配置和编译</span></div></div><div><div><div>9</div></div><div><span><span>cmake .. </span><span>-</span><span>G </span></span><span>"Visual Studio 17 2022"</span><span><span> </span><span>-</span><span>A x64</span></span></div></div><div><div><div>10</div></div><div><span><span>cmake </span><span>--</span><span>build . </span><span>--</span><span>config Release</span></span></div></div><div><div><div>11</div></div><div><span><span>cmake </span><span>--</span><span>install . </span><span>--</span><span>prefix .</span><span>/</span><span>install</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.3 Visual Studio配置<a href="#63-visual-studio配置"><span>#</span></a></h3><p>在Visual Studio项目中配置libevent：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- 包含目录 --&gt;</span></div></div><div><div><div>2</div></div><div><span>C:\libevent\include</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>&lt;!-- 库目录 --&gt;</span></div></div><div><div><div>5</div></div><div><span>C:\libevent\lib</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;!-- 链接库 --&gt;</span></div></div><div><div><div>8</div></div><div><span>libevent.lib</span></div></div><div><div><div>9</div></div><div><span>ws2_32.lib</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.4 Windows测试代码<a href="#64-windows测试代码"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;winsock2.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;ws2tcpip.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#pragma</span><span> </span><span>comment</span><span>(</span><span>lib</span><span>, </span><span>"ws2_32.lib"</span><span>)</span></div></div><div><div><div>5</div></div><div><span>#endif</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>WSADATA wsa_data;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>WSAStartup</span><span>(</span><span>MAKEWORD</span><span>(</span><span>2</span><span>, </span><span>2</span><span>), </span><span>&amp;</span><span>wsa_data);</span></div></div><div><div><div>14</div></div><div><span>#endif</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>17</div></div><div><span>    </span><span>if</span><span> (base) {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>printf</span><span>(</span><span>"libevent initialized successfully!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>19</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Using method: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>event_base_get_method</span><span>(base));</span></div></div><div><div><div>20</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>24</div></div><div><span>    </span><span>WSACleanup</span><span>();</span></div></div><div><div><div>25</div></div><div><span>#endif</span></div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>七、macOS平台安装<a href="#七macos平台安装"><span>#</span></a></h2><section><h3>7.1 使用Homebrew安装<a href="#71-使用homebrew安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装libevent</span></div></div><div><div><div>2</div></div><div><span>brew</span><span> </span><span>install</span><span> </span><span>libevent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 查看安装信息</span></div></div><div><div><div>5</div></div><div><span>brew</span><span> </span><span>info</span><span> </span><span>libevent</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>7.2 使用MacPorts安装<a href="#72-使用macports安装"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>port</span><span> </span><span>install</span><span> </span><span>libevent</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>7.3 源码编译（macOS）<a href="#73-源码编译macos"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 下载源码</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-O</span><span> </span><span>https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz</span></div></div><div><div><div>3</div></div><div><span>tar</span><span> </span><span>-xzf</span><span> </span><span>libevent-2.1.12-stable.tar.gz</span></div></div><div><div><div>4</div></div><div><span>cd</span><span> </span><span>libevent-2.1.12-stable</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># 配置和编译</span></div></div><div><div><div>7</div></div><div><span>./configure</span><span> </span><span>--prefix=/usr/local/libevent</span></div></div><div><div><div>8</div></div><div><span>make</span><span> </span><span>-j$(</span><span>sysctl</span><span> -n </span><span>hw.ncpu</span><span>)</span></div></div><div><div><div>9</div></div><div><span>sudo</span><span> </span><span>make</span><span> </span><span>install</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>7.4 macOS编译测试程序<a href="#74-macos编译测试程序"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>gcc</span><span> </span><span>main.c</span><span> </span><span>-I/usr/local/include</span><span> </span><span>-L/usr/local/lib</span><span> </span><span>-levent</span><span> </span><span>-o</span><span> </span><span>main</span></div></div><div><div><div>2</div></div><div><span>./main</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>八、常见安装问题<a href="#八常见安装问题"><span>#</span></a></h2>

<table><thead><tr><th>问题</th><th>解决方案</th></tr></thead><tbody><tr><td><code>openssl/ssl.h not found</code></td><td>安装OpenSSL开发包：<code>apt install libssl-dev</code></td></tr><tr><td><code>event2/event.h not found</code></td><td>检查include路径是否正确配置</td></tr><tr><td><code>undefined reference to event_base_new</code></td><td>添加链接选项 <code>-levent</code></td></tr><tr><td>Windows下WSA错误</td><td>确保调用 <code>WSAStartup()</code> 初始化</td></tr><tr><td>macOS下找不到库</td><td>设置 <code>DYLD_LIBRARY_PATH</code> 或使用 <code>install_name_tool</code></td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-introduction/</id>
      <title type="text">📖 序言与简介 - libevent源码深度解析系列（一）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-introduction/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🚀 序言与简介<a href="#-序言与简介"><span>#</span></a></h1><section><h2>一、高性能服务器编程<a href="#一高性能服务器编程"><span>#</span></a></h2><section><h3>1.1 高性能服务器特点<a href="#11-高性能服务器特点"><span>#</span></a></h3><p>目前网络应用程序大多是C/S架构的，例如：</p><ul>
<li>🍪 日常使用的邮箱，聊天软件QQ、微信</li>
<li>🍪 看视频的网站B站、腾讯视频</li>
<li>🍪 浏览网页用的浏览器</li>
</ul><p>他们的基本特点就是，客户端软件从向服务端请求资源，服务端做一些处理之后，把资源返回给客户端。</p><p>高性能服务器的关键需求至少有如下几点：</p><ul>
<li>👉 <strong>效率高</strong>：既然是高性能，那处理客户端请求的效率当然要很高了</li>
<li>👉 <strong>高可用</strong>：不能随便就挂掉了</li>
<li>👉 <strong>编程简单</strong>：基于此服务器进行业务开发需要足够简单</li>
<li>👉 <strong>可扩展</strong>：可方便的扩展功能（微服务）</li>
<li>👉 <strong>可伸缩</strong>：可简单的通过部署的方式进行容量的伸缩，也就是服务需要无状态</li>
</ul></section><section><h3>1.2 传统网络编程的局限<a href="#12-传统网络编程的局限"><span>#</span></a></h3><p>从上面描述也可以知道，高性能服务器的重要基石就是接受客户端的连接，处理客户端发送的网络请求。下面我们看一下基本的网络编程方式。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>while</span><span>(</span><span>1</span><span>){</span></div></div><div><div><div>2</div></div><div><span>    </span><span>listen</span><span>(listen_socket);</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>cli_socket </span><span>=</span><span> </span><span>accept</span><span>(listen_socket);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>while</span><span>(</span><span>read</span><span>(cli_socket) </span><span>&gt;</span><span> </span><span>0</span><span>){</span></div></div><div><div><div>5</div></div><div><span>        </span><span>//do something, cost some time</span></div></div><div><div><div>6</div></div><div><span>        </span><span>write</span><span>(cli_socket);</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div><span>    </span><span>//peer close or peer err</span></div></div><div><div><div>9</div></div><div><span>    </span><span>close</span><span>(cli_socket)</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>服务端监听一个网络套接字，然后接受客户端的连接请求，并从客户端读取请求数据，然后做出响应。</p><p>上面程序中，accept和read的系统接口一般都是阻塞的，所以上面的编程示例同一时刻只能处理一个客户端的请求。但是我们知道，这在现实中基本还是不可使用的，例如B站，同一时刻不可能只为一个网络用户提供视频服务。</p><p><strong>改进方案</strong>：每一个客户端连接上来就为他分配一个线程，处理这个客户的请求，然后主线程继续等待客户端的连接。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>workThread</span><span>(</span><span>int</span><span><span> </span><span>cli_socket</span><span>){</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>while</span><span>(</span><span>read</span><span>(cli_socket) </span><span>&gt;</span><span> </span><span>0</span><span>){</span></div></div><div><div><div>3</div></div><div><span>        </span><span>//do something, cost some time</span></div></div><div><div><div>4</div></div><div><span>        </span><span>write</span><span>(cli_socket);</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>    </span><span>close</span><span>(cli_socket)</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>main</span><span>(){</span></div></div><div><div><div>10</div></div><div><span>    </span><span>while</span><span>(</span><span>1</span><span>){</span></div></div><div><div><div>11</div></div><div><span>        </span><span>listen</span><span>(listen_socket);</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>cli_socket </span><span>=</span><span> </span><span>accept</span><span>(listen_socket);</span></div></div><div><div><div>13</div></div><div><span>        </span><span>create_thread</span><span>(workThread, listen_socket);</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>问题</strong>：每一个客户端就需要一个线程，如果面对大量用户，那就需要创建大量线程，可是服务器的硬件资源是有限制的。具体问题详见 <a href="http://www.kegel.com/c10k.html" target="_blank">The C10K Problem</a>。</p><p>针对这个问题可以使用linux的多路复用机制来解决。</p><hr /></section></section><section><h2>二、Reactor模型<a href="#二reactor模型"><span>#</span></a></h2><p>从上述的demo中可以看出，在wait的地方阻塞等待，直到有事件触发，然后就可以调用事件的回调进行处理。这就是Reactor网络编程模型。</p><section><h3>2.1 Reactor模型结构<a href="#21-reactor模型结构"><span>#</span></a></h3><p>下面这个模型图片在计算机业内的公认讲Reactor模型最好的论文。</p><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/83b659e4df8e53e7d13fa403a31da6b9.png" alt="Reactor模型" /><figcaption>Reactor模型</figcaption></figure><p></p><p>简单解释一下这个模型图：</p>

<table><thead><tr><th>组件</th><th>说明</th></tr></thead><tbody><tr><td><strong>Synchronous Event Demultiplexer</strong></td><td>同步事件分离器，用于监听各种事件，调用方调用监听方法的时候会被阻塞，直到有事件发生，才会返回。对于Linux来说，同步事件分离器指的就是IO多路复用模型，比如epoll，poll，select 等。</td></tr><tr><td><strong>Handler</strong></td><td>本质上是文件描述符，是一个抽象的概念，可以简单的理解为一个一个事件，该事件可以来自于外部，比如客户端连接事件，客户端的写事件等等，也可以是内部的事件，比如操作系统产生的定时器事件等等。</td></tr><tr><td><strong>Event Handler</strong></td><td>事件处理器，本质上是回调方法，当有事件发生后，框架会根据Handler调用对应的回调方法，在大多数情况下，是虚函数，需要用户自己实现接口，实现具体的方法。</td></tr><tr><td><strong>Concrete Event Handler</strong></td><td>具体的事件处理器，是Event Handler的具体实现。</td></tr><tr><td><strong>Initiation Dispatcher</strong></td><td>初始化事件分发器，实际上就是Reactor角色，提供了一系列方法，对Event Handler进行注册和移除；还会调用Synchronous Event Demultiplexer监听各种事件；当有事件发生后，还要调用对应的Event Handler。</td></tr></tbody></table></section><section><h3>2.2 简化的Reactor模型图<a href="#22-简化的reactor模型图"><span>#</span></a></h3><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/63b27530b79ffb5ea1197b3fb8de1c4c.png" alt="简化Reactor模型" /><figcaption>简化Reactor模型</figcaption></figure><p></p><p>这是一个最简单的Reactor网络模型，Reactor处理多个client的连接处理请求，Reactor模型负责把产生的事件（连接，可读，可写，异常等）通过dispatch分发给各回调函数进行处理。</p><p><strong>注意</strong>：上述是单线程的Reactor处理模型，单线程的Reactor要求每个处理回调不能卡死，业务处理尽可能快，否则会影响后续的事件处理，导致事件队列阻塞。</p><hr /></section></section><section><h2>三、libevent简介<a href="#三libevent简介"><span>#</span></a></h2><section><h3>3.1 libevent特点<a href="#31-libevent特点"><span>#</span></a></h3><p>Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库，主要有以下几个亮点：</p><ul>
<li>🔷 <strong>事件驱动（event-driven）</strong>，高性能</li>
<li>🔷 <strong>轻量级</strong>，专注于网络，不如 ACE 那么臃肿庞大</li>
<li>🔷 <strong>源代码相当精炼、易读</strong></li>
<li>🔷 <strong>跨平台</strong>，支持 Windows、Linux、*BSD 和 Mac Os</li>
<li>🔷 <strong>支持多种 I/O 多路复用技术</strong>，epoll、poll、dev/poll、select 和 kqueue 等</li>
<li>🔷 <strong>支持 I/O，定时器和信号等事件</strong></li>
<li>🔷 <strong>注册事件优先级</strong></li>
</ul><p>Libevent 已经被广泛的应用，作为底层的网络库；比如 memcached、Vomit、Nylon、Netchat等等。</p></section><section><h3>3.2 libevent组件<a href="#32-libevent组件"><span>#</span></a></h3><p>libevent包括事件管理、缓存管理、DNS、HTTP、缓存事件几大部分：</p>

<table><thead><tr><th>组件</th><th>说明</th></tr></thead><tbody><tr><td><strong>事件管理</strong></td><td>包括各种IO（socket）、定时器、信号等事件</td></tr><tr><td><strong>缓存管理</strong></td><td>evbuffer功能</td></tr><tr><td><strong>DNS</strong></td><td>libevent提供的一个异步DNS查询功能</td></tr><tr><td><strong>HTTP</strong></td><td>libevent的一个轻量级http实现，包括服务器和客户端</td></tr></tbody></table><p>libevent也支持ssl，这对于有安全需求的网络程序非常的重要，但是其支持不是很完善，比如http server的实现就不支持ssl。</p></section><section><h3>3.3 libevent的历史背景<a href="#33-libevent的历史背景"><span>#</span></a></h3><p><code>libevent</code> 的起源可以追溯到 2003 年，由美国加州大学伯克利分校的研究员 Brad Fitzpatrick 开发。最初，它是为了解决在构建高性能网络应用时面临的挑战，特别是在 Linux 和 BSD 系统上实现高效的事件处理机制。Fitzpatrick 在开发 LiveJournal 项目的过程中遇到了性能瓶颈，传统的同步 I/O 模型无法满足大规模并发访问的需求。因此，他设计并实现了 <code>libevent</code>，以提供一种更为灵活和高效的事件处理框架。</p><p>最初的 <code>libevent</code> 实现基于 <code>select()</code> 系统调用，但很快就转向了更为先进的 <code>poll()</code> 和 <code>kqueue()</code>（针对 BSD 系统）。随着 Linux 内核的发展，特别是引入了 <code>epoll</code> 机制之后，<code>libevent</code> 迅速采用了这一新技术，进一步提升了性能。</p></section><section><h3>3.4 libevent的API基本使用步骤<a href="#34-libevent的api基本使用步骤"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event_base_new</span><span>()</span><span>    // 创建初始化event_base</span></div></div><div><div><div>2</div></div><div><span>event_new</span><span>()</span><span>         // 创建事件</span></div></div><div><div><div>3</div></div><div><span>event_add</span><span>()</span><span>         // 将event添加到事件链表上，注册事件</span></div></div><div><div><div>4</div></div><div><span>event_base_dispatch</span><span>()</span><span> // 循环、检测、分发事件</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section><section><h2>四、事件驱动编程<a href="#四事件驱动编程"><span>#</span></a></h2><section><h3>4.1 事件驱动编程概述<a href="#41-事件驱动编程概述"><span>#</span></a></h3><p>事件驱动编程（Event-Driven Programming，EDP）是一种编程范式，因其高效率和灵活性而受到广泛关注，它强调程序的执行是由外部事件触发的，而非通过预定的顺序流程控制。</p><p>事件驱动编程的核心思想是”被动等待”和”主动响应”。程序在大部分时间内处于等待状态，一旦检测到某个感兴趣的事件发生，就会触发相应的处理逻辑。这种方法不仅减少了不必要的计算开销，还能显著提高系统的响应速度和整体性能。</p></section><section><h3>4.2 事件驱动编程的优势<a href="#42-事件驱动编程的优势"><span>#</span></a></h3><ol>
<li><strong>高效性</strong>：事件驱动编程模型允许程序在没有事件发生时处于休眠状态，这大大减少了 CPU 的空闲时间，提高了系统的整体效率。</li>
<li><strong>可扩展性</strong>：由于事件驱动模型可以同时处理多个并发事件，因此非常适合构建高并发的应用程序。</li>
<li><strong>灵活性</strong>：事件驱动编程模型允许开发者更灵活地组织代码结构，通过注册不同的事件处理函数来实现模块化的设计。</li>
<li><strong>异步处理</strong>：事件驱动模型天然支持异步处理方式，这意味着程序可以同时处理多个任务，无需等待某个操作完成后再继续执行后续逻辑。</li>
</ol><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-logging/</id>
      <title type="text">📝 日志模块 - libevent源码深度解析系列（八）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-logging/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📝 日志模块<a href="#-日志模块"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent提供了日志功能，允许用户自定义日志输出方式。默认情况下，日志输出到标准错误输出（stderr）。</p></section><section><h2>二、日志级别<a href="#二日志级别"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>EVENT_LOG_DEBUG</span><span>  </span><span>0</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>EVENT_LOG_MSG</span><span>    </span><span>1</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>EVENT_LOG_WARN</span><span>   </span><span>2</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>EVENT_LOG_ERR</span><span>    </span><span>3</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>三、日志回调函数<a href="#三日志回调函数"><span>#</span></a></h2><section><h3>3.1 回调函数类型<a href="#31-回调函数类型"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>event_log_cb)(</span><span>int</span><span> severity, </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 设置日志回调<a href="#32-设置日志回调"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置日志回调函数，设置为NULL恢复默认行为 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>event_set_log_callback</span><span><span>(</span><span>event_log_cb </span><span>cb</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.3 示例<a href="#33-示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>my_log_cb</span><span>(</span><span>int</span><span><span> </span><span>severity</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>msg</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>level;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>switch</span><span> (severity) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_DEBUG: level </span><span>=</span><span> </span><span>"DEBUG"</span><span>; </span><span>break</span><span>;</span></div></div><div><div><div>8</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_MSG:   level </span><span>=</span><span> </span><span>"MSG"</span><span>;   </span><span>break</span><span>;</span></div></div><div><div><div>9</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_WARN:  level </span><span>=</span><span> </span><span>"WARN"</span><span>;  </span><span>break</span><span>;</span></div></div><div><div><div>10</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_ERR:   level </span><span>=</span><span> </span><span>"ERR"</span><span>;   </span><span>break</span><span>;</span></div></div><div><div><div>11</div></div><div><span>        </span><span>default</span><span>:              level </span><span>=</span><span> </span><span>"???"</span><span>;   </span><span>break</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"[libevent][</span><span>%s</span><span>] </span><span>%s</span><span>\n</span><span>"</span><span>, level, msg);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>event_set_log_callback</span><span>(my_log_cb);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>// ... 其他代码</span></div></div><div><div><div>19</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、致命错误处理<a href="#四致命错误处理"><span>#</span></a></h2><section><h3>4.1 设置致命错误回调<a href="#41-设置致命错误回调"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>event_fatal_cb)(</span><span>int</span><span> err);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>/* 设置致命错误回调函数</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* 设置后，libevent遇到内部错误时调用此回调，而不是退出</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* 注意：回调函数不应该返回</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>void</span><span> </span><span>event_set_fatal_callback</span><span><span>(</span><span>event_fatal_cb </span><span>cb</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 示例<a href="#42-示例"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>my_fatal_cb</span><span>(</span><span>int</span><span><span> </span><span>err</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Fatal error: </span><span>%d</span><span>\n</span><span>"</span><span>, err);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>abort</span><span>();</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>event_set_fatal_callback</span><span>(my_fatal_cb);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>// ... 其他代码</span></div></div><div><div><div>13</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>五、调试日志<a href="#五调试日志"><span>#</span></a></h2><section><h3>5.1 启用调试日志<a href="#51-启用调试日志"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 启用调试日志</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* 启用后，libevent会输出详细的调试信息</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* 注意：这会降低性能，仅用于调试</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>event_enable_debug_mode</span><span>(</span><span>void</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.2 调试事件<a href="#52-调试事件"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 记录事件添加的调试信息 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>event_enable_debug_logging</span><span>(</span><span>int</span><span><span> </span><span>which</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>EVENT_DBG_NONE</span><span>  </span><span><span>0x</span><span>00</span></span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>EVENT_DBG_ALL</span><span>   </span><span><span>0x</span><span>ffffffff</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、内存调试<a href="#六内存调试"><span>#</span></a></h2><section><h3>6.1 内存分配函数<a href="#61-内存分配函数"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置自定义内存分配函数 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>event_set_mem_functions</span><span>(</span><span>void</span><span> </span><span>*</span><span>(</span><span>*</span><span><span>malloc_fn</span><span>)(</span></span><span>size_t</span><span><span> sz</span><span>),</span></span></div></div><div><div><div>3</div></div><div><span>                            </span><span>void</span><span> </span><span>*</span><span>(</span><span>*</span><span><span>realloc_fn</span><span>)(</span></span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>,</span><span> </span></span><span>size_t</span><span><span> sz</span><span>),</span></span></div></div><div><div><div>4</div></div><div><span>                            </span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>free_fn</span><span>)(</span></span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>));</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 示例<a href="#62-示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>*</span><span>my_malloc</span><span>(</span><span>size_t</span><span><span> </span><span>sz</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>p </span><span>=</span><span> </span><span>malloc</span><span>(sz);</span></div></div><div><div><div>6</div></div><div><span>    </span><span>printf</span><span>(</span><span>"malloc(</span><span>%zu</span><span>) = </span><span>%p</span><span>\n</span><span>"</span><span>, sz, p);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> p;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>*</span><span>my_realloc</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>sz</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>p </span><span>=</span><span> </span><span>realloc</span><span>(ptr, sz);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>printf</span><span>(</span><span>"realloc(</span><span>%p</span><span>, </span><span>%zu</span><span>) = </span><span>%p</span><span>\n</span><span>"</span><span>, ptr, sz, p);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>return</span><span> p;</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>my_free</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>printf</span><span>(</span><span>"free(</span><span>%p</span><span>)</span><span>\n</span><span>"</span><span>, ptr);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>free</span><span>(ptr);</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>event_set_mem_functions</span><span>(my_malloc, my_realloc, my_free);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>// ... 其他代码</span></div></div><div><div><div>24</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、完整示例<a href="#七完整示例"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;time.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>const</span><span> </span><span>char*</span><span> </span><span>get_time_str</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>static</span><span> </span><span>char</span><span> </span><span>buf</span><span>[</span><span>64</span><span>];</span></div></div><div><div><div>9</div></div><div><span>    </span><span>time_t</span><span> t </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> tm </span><span>*</span><span>tm </span><span>=</span><span> </span><span>localtime</span><span>(</span><span>&amp;</span><span>t);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>strftime</span><span>(buf, </span><span>sizeof</span><span>(buf), </span><span>"</span><span>%</span><span>Y-</span><span>%</span><span>m-</span><span>%d</span><span> </span><span>%</span><span>H:</span><span>%</span><span>M:</span><span>%S</span><span>"</span><span>, tm);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> buf;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>my_log_cb</span><span>(</span><span>int</span><span><span> </span><span>severity</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>msg</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>level;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>switch</span><span> (severity) {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_DEBUG: level </span><span>=</span><span> </span><span>"DEBUG"</span><span>; </span><span>break</span><span>;</span></div></div><div><div><div>19</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_MSG:   level </span><span>=</span><span> </span><span>"INFO"</span><span>;  </span><span>break</span><span>;</span></div></div><div><div><div>20</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_WARN:  level </span><span>=</span><span> </span><span>"WARN"</span><span>;  </span><span>break</span><span>;</span></div></div><div><div><div>21</div></div><div><span>        </span><span>case</span><span> EVENT_LOG_ERR:   level </span><span>=</span><span> </span><span>"ERROR"</span><span>; </span><span>break</span><span>;</span></div></div><div><div><div>22</div></div><div><span>        </span><span>default</span><span>:              level </span><span>=</span><span> </span><span>"UNKNOWN"</span><span>; </span><span>break</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"[</span><span>%s</span><span>][libevent][</span><span>%s</span><span>] </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>get_time_str</span><span>(), level, msg);</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer triggered</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>32</div></div><div><span>    </span><span>event_set_log_callback</span><span>(my_log_cb);</span></div></div><div><div><div>33</div></div><div><span>    </span><span>event_enable_debug_mode</span><span>();</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>36</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>37</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>38</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Using method: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>event_base_get_method</span><span>(base));</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>2</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>45</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>// 运行5秒后退出</span></div></div><div><div><div>48</div></div><div><span>    </span><span>struct</span><span> timeval exit_tv </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>49</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>&amp;</span><span>exit_tv);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>54</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>57</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>八、日志使用建议<a href="#八日志使用建议"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>开发调试</strong></td><td>启用调试日志，输出详细信息</td></tr><tr><td><strong>生产环境</strong></td><td>只记录警告和错误级别日志</td></tr><tr><td><strong>内存泄漏排查</strong></td><td>使用自定义内存分配函数跟踪内存分配</td></tr><tr><td><strong>崩溃分析</strong></td><td>设置致命错误回调，记录错误信息</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-multi-threaded/</id>
      <title type="text">🧵 多线程编程 - libevent源码深度解析系列（十四）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-multi-threaded/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔀 多线程编程<a href="#-多线程编程"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent本身是单线程设计，但提供了多线程支持。在多线程环境中使用libevent需要遵循特定的规则，否则会导致数据竞争和未定义行为。</p></section><section><h2>二、多线程原则<a href="#二多线程原则"><span>#</span></a></h2><section><h3>2.1 基本原则<a href="#21-基本原则"><span>#</span></a></h3>

<table><thead><tr><th>原则</th><th>说明</th></tr></thead><tbody><tr><td><strong>一个event_base一个线程</strong></td><td>每个event_base只能在创建它的线程中使用</td></tr><tr><td><strong>线程间通信</strong></td><td>使用socket pair或bufferevent_pair进行线程间通信</td></tr><tr><td><strong>锁保护</strong></td><td>多线程共享资源需要使用锁保护</td></tr><tr><td><strong>避免阻塞</strong></td><td>回调函数中不要执行阻塞操作</td></tr></tbody></table></section><section><h3>2.2 线程安全初始化<a href="#22-线程安全初始化"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/thread.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 初始化线程支持（必须在event_base_new之前调用）</span></div></div><div><div><div>6</div></div><div><span>#ifdef</span><span> </span><span>_WIN32</span></div></div><div><div><div>7</div></div><div><span>    </span><span>evthread_use_windows_threads</span><span>();</span></div></div><div><div><div>8</div></div><div><span>#else</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evthread_use_pthreads</span><span>();</span></div></div><div><div><div>10</div></div><div><span>#endif</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 现在可以创建线程安全的event_base</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>14</div></div><div><span>    </span><span>event_config_set_flag</span><span>(cfg, EVENT_BASE_FLAG_NOLOCK);</span><span>  // 单线程可禁用锁提高性能</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>17</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、线程API<a href="#三线程api"><span>#</span></a></h2><section><h3>3.1 线程支持初始化<a href="#31-线程支持初始化"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 使用pthreads（Linux/Unix） */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evthread_use_pthreads</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 使用Windows线程 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evthread_use_windows_threads</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 自定义锁实现 */</span></div></div><div><div><div>8</div></div><div><span>int</span><span> </span><span>evthread_set_lock_callbacks</span><span>(</span><span>const</span><span> </span><span>struct</span><span> evthread_lock_callbacks </span><span>*</span><span><span>cbs</span><span>);</span></span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>evthread_set_condition_callbacks</span><span>(</span><span>const</span><span> </span><span>struct</span><span> evthread_condition_callbacks </span><span>*</span><span><span>cbs</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>evthread_set_id_callback</span><span>(</span><span>unsigned</span><span> </span><span>long</span><span><span> </span><span>(</span></span><span>*</span><span><span>id_fn</span><span>)(</span></span><span>void</span><span>));</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 锁回调结构体<a href="#32-锁回调结构体"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> evthread_lock_callbacks {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>int</span><span> lock_api_version;</span><span>     // EVTHREAD_LOCK_API_VERSION</span></div></div><div><div><div>3</div></div><div><span>    </span><span>unsigned</span><span> supported_locktypes;</span><span>  // 支持的锁类型</span></div></div><div><div><div>4</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>(</span><span>*</span><span>alloc)(</span><span>unsigned</span><span> locktype);</span><span>  // 分配锁</span></div></div><div><div><div>5</div></div><div><span>    </span><span>void</span><span> (</span><span>*</span><span>free)(</span><span>void</span><span> </span><span>*</span><span>lock, </span><span>unsigned</span><span> locktype);</span><span>  // 释放锁</span></div></div><div><div><div>6</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>lock)(</span><span>unsigned</span><span> mode, </span><span>void</span><span> </span><span>*</span><span>lock);</span><span>   // 加锁</span></div></div><div><div><div>7</div></div><div><span>    </span><span>int</span><span> (</span><span>*</span><span>unlock)(</span><span>unsigned</span><span> mode, </span><span>void</span><span> </span><span>*</span><span>lock);</span><span> // 解锁</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 锁模式 */</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>EVTHREAD_LOCK</span><span>    </span><span><span>0x</span><span>01</span></span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>EVTHREAD_TRY</span><span>     </span><span><span>0x</span><span>02</span></span></div></div><div><div><div>13</div></div><div><span>#define</span><span> </span><span>EVTHREAD_UNLOCK</span><span>  </span><span><span>0x</span><span>04</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>/* 锁类型 */</span></div></div><div><div><div>16</div></div><div><span>#define</span><span> </span><span>EVTHREAD_LOCKTYPE_RECURSIVE</span><span> </span><span>1</span></div></div><div><div><div>17</div></div><div><span>#define</span><span> </span><span>EVTHREAD_LOCKTYPE_READWRITE</span><span> </span><span>2</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、线程间通信<a href="#四线程间通信"><span>#</span></a></h2><section><h3>4.1 使用socket pair<a href="#41-使用socket-pair"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;pthread.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>main_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent </span><span>*</span><span>main_bev </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>notify_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>17</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>20</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received from worker: </span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>void</span><span> </span><span>*</span><span>worker_thread</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>25</div></div><div><span>    </span><span>int</span><span> notify_fd </span><span>=</span><span> </span><span>*</span><span>(</span><span>int</span><span> </span><span>*</span><span>)arg;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>// 模拟工作</span></div></div><div><div><div>28</div></div><div><span>    </span><span>sleep</span><span>(</span><span>2</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>// 通知主线程</span></div></div><div><div><div>31</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>msg </span><span>=</span><span> </span><span>"Work done!</span><span>\n</span><span>"</span><span>;</span></div></div><div><div><div>32</div></div><div><span>    </span><span>write</span><span>(notify_fd, msg, </span><span>strlen</span><span>(msg));</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>38</div></div><div><span>    </span><span>evthread_use_pthreads</span><span>();</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>main_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>// 创建socket pair</span></div></div><div><div><div>43</div></div><div><span>    </span><span>int</span><span> </span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>44</div></div><div><span>    </span><span>evutil_socketpair</span><span>(AF_UNIX, SOCK_STREAM, </span><span>0</span><span>, pair);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>// 主线程监听pair[0]</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>main_bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(main_base, </span><span>pair</span><span>[</span><span>0</span><span>], BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(main_bev, notify_cb, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>49</div></div><div><span>    </span><span>bufferevent_enable</span><span>(main_bev, EV_READ);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>// 创建工作线程，传递pair[1]</span></div></div><div><div><div>52</div></div><div><span>    </span><span>pthread_t</span><span> tid;</span></div></div><div><div><div>53</div></div><div><span>    </span><span>pthread_create</span><span>(</span><span>&amp;</span><span>tid, </span><span>NULL</span><span>, worker_thread, </span><span>&amp;</span><span>pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>// 设置5秒后退出</span></div></div><div><div><div>56</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>57</div></div><div><span>    </span><span>event_base_loopexit</span><span>(main_base, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>event_base_dispatch</span><span>(main_base);</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>pthread_join</span><span>(tid, </span><span>NULL</span><span>);</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>bufferevent_free</span><span>(main_bev);</span></div></div><div><div><div>64</div></div><div><span>    </span><span>event_base_free</span><span>(main_base);</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>67</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 使用bufferevent_pair<a href="#42-使用bufferevent_pair"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;pthread.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>struct</span><span> thread_data {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev;</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>worker_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>17</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>20</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Worker received: </span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>        </span><span>// 回复</span></div></div><div><div><div>23</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, </span><span>"ACK</span><span>\n</span><span>"</span><span>, </span><span>4</span><span>);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>void</span><span> </span><span>*</span><span>worker_thread</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>struct</span><span> thread_data </span><span>*</span><span>data </span><span>=</span><span> arg;</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>data</span><span>-&gt;</span><span>bev</span><span>, worker_read_cb, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>31</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>data</span><span>-&gt;</span><span>bev</span><span>, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>event_base_dispatch</span><span>(</span><span>data</span><span>-&gt;</span><span>base</span><span>);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>36</div></div><div><span>}</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>39</div></div><div><span>    </span><span>evthread_use_pthreads</span><span>();</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>main_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>worker_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>// 创建bufferevent pair</span></div></div><div><div><div>45</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>pair</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>46</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(worker_base, BEV_OPT_THREADSAFE, pair);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>// pair[0]给工作线程</span></div></div><div><div><div>49</div></div><div><span>    </span><span>struct</span><span> thread_data data </span><span>=</span><span> {worker_base, </span><span>pair</span><span>[</span><span>0</span><span>]};</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>// pair[1]给主线程</span></div></div><div><div><div>52</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>main_bev </span><span>=</span><span> </span><span>bufferevent_pair_new</span><span>(main_base, BEV_OPT_THREADSAFE, pair);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>// 启动工作线程</span></div></div><div><div><div>55</div></div><div><span>    </span><span>pthread_t</span><span> tid;</span></div></div><div><div><div>56</div></div><div><span>    </span><span>pthread_create</span><span>(</span><span>&amp;</span><span>tid, </span><span>NULL</span><span>, worker_thread, </span><span>&amp;</span><span>data);</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>// 主线程发送消息</span></div></div><div><div><div>59</div></div><div><span>    </span><span>sleep</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>bufferevent_write</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>], </span><span>"Hello from main</span><span>\n</span><span>"</span><span>, </span><span>16</span><span>);</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>    </span><span>// 等待回复</span></div></div><div><div><div>63</div></div><div><span>    </span><span>sleep</span><span>(</span><span>1</span><span>);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>// 退出</span></div></div><div><div><div>66</div></div><div><span>    </span><span>event_base_loopexit</span><span>(worker_base, </span><span>NULL</span><span>);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>event_base_loopexit</span><span>(main_base, </span><span>NULL</span><span>);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>pthread_join</span><span>(tid, </span><span>NULL</span><span>);</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>72</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>73</div></div><div><span>    </span><span>event_base_free</span><span>(worker_base);</span></div></div><div><div><div>74</div></div><div><span>    </span><span>event_base_free</span><span>(main_base);</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>77</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、HTTP服务器C++封装<a href="#五http服务器c封装"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;iostream&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;vector&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;queue&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;thread&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;mutex&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;condition_variable&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;memory&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>11</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>class</span><span> </span><span>HttpServer</span><span> {</span></div></div><div><div><div>14</div></div><div><span>public:</span></div></div><div><div><div>15</div></div><div><span>    </span><span>HttpServer</span><span>(</span><span>int</span><span><span> </span><span>port</span><span>, </span></span><span>int</span><span><span> </span><span>thread_num</span><span> </span></span><span>=</span><span> </span><span>4</span><span>)</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>: </span><span>port_</span><span>(</span><span>port</span><span>), </span><span>thread_num_</span><span>(</span><span>thread_num</span><span>), </span><span>base_</span><span>(</span><span>nullptr</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>evthread_use_pthreads</span><span>();</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>~HttpServer</span><span>() {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>stop</span><span>();</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>bool</span><span> </span><span>start</span><span>() {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>base_</span><span> </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>26</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span>base_</span><span>) </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>        </span><span>http_</span><span> </span><span>=</span><span> </span><span>evhttp_new</span><span>(</span><span>base_</span><span>);</span></div></div><div><div><div>29</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span>http_</span><span>) </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>        </span><span>if</span><span> (</span><span>evhttp_bind_socket</span><span>(</span><span>http_</span><span>, </span><span>"0.0.0.0"</span><span>, </span><span>port_</span><span>) </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>32</div></div><div><span>            </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>        </span><span>evhttp_set_gencb</span><span>(</span><span>http_</span><span>, HttpServer::</span><span>generic_callback</span><span>, </span><span>this</span><span>);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>// 启动工作线程</span></div></div><div><div><div>38</div></div><div><span>        </span><span>for</span><span> (</span><span>int</span><span> </span><span>i</span><span> </span><span>=</span><span> </span><span>0</span><span>; </span><span>i</span><span> </span><span>&lt;</span><span> </span><span>thread_num_</span><span>; </span><span>i</span><span>++</span><span>) {</span></div></div><div><div><div>39</div></div><div><span>            </span><span>threads_</span><span>.</span><span>emplace_back</span><span>(</span><span>&amp;</span><span>HttpServer::</span><span>worker_loop</span><span>, </span><span>this</span><span>);</span></div></div><div><div><div>40</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>std::</span><span>cout</span><span> </span><span>&lt;&lt;</span><span> </span><span>"HTTP server started on port "</span><span> </span><span>&lt;&lt;</span><span> </span><span>port_</span><span> </span><span>&lt;&lt;</span><span> std::</span><span>endl</span><span>;</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>        </span><span>event_base_dispatch</span><span>(</span><span>base_</span><span>);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>        </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>void</span><span> </span><span>stop</span><span>() {</span></div></div><div><div><div>50</div></div><div><span>        </span><span>if</span><span> (</span><span>base_</span><span>) {</span></div></div><div><div><div>51</div></div><div><span>            </span><span>event_base_loopbreak</span><span>(</span><span>base_</span><span>);</span></div></div><div><div><div>52</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>        </span><span>for</span><span> (</span><span>auto&amp;</span><span> </span><span>t</span><span> : </span><span>threads_</span><span>) {</span></div></div><div><div><div>55</div></div><div><span>            </span><span>if</span><span> (</span><span>t</span><span>.</span><span>joinable</span><span>()) {</span></div></div><div><div><div>56</div></div><div><span>                </span><span>t</span><span>.</span><span>join</span><span>();</span></div></div><div><div><div>57</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>58</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>        </span><span>if</span><span> (</span><span>http_</span><span>) {</span></div></div><div><div><div>61</div></div><div><span>            </span><span>evhttp_free</span><span>(</span><span>http_</span><span>);</span></div></div><div><div><div>62</div></div><div><span>            </span><span>http_</span><span> </span><span>=</span><span> </span><span>nullptr</span><span>;</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>        </span><span>if</span><span> (</span><span>base_</span><span>) {</span></div></div><div><div><div>66</div></div><div><span>            </span><span>event_base_free</span><span>(</span><span>base_</span><span>);</span></div></div><div><div><div>67</div></div><div><span>            </span><span>base_</span><span> </span><span>=</span><span> </span><span>nullptr</span><span>;</span></div></div><div><div><div>68</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>69</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>private:</span></div></div><div><div><div>72</div></div><div><span>    </span><span>static</span><span> </span><span>void</span><span> </span><span>generic_callback</span><span>(</span><span>struct</span><span> </span><span>evhttp_request</span><span> </span><span>*</span><span><span>req</span><span>, </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>73</div></div><div><span>        </span><span>HttpServer</span><span> </span><span>*</span><span>server</span><span> </span><span>=</span><span> </span><span><span>static_cast</span><span>&lt;</span></span><span>HttpServer</span><span>*&gt;</span><span>(</span><span>arg</span><span>);</span></div></div><div><div><div>74</div></div><div><span>        </span><span>server</span><span>-&gt;</span><span>handle_request</span><span>(</span><span>req</span><span>);</span></div></div><div><div><div>75</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>76</div></div><div>
</div></div><div><div><div>77</div></div><div><span>    </span><span>void</span><span> </span><span>handle_request</span><span>(</span><span>struct</span><span> </span><span>evhttp_request</span><span> </span><span>*</span><span><span>req</span><span>) {</span></span></div></div><div><div><div>78</div></div><div><span>        </span><span>struct</span><span> </span><span>evbuffer</span><span> </span><span>*</span><span>buf</span><span> </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>79</div></div><div>
</div></div><div><div><div>80</div></div><div><span>        </span><span>evbuffer_add_printf</span><span>(</span><span>buf</span><span>, </span><span>"&lt;html&gt;&lt;body&gt;"</span><span>);</span></div></div><div><div><div>81</div></div><div><span>        </span><span>evbuffer_add_printf</span><span>(</span><span>buf</span><span>, </span><span>"&lt;h1&gt;Hello from libevent HTTP Server&lt;/h1&gt;"</span><span>);</span></div></div><div><div><div>82</div></div><div><span>        </span><span>evbuffer_add_printf</span><span>(</span><span>buf</span><span>, </span><span>"&lt;p&gt;URI: </span><span>%s</span><span>&lt;/p&gt;"</span><span>, </span><span>evhttp_request_get_uri</span><span>(</span><span>req</span><span>));</span></div></div><div><div><div>83</div></div><div><span>        </span><span>evbuffer_add_printf</span><span>(</span><span>buf</span><span>, </span><span>"&lt;/body&gt;&lt;/html&gt;"</span><span>);</span></div></div><div><div><div>84</div></div><div>
</div></div><div><div><div>85</div></div><div><span>        </span><span>evhttp_add_header</span><span>(</span><span>evhttp_request_get_output_headers</span><span>(</span><span>req</span><span>),</span></div></div><div><div><div>86</div></div><div><span>                          </span><span>"Content-Type"</span><span>, </span><span>"text/html"</span><span>);</span></div></div><div><div><div>87</div></div><div><span>        </span><span>evhttp_send_reply</span><span>(</span><span>req</span><span>, </span><span>HTTP_OK</span><span>, </span><span>"OK"</span><span>, </span><span>buf</span><span>);</span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>        </span><span>evbuffer_free</span><span>(</span><span>buf</span><span>);</span></div></div><div><div><div>90</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>    </span><span>void</span><span> </span><span>worker_loop</span><span>() {</span></div></div><div><div><div>93</div></div><div><span><span>        </span></span><span>// 工作线程可以处理其他任务</span></div></div><div><div><div>94</div></div><div><span>        </span><span>while</span><span> (</span><span>true</span><span>) {</span></div></div><div><div><div>95</div></div><div><span><span>            </span></span><span>// 这里可以添加任务队列处理逻辑</span></div></div><div><div><div>96</div></div><div><span><span>            </span></span><span>std::</span><span>this_thread</span><span>::</span><span>sleep_for</span><span><span>(</span><span>std</span><span>::</span><span>chrono</span><span>::</span></span><span>milliseconds</span><span>(</span><span>100</span><span>));</span></div></div><div><div><div>97</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>98</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>99</div></div><div>
</div></div><div><div><div>100</div></div><div><span>    </span><span>int</span><span> </span><span>port_</span><span>;</span></div></div><div><div><div>101</div></div><div><span>    </span><span>int</span><span> </span><span>thread_num_</span><span>;</span></div></div><div><div><div>102</div></div><div><span>    </span><span>struct</span><span> </span><span>event_base</span><span> </span><span>*</span><span>base_</span><span>;</span></div></div><div><div><div>103</div></div><div><span>    </span><span>struct</span><span> </span><span>evhttp</span><span> </span><span>*</span><span>http_</span><span>;</span></div></div><div><div><div>104</div></div><div><span><span>    </span></span><span>std::</span><span>vector</span><span>&lt;std::</span><span>thread</span><span>&gt; </span><span>threads_</span><span>;</span></div></div><div><div><div>105</div></div><div><span>};</span></div></div><div><div><div>106</div></div><div>
</div></div><div><div><div>107</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>108</div></div><div><span>    </span><span>HttpServer</span><span> </span><span>server</span><span>(</span><span>8080</span><span>, </span><span>4</span><span>);</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>server</span><span>.</span><span>start</span><span>()) {</span></div></div><div><div><div>111</div></div><div><span><span>        </span></span><span>std::</span><span>cerr</span><span> </span><span>&lt;&lt;</span><span> </span><span>"Failed to start server"</span><span> </span><span>&lt;&lt;</span><span> std::</span><span>endl</span><span>;</span></div></div><div><div><div>112</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>113</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>114</div></div><div>
</div></div><div><div><div>115</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>116</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>六、最佳实践<a href="#六最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>建议</th><th>说明</th></tr></thead><tbody><tr><td><strong>初始化顺序</strong></td><td>先调用evthread_use_pthreads()，再创建event_base</td></tr><tr><td><strong>锁粒度</strong></td><td>尽量减少锁的持有时间</td></tr><tr><td><strong>避免死锁</strong></td><td>注意锁的获取顺序</td></tr><tr><td><strong>线程池</strong></td><td>对于CPU密集型任务使用线程池</td></tr><tr><td><strong>资源清理</strong></td><td>确保线程退出前释放所有资源</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-performance-optimization/</id>
      <title type="text">🚀 性能调优指南 - libevent源码深度解析系列（二十五）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-performance-optimization/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🚀 性能调优指南<a href="#-性能调优指南"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>在生产环境中使用libevent时，性能调优至关重要。本章将从多个维度介绍libevent的性能优化策略，包括性能瓶颈分析、事件循环优化、内存管理、多线程扩展、系统调优等方面。</p></section><section><h2>二、性能瓶颈分析<a href="#二性能瓶颈分析"><span>#</span></a></h2><section><h3>2.1 常见性能瓶颈<a href="#21-常见性能瓶颈"><span>#</span></a></h3>

<table><thead><tr><th>瓶颈类型</th><th>表现症状</th><th>原因分析</th><th>优化方向</th></tr></thead><tbody><tr><td>事件循环阻塞</td><td>CPU使用率低，响应延迟高</td><td>回调函数执行时间过长</td><td>缩短回调处理时间，使用线程池</td></tr><tr><td>系统调用频繁</td><td>系统调用次数高</td><td>事件频繁触发，缓冲区水位设置不当</td><td>调整缓冲区水位，使用changelist</td></tr><tr><td>内存分配频繁</td><td>内存碎片多，GC压力大</td><td>频繁的小内存分配</td><td>使用内存池，自定义内存分配器</td></tr><tr><td>锁竞争严重</td><td>多线程场景下性能下降</td><td>线程间共享资源竞争</td><td>减少锁范围，使用无锁数据结构</td></tr><tr><td>DNS解析延迟</td><td>连接建立时间长</td><td>同步DNS解析，DNS服务器响应慢</td><td>使用异步DNS，实现DNS缓存</td></tr><tr><td>网络I/O瓶颈</td><td>网络吞吐量低</td><td>TCP参数配置不当，缓冲区大小不合理</td><td>优化TCP参数，调整缓冲区大小</td></tr></tbody></table></section><section><h3>2.2 性能分析工具<a href="#22-性能分析工具"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用strace分析系统调用</span></div></div><div><div><div>2</div></div><div><span>strace</span><span> </span><span>-c</span><span> </span><span>-p</span><span> </span><span>&lt;pid&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用perf分析CPU使用情况</span></div></div><div><div><div>5</div></div><div><span>perf</span><span> </span><span>record</span><span> </span><span>-g</span><span> </span><span>-p</span><span> </span><span>&lt;pid&gt;</span></div></div><div><div><div>6</div></div><div><span>perf</span><span> </span><span>report</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 使用valgrind分析内存使用</span></div></div><div><div><div>9</div></div><div><span>valgrind</span><span> </span><span>--tool=massif</span><span> </span><span>./your_program</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># 使用pmap分析内存映射</span></div></div><div><div><div>12</div></div><div><span>pmap</span><span> </span><span>-x</span><span> </span><span>&lt;pid&gt;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 使用netstat分析网络连接</span></div></div><div><div><div>15</div></div><div><span>netstat</span><span> </span><span>-ant</span><span> | </span><span>grep</span><span> </span><span>ESTABLISHED</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.3 性能瓶颈定位方法<a href="#23-性能瓶颈定位方法"><span>#</span></a></h3><ol>
<li><strong>系统层面</strong>：使用 <code>top</code>、<code>vmstat</code>、<code>iostat</code> 监控系统资源使用</li>
<li><strong>进程层面</strong>：使用 <code>ps</code>、<code>pmap</code> 监控进程状态和内存使用</li>
<li><strong>网络层面</strong>：使用 <code>netstat</code>、<code>tcpdump</code> 监控网络连接和流量</li>
<li><strong>应用层面</strong>：实现自定义监控，统计关键指标</li>
</ol></section></section><section><h2>三、事件循环优化<a href="#三事件循环优化"><span>#</span></a></h2><section><h3>3.1 选择合适的后端<a href="#31-选择合适的后端"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>void</span><span> </span><span>print_available_backends</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>**</span><span>methods </span><span>=</span><span> </span><span>event_get_supported_methods</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Supported backends:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; </span><span>methods</span><span>[i] </span><span>!=</span><span> </span><span>NULL</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>printf</span><span>(</span><span>"  - </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>methods</span><span>[i]);</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>create_optimized_base</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>// 优先使用epoll（Linux）</span></div></div><div><div><div>17</div></div><div><span>    </span><span>event_config_avoid_method</span><span>(cfg, </span><span>"select"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>event_config_avoid_method</span><span>(cfg, </span><span>"poll"</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 或明确指定后端</span></div></div><div><div><div>21</div></div><div><span>    </span><span>// event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Using backend: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>event_base_get_method</span><span>(base));</span></div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span> base;</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 event_config标志<a href="#32-event_config标志"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>create_base_with_flags</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>// 使用changelist优化（减少系统调用）</span></div></div><div><div><div>7</div></div><div><span>    </span><span>event_config_set_flag</span><span>(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 忽略SIGPIPE</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_config_set_flag</span><span>(cfg, EVENT_BASE_FLAG_IGNORE_ENV);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 不使用eventfd（某些场景）</span></div></div><div><div><div>13</div></div><div><span>    </span><span>// event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> base;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.3 配置标志说明<a href="#33-配置标志说明"><span>#</span></a></h3>

<table><thead><tr><th>标志</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td><code>EVENT_BASE_FLAG_NOLOCK</code></td><td>禁用锁</td><td>单线程环境</td></tr><tr><td><code>EVENT_BASE_FLAG_IGNORE_ENV</code></td><td>忽略环境变量</td><td>生产环境</td></tr><tr><td><code>EVENT_BASE_FLAG_STARTUP_IOCP</code></td><td>启用IOCP</td><td>Windows</td></tr><tr><td><code>EVENT_BASE_FLAG_NO_CACHE_TIME</code></td><td>不缓存时间</td><td>高精度计时</td></tr><tr><td><code>EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST</code></td><td>使用changelist</td><td>Linux高并发</td></tr></tbody></table></section><section><h3>3.4 事件循环性能优化策略<a href="#34-事件循环性能优化策略"><span>#</span></a></h3><ol>
<li><strong>减少事件触发频率</strong>：合理设置缓冲区水位，避免频繁的读写事件</li>
<li><strong>优化事件回调</strong>：回调函数应简洁，避免阻塞操作</li>
<li><strong>批量处理</strong>：使用批量读取和处理，减少系统调用</li>
<li><strong>合理使用超时</strong>：设置适当的超时时间，避免资源泄漏</li>
<li><strong>避免事件风暴</strong>：使用背压机制，控制事件处理速率</li>
</ol></section></section><section><h2>四、内存管理优化<a href="#四内存管理优化"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>void</span><span> </span><span>print_available_backends</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>**</span><span>methods </span><span>=</span><span> </span><span>event_get_supported_methods</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Supported backends:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; </span><span>methods</span><span>[i] </span><span>!=</span><span> </span><span>NULL</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>printf</span><span>(</span><span>"  - </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>methods</span><span>[i]);</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>create_optimized_base</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>// 优先使用epoll（Linux）</span></div></div><div><div><div>17</div></div><div><span>    </span><span>event_config_avoid_method</span><span>(cfg, </span><span>"select"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>event_config_avoid_method</span><span>(cfg, </span><span>"poll"</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 或明确指定后端</span></div></div><div><div><div>21</div></div><div><span>    </span><span>// event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Using backend: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>event_base_get_method</span><span>(base));</span></div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span> base;</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h3>2.2 event_config标志<a href="#22-event_config标志"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>create_base_with_flags</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>// 使用changelist优化（减少系统调用）</span></div></div><div><div><div>7</div></div><div><span>    </span><span>event_config_set_flag</span><span>(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>// 忽略SIGPIPE</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_config_set_flag</span><span>(cfg, EVENT_BASE_FLAG_IGNORE_ENV);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 不使用eventfd（某些场景）</span></div></div><div><div><div>13</div></div><div><span>    </span><span>// event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> base;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.3 配置标志说明<a href="#23-配置标志说明"><span>#</span></a></h3>

<table><thead><tr><th>标志</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td><code>EVENT_BASE_FLAG_NOLOCK</code></td><td>禁用锁</td><td>单线程环境</td></tr><tr><td><code>EVENT_BASE_FLAG_IGNORE_ENV</code></td><td>忽略环境变量</td><td>生产环境</td></tr><tr><td><code>EVENT_BASE_FLAG_STARTUP_IOCP</code></td><td>启用IOCP</td><td>Windows</td></tr><tr><td><code>EVENT_BASE_FLAG_NO_CACHE_TIME</code></td><td>不缓存时间</td><td>高精度计时</td></tr><tr><td><code>EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST</code></td><td>使用changelist</td><td>Linux高并发</td></tr></tbody></table></section></section><section><h2>三、内存管理优化<a href="#三内存管理优化"><span>#</span></a></h2><section><h3>3.1 自定义内存分配器<a href="#31-自定义内存分配器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>size_t</span><span> g_total_allocated </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>*</span><span>my_malloc</span><span>(</span><span>size_t</span><span><span> </span><span>size</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>ptr </span><span>=</span><span> </span><span>malloc</span><span>(size);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (ptr) {</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>g_total_allocated </span><span>+=</span><span> size;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> ptr;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>*</span><span>my_realloc</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>size</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>return</span><span> </span><span>realloc</span><span>(ptr, size);</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>my_free</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>free</span><span>(ptr);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>void</span><span> </span><span>setup_custom_allocator</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>    </span><span>event_set_mem_functions</span><span>(my_malloc, my_realloc, my_free);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Custom memory allocator installed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 内存池模式<a href="#32-内存池模式"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>POOL_SIZE</span><span> </span><span>1024</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>BLOCK_SIZE</span><span> </span><span>4096</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>char</span><span> </span><span>blocks</span><span>[POOL_SIZE][BLOCK_SIZE];</span></div></div><div><div><div>9</div></div><div><span>    </span><span>int</span><span> </span><span>free_list</span><span>[POOL_SIZE];</span></div></div><div><div><div>10</div></div><div><span>    </span><span>int</span><span> free_count;</span></div></div><div><div><div>11</div></div><div><span>} </span><span>memory_pool_t</span><span>;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>memory_pool_t</span><span> g_pool;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>void</span><span> </span><span>pool_init</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>    </span><span>g_pool</span><span>.</span><span>free_count</span><span> </span><span>=</span><span> POOL_SIZE;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> POOL_SIZE; i</span><span>++</span><span>) {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>g_pool</span><span>.</span><span>free_list</span><span>[i] </span><span>=</span><span> i;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>void</span><span> </span><span>*</span><span>pool_alloc</span><span>(</span><span>size_t</span><span><span> </span><span>size</span><span>) {</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span> (size </span><span>&gt;</span><span> BLOCK_SIZE </span><span>||</span><span> </span><span>g_pool</span><span>.</span><span>free_count</span><span> </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>        </span><span>return</span><span> </span><span>malloc</span><span>(size);</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>int</span><span> idx </span><span>=</span><span> </span><span>g_pool</span><span>.</span><span>free_list</span><span>[</span><span>--</span><span>g_pool</span><span>.</span><span>free_count</span><span>];</span></div></div><div><div><div>28</div></div><div><span>    </span><span>return</span><span> </span><span>g_pool</span><span>.</span><span>blocks</span><span>[idx];</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>void</span><span> </span><span>pool_free</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>) {</span></span></div></div><div><div><div>32</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>p </span><span>=</span><span> (</span><span>char</span><span> </span><span>*</span><span>)ptr;</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>if</span><span> (p </span><span>&gt;=</span><span> (</span><span>char</span><span> </span><span>*</span><span>)</span><span>g_pool</span><span>.</span><span>blocks</span><span> </span><span>&amp;&amp;</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>p </span><span>&lt;</span><span> (</span><span>char</span><span> </span><span>*</span><span>)</span><span>g_pool</span><span>.</span><span>blocks</span><span> </span><span>+</span><span> POOL_SIZE </span><span>*</span><span> BLOCK_SIZE) {</span></div></div><div><div><div>36</div></div><div><span>        </span><span>int</span><span> idx </span><span>=</span><span> (p </span><span>-</span><span> (</span><span>char</span><span> </span><span>*</span><span>)</span><span>g_pool</span><span>.</span><span>blocks</span><span>) </span><span>/</span><span> BLOCK_SIZE;</span></div></div><div><div><div>37</div></div><div><span>        </span><span>g_pool</span><span>.</span><span>free_list</span><span>[</span><span>g_pool</span><span>.</span><span>free_count</span><span>++</span><span>] </span><span>=</span><span> idx;</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>39</div></div><div><span>        </span><span>free</span><span>(ptr);</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、bufferevent优化<a href="#四bufferevent优化"><span>#</span></a></h2><section><h3>4.1 缓冲区大小调整<a href="#41-缓冲区大小调整"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>optimize_bufferevent</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 设置读写缓冲区水位</span></div></div><div><div><div>5</div></div><div><span>    </span><span>bufferevent_setwatermark</span><span>(bev, EV_READ, </span><span>0</span><span>, </span><span>65536</span><span>);</span><span>   // 读高水位64KB</span></div></div><div><div><div>6</div></div><div><span>    </span><span>bufferevent_setwatermark</span><span>(bev, EV_WRITE, </span><span>4096</span><span>, </span><span>0</span><span>);</span><span>   // 写低水位4KB</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 设置超时</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>30</span><span>, </span><span>0</span><span>};</span><span>  // 30秒</span></div></div><div><div><div>10</div></div><div><span>    </span><span>bufferevent_set_timeouts</span><span>(bev, </span><span>&amp;</span><span>tv, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 启用延迟回调（减少锁竞争）</span></div></div><div><div><div>13</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, write_cb, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 批量数据处理<a href="#42-批量数据处理"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>optimized_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>6</div></div><div><span>    </span><span>size_t</span><span> total_len </span><span>=</span><span> </span><span>evbuffer_get_length</span><span>(input);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 批量处理，避免多次小读取</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (total_len </span><span>&lt;</span><span> </span><span>4096</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>        </span><span>return</span><span>;</span><span>  // 等待更多数据</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>// 使用peek避免数据复制</span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> evbuffer_iovec </span><span>vec</span><span>[</span><span>4</span><span>];</span></div></div><div><div><div>15</div></div><div><span>    </span><span>int</span><span> n </span><span>=</span><span> </span><span>evbuffer_peek</span><span>(input, </span><span>-</span><span>1</span><span>, </span><span>NULL</span><span>, vec, </span><span>4</span><span>);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> n; i</span><span>++</span><span>) {</span></div></div><div><div><div>18</div></div><div><span>        </span><span>process_data</span><span>(</span><span>vec</span><span>[i].</span><span>iov_base</span><span>, </span><span>vec</span><span>[i].</span><span>iov_len</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 丢弃已处理的数据</span></div></div><div><div><div>22</div></div><div><span>    </span><span>evbuffer_drain</span><span>(input, total_len);</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、多线程优化<a href="#五多线程优化"><span>#</span></a></h2><section><h3>5.1 线程池模型<a href="#51-线程池模型"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;pthread.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/thread.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>NUM_WORKERS</span><span> </span><span>4</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>pthread_t</span><span> thread;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>int</span><span> </span><span>notify_fd</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>notify_event;</span></div></div><div><div><div>14</div></div><div><span>} </span><span>worker_t</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>worker_t</span><span> </span><span>workers</span><span>[NUM_WORKERS];</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>*</span><span>worker_loop</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>worker_t</span><span> </span><span>*</span><span>worker </span><span>=</span><span> arg;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>event_base_dispatch</span><span>(</span><span>worker</span><span>-&gt;</span><span>base</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>notify_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>25</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1</span><span>];</span></div></div><div><div><div>26</div></div><div><span>    </span><span>read</span><span>(fd, buf, </span><span>1</span><span>);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>// 处理任务</span></div></div><div><div><div>28</div></div><div><span>}</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>void</span><span> </span><span>init_thread_pool</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>evthread_use_pthreads</span><span>();</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> NUM_WORKERS; i</span><span>++</span><span>) {</span></div></div><div><div><div>34</div></div><div><span>        </span><span>workers</span><span>[i].</span><span>base</span><span> </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>        </span><span>// 创建通知管道</span></div></div><div><div><div>37</div></div><div><span>        </span><span>evutil_socketpair</span><span>(AF_UNIX, SOCK_STREAM, </span><span>0</span><span>, </span><span>workers</span><span>[i].</span><span>notify_fd</span><span>);</span></div></div><div><div><div>38</div></div><div><span>        </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>workers</span><span>[i].</span><span>notify_fd</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>39</div></div><div><span>        </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>workers</span><span>[i].</span><span>notify_fd</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>        </span><span>// 监听通知</span></div></div><div><div><div>42</div></div><div><span>        </span><span>workers</span><span>[i].</span><span>notify_event</span><span> </span><span>=</span><span> </span><span>event_new</span><span>(</span><span>workers</span><span>[i].</span><span>base</span><span>,</span></div></div><div><div><div>43</div></div><div><span>            </span><span>workers</span><span>[i].</span><span>notify_fd</span><span>[</span><span>0</span><span>], EV_READ </span><span>|</span><span> EV_PERSIST, notify_cb, </span><span>&amp;</span><span>workers</span><span>[i]);</span></div></div><div><div><div>44</div></div><div><span>        </span><span>event_add</span><span>(</span><span>workers</span><span>[i].</span><span>notify_event</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>        </span><span>pthread_create</span><span>(</span><span>&amp;</span><span>workers</span><span>[i].</span><span>thread</span><span>, </span><span>NULL</span><span>, worker_loop, </span><span>&amp;</span><span>workers</span><span>[i]);</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>void</span><span> </span><span>dispatch_to_worker</span><span>(</span><span>int</span><span><span> </span><span>worker_id</span><span>,</span><span> </span></span><span>void</span><span><span> </span><span>(</span></span><span>*</span><span><span>task</span><span>)(</span></span><span>void</span><span> </span><span>*</span><span><span>),</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>51</div></div><div><span>    </span><span>// 发送通知到worker</span></div></div><div><div><div>52</div></div><div><span>    </span><span>write</span><span>(</span><span>workers</span><span>[worker_id].</span><span>notify_fd</span><span>[</span><span>1</span><span>], </span><span>"T"</span><span>, </span><span>1</span><span>);</span></div></div><div><div><div>53</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 连接分发策略<a href="#52-连接分发策略"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdatomic.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>static</span><span> </span><span>atomic_int</span><span> g_next_worker </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>get_next_worker</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>return</span><span> </span><span>atomic_fetch_add</span><span>(</span><span>&amp;</span><span>g_next_worker, </span><span>1</span><span>) </span><span>%</span><span> NUM_WORKERS;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>10</div></div><div><span>               </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>int</span><span> worker_id </span><span>=</span><span> </span><span>get_next_worker</span><span>();</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>// 将连接分发给worker</span></div></div><div><div><div>14</div></div><div><span>    </span><span>dispatch_connection_to_worker</span><span>(worker_id, fd, addr, len);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、I/O多路复用调优<a href="#六io多路复用调优"><span>#</span></a></h2><section><h3>6.1 文件描述符限制<a href="#61-文件描述符限制"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 查看当前限制</span></div></div><div><div><div>2</div></div><div><span>ulimit</span><span> </span><span>-n</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 临时修改</span></div></div><div><div><div>5</div></div><div><span>ulimit</span><span> </span><span>-n</span><span> </span><span>65535</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 永久修改（/etc/security/limits.conf）</span></div></div><div><div><div>8</div></div><div><span>* soft nofile 65535</span></div></div><div><div><div>9</div></div><div><span>* hard nofile 65535</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 内核参数调优<a href="#62-内核参数调优"><span>#</span></a></h3><div><div><div><figure><figcaption><span>/etc/sysctl.conf</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 最大文件描述符</span></div></div><div><div><div>2</div></div><div><span>fs.file-max</span><span> </span><span>=</span><span> </span><span>1000000</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># TCP连接队列</span></div></div><div><div><div>5</div></div><div><span>net.core.somaxconn</span><span> </span><span>=</span><span> </span><span>65535</span></div></div><div><div><div>6</div></div><div><span>net.ipv4.tcp_max_syn_backlog</span><span> </span><span>=</span><span> </span><span>65535</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># TCP缓冲区</span></div></div><div><div><div>9</div></div><div><span>net.core.rmem_max</span><span> </span><span>=</span><span> </span><span>16777216</span></div></div><div><div><div>10</div></div><div><span>net.core.wmem_max</span><span> </span><span>=</span><span> </span><span>16777216</span></div></div><div><div><div>11</div></div><div><span>net.ipv4.tcp_rmem</span><span> </span><span>=</span><span> </span><span>4096</span><span> </span><span>87380</span><span> </span><span>16777216</span></div></div><div><div><div>12</div></div><div><span>net.ipv4.tcp_wmem</span><span> </span><span>=</span><span> </span><span>4096</span><span> </span><span>65536</span><span> </span><span>16777216</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># TIME_WAIT优化</span></div></div><div><div><div>15</div></div><div><span>net.ipv4.tcp_tw_reuse</span><span> </span><span>=</span><span> </span><span>1</span></div></div><div><div><div>16</div></div><div><span>net.ipv4.tcp_fin_timeout</span><span> </span><span>=</span><span> </span><span>30</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span># 应用配置</span></div></div><div><div><div>19</div></div><div><span>sysctl</span><span> </span><span>-p</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、DNS优化<a href="#七dns优化"><span>#</span></a></h2><section><h3>7.1 DNS缓存<a href="#71-dns缓存"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;time.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>DNS_CACHE_SIZE</span><span> </span><span>1024</span></div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>DNS_CACHE_TTL</span><span> </span><span>300</span><span>  // 5分钟</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>11</div></div><div><span>    </span><span>char</span><span> </span><span>hostname</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> in_addr addr;</span></div></div><div><div><div>13</div></div><div><span>    </span><span>time_t</span><span> expire;</span></div></div><div><div><div>14</div></div><div><span>} </span><span>dns_cache_entry_t</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>dns_cache_entry_t</span><span> </span><span>dns_cache</span><span>[DNS_CACHE_SIZE];</span></div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>int</span><span> dns_cache_count </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>struct</span><span> in_addr </span><span>*</span><span>dns_cache_lookup</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>hostname</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>time_t</span><span> now </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> dns_cache_count; i</span><span>++</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>if</span><span> (</span><span>strcmp</span><span>(</span><span>dns_cache</span><span>[i].</span><span>hostname</span><span>, hostname) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>            </span><span>if</span><span> (</span><span>dns_cache</span><span>[i].</span><span>expire</span><span> </span><span>&gt;</span><span> now) {</span></div></div><div><div><div>25</div></div><div><span>                </span><span>return</span><span> </span><span>&amp;</span><span>dns_cache</span><span>[i].</span><span>addr</span><span>;</span></div></div><div><div><div>26</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>            </span><span>// 过期，移除</span></div></div><div><div><div>28</div></div><div><span>            </span><span>dns_cache</span><span>[i] </span><span>=</span><span> </span><span>dns_cache</span><span>[</span><span>--</span><span>dns_cache_count];</span></div></div><div><div><div>29</div></div><div><span>            </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span>    </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>33</div></div><div><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>void</span><span> </span><span>dns_cache_store</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>hostname</span><span>,</span><span> </span></span><span>struct</span><span><span> in_addr </span><span>addr</span><span>) {</span></span></div></div><div><div><div>36</div></div><div><span>    </span><span>if</span><span> (dns_cache_count </span><span>&gt;=</span><span> DNS_CACHE_SIZE) {</span></div></div><div><div><div>37</div></div><div><span>        </span><span>return</span><span>;</span><span>  // 缓存满</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>strncpy</span><span>(</span><span>dns_cache</span><span>[dns_cache_count].</span><span>hostname</span><span>, hostname, </span><span>255</span><span>);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>dns_cache</span><span>[dns_cache_count].</span><span>addr</span><span> </span><span>=</span><span> addr;</span></div></div><div><div><div>42</div></div><div><span>    </span><span>dns_cache</span><span>[dns_cache_count].</span><span>expire</span><span> </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>) </span><span>+</span><span> DNS_CACHE_TTL;</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>dns_cache_count</span><span>++</span><span>;</span></div></div><div><div><div>44</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>7.2 DNS配置优化<a href="#72-dns配置优化"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/dns.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>optimize_dns</span><span>(</span><span>struct</span><span> evdns_base </span><span>*</span><span><span>dns_base</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 设置超时</span></div></div><div><div><div>5</div></div><div><span>    </span><span>evdns_base_set_option</span><span>(dns_base, </span><span>"timeout"</span><span>, </span><span>"2"</span><span>);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>// 设置重试次数</span></div></div><div><div><div>8</div></div><div><span>    </span><span>evdns_base_set_option</span><span>(dns_base, </span><span>"attempts"</span><span>, </span><span>"2"</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>// 添加多个DNS服务器</span></div></div><div><div><div>11</div></div><div><span>    </span><span>evdns_base_nameserver_ip_add</span><span>(dns_base, </span><span>"8.8.8.8"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>    </span><span>evdns_base_nameserver_ip_add</span><span>(dns_base, </span><span>"8.8.4.4"</span><span>);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>evdns_base_nameserver_ip_add</span><span>(dns_base, </span><span>"1.1.1.1"</span><span>);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 启用DNS缓存（应用层实现）</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、HTTP优化<a href="#八http优化"><span>#</span></a></h2><section><h3>8.1 连接复用<a href="#81-连接复用"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/http_connection.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>struct</span><span> evhttp_connection </span><span>*</span><span>g_http_conn </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>struct</span><span> evhttp_connection </span><span>*</span><span>get_http_connection</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span>                                               </span><span>struct</span><span> evdns_base </span><span>*</span><span><span>dns_base</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>g_http_conn) {</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>g_http_conn </span><span>=</span><span> </span><span>evhttp_connection_base_new</span><span>(base, dns_base,</span></div></div><div><div><div>10</div></div><div><span>                                                  </span><span>"api.example.com"</span><span>, </span><span>80</span><span>);</span></div></div><div><div><div>11</div></div><div><span>        </span><span>evhttp_connection_set_timeout</span><span>(g_http_conn, </span><span>30</span><span>);</span></div></div><div><div><div>12</div></div><div><span>        </span><span>evhttp_connection_set_retries</span><span>(g_http_conn, </span><span>3</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>    </span><span>return</span><span> g_http_conn;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>8.2 HTTP服务器优化<a href="#82-http服务器优化"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>optimize_http_server</span><span>(</span><span>struct</span><span> evhttp </span><span>*</span><span><span>http</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 设置最大body大小</span></div></div><div><div><div>5</div></div><div><span>    </span><span>evhttp_set_max_body_size</span><span>(http, </span><span>10</span><span> </span><span>*</span><span> </span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>);</span><span>  // 10MB</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>// 设置最大headers大小</span></div></div><div><div><div>8</div></div><div><span>    </span><span>evhttp_set_max_headers_size</span><span>(http, </span><span>8192</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>// 设置超时</span></div></div><div><div><div>11</div></div><div><span>    </span><span>evhttp_set_timeout</span><span>(http, </span><span>30</span><span>);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>// 设置支持的Content-Type</span></div></div><div><div><div>14</div></div><div><span>    </span><span>evhttp_set_allowed_methods</span><span>(http,</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>EVHTTP_REQ_GET </span><span>|</span><span> EVHTTP_REQ_POST </span><span>|</span><span> EVHTTP_REQ_PUT </span><span>|</span><span> EVHTTP_REQ_DELETE);</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>九、监控与统计<a href="#九监控与统计"><span>#</span></a></h2><section><h3>9.1 性能统计<a href="#91-性能统计"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;time.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;stdatomic.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>typedef</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>atomic_ulong</span><span> connections_total;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>atomic_ulong</span><span> connections_active;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>atomic_ulong</span><span> bytes_read;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>atomic_ulong</span><span> bytes_written;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>atomic_ulong</span><span> requests_total;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>atomic_ulong</span><span> requests_failed;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>time_t</span><span> start_time;</span></div></div><div><div><div>13</div></div><div><span>} </span><span>server_stats_t</span><span>;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>server_stats_t</span><span> g_stats </span><span>=</span><span> {</span><span>0</span><span>};</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>void</span><span> </span><span>stats_init</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>g_stats</span><span>.</span><span>start_time</span><span> </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>void</span><span> </span><span>stats_print</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>time_t</span><span> now </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>double</span><span> uptime </span><span>=</span><span> </span><span>difftime</span><span>(now, </span><span>g_stats</span><span>.</span><span>start_time</span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>=== Server Statistics ===</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Uptime: </span><span>%.0f</span><span> seconds</span><span>\n</span><span>"</span><span>, uptime);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total connections: </span><span>%lu</span><span>\n</span><span>"</span><span>, </span><span>g_stats</span><span>.</span><span>connections_total</span><span>);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Active connections: </span><span>%lu</span><span>\n</span><span>"</span><span>, </span><span>g_stats</span><span>.</span><span>connections_active</span><span>);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Bytes read: </span><span>%lu</span><span> (</span><span>%.2f</span><span> MB)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>30</div></div><div><span>           </span><span>g_stats</span><span>.</span><span>bytes_read</span><span>, (</span><span>double</span><span>)</span><span>g_stats</span><span>.</span><span>bytes_read</span><span> </span><span>/</span><span> (</span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>));</span></div></div><div><div><div>31</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Bytes written: </span><span>%lu</span><span> (</span><span>%.2f</span><span> MB)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>32</div></div><div><span>           </span><span>g_stats</span><span>.</span><span>bytes_written</span><span>, (</span><span>double</span><span>)</span><span>g_stats</span><span>.</span><span>bytes_written</span><span> </span><span>/</span><span> (</span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>));</span></div></div><div><div><div>33</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Requests: </span><span>%lu</span><span> total, </span><span>%lu</span><span> failed</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>34</div></div><div><span>           </span><span>g_stats</span><span>.</span><span>requests_total</span><span>, </span><span>g_stats</span><span>.</span><span>requests_failed</span><span>);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Throughput: </span><span>%.2f</span><span> req/s, </span><span>%.2f</span><span> KB/s read, </span><span>%.2f</span><span> KB/s write</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>36</div></div><div><span>           </span><span>g_stats</span><span>.</span><span>requests_total</span><span> </span><span>/</span><span> uptime,</span></div></div><div><div><div>37</div></div><div><span><span>           </span></span><span>(</span><span>double</span><span>)</span><span>g_stats</span><span>.</span><span>bytes_read</span><span> </span><span>/</span><span> uptime </span><span>/</span><span> </span><span>1024</span><span>,</span></div></div><div><div><div>38</div></div><div><span><span>           </span></span><span>(</span><span>double</span><span>)</span><span>g_stats</span><span>.</span><span>bytes_written</span><span> </span><span>/</span><span> uptime </span><span>/</span><span> </span><span>1024</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>printf</span><span>(</span><span>"=========================</span><span>\n\n</span><span>"</span><span>);</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>9.2 定时统计输出<a href="#92-定时统计输出"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>stats_timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>stats_print</span><span>();</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>void</span><span> </span><span>setup_stats_timer</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, stats_timer_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> timeval interval </span><span>=</span><span> {</span><span>60</span><span>, </span><span>0</span><span>};</span><span>  // 每分钟</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>interval);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>十、性能测试工具<a href="#十性能测试工具"><span>#</span></a></h2><section><h3>10.1 基准测试<a href="#101-基准测试"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;time.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>#define</span><span> </span><span>NUM_ITERATIONS</span><span> </span><span>1000000</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>g_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent </span><span>*</span><span>g_pair</span><span>[</span><span>2</span><span>] </span><span>=</span><span> {</span><span>NULL</span><span>, </span><span>NULL</span><span>};</span></div></div><div><div><div>12</div></div><div><span>static</span><span> </span><span>atomic_int</span><span> g_counter </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>struct</span><span> timespec g_start_time;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>64</span><span>];</span></div></div><div><div><div>17</div></div><div><span>    </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf));</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>if</span><span> (</span><span>++</span><span>g_counter </span><span>&gt;=</span><span> NUM_ITERATIONS) {</span></div></div><div><div><div>20</div></div><div><span>        </span><span>struct</span><span> timespec end_time;</span></div></div><div><div><div>21</div></div><div><span>        </span><span>clock_gettime</span><span>(CLOCK_MONOTONIC, </span><span>&amp;</span><span>end_time);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>        </span><span>double</span><span> elapsed </span><span>=</span><span> (</span><span>end_time</span><span>.</span><span>tv_sec</span><span> </span><span>-</span><span> </span><span>g_start_time</span><span>.</span><span>tv_sec</span><span>) </span><span>+</span></div></div><div><div><div>24</div></div><div><span><span>                         </span></span><span>(</span><span>end_time</span><span>.</span><span>tv_nsec</span><span> </span><span>-</span><span> </span><span>g_start_time</span><span>.</span><span>tv_nsec</span><span>) </span><span>/</span><span> </span><span><span>1</span><span>e</span><span>9</span></span><span>;</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Benchmark completed:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>27</div></div><div><span>        </span><span>printf</span><span>(</span><span>"  Iterations: </span><span>%d</span><span>\n</span><span>"</span><span>, NUM_ITERATIONS);</span></div></div><div><div><div>28</div></div><div><span>        </span><span>printf</span><span>(</span><span>"  Time: </span><span>%.3f</span><span> seconds</span><span>\n</span><span>"</span><span>, elapsed);</span></div></div><div><div><div>29</div></div><div><span>        </span><span>printf</span><span>(</span><span>"  Throughput: </span><span>%.0f</span><span> ops/s</span><span>\n</span><span>"</span><span>, NUM_ITERATIONS </span><span>/</span><span> elapsed);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>        </span><span>event_base_loopbreak</span><span>(g_base);</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>33</div></div><div><span>        </span><span>bufferevent_write</span><span>(</span><span>g_pair</span><span>[</span><span>0</span><span>], </span><span>"ping"</span><span>, </span><span>4</span><span>);</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>void</span><span> </span><span>run_benchmark</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>g_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>39</div></div><div><span>    </span><span>bufferevent_pair_new</span><span>(g_base, </span><span>0</span><span>, g_pair);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>g_pair</span><span>[</span><span>1</span><span>], read_cb, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>42</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>g_pair</span><span>[</span><span>1</span><span>], EV_READ);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>clock_gettime</span><span>(CLOCK_MONOTONIC, </span><span>&amp;</span><span>g_start_time);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>bufferevent_write</span><span>(</span><span>g_pair</span><span>[</span><span>0</span><span>], </span><span>"ping"</span><span>, </span><span>4</span><span>);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>event_base_dispatch</span><span>(g_base);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>g_pair</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>50</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>g_pair</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>51</div></div><div><span>    </span><span>event_base_free</span><span>(g_base);</span></div></div><div><div><div>52</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十一、性能优化清单<a href="#十一性能优化清单"><span>#</span></a></h2><section><h3>11.1 启动前检查<a href="#111-启动前检查"><span>#</span></a></h3>

<table><thead><tr><th>项目</th><th>检查项</th><th>建议</th></tr></thead><tbody><tr><td>系统限制</td><td><code>ulimit -n</code></td><td>&gt;= 65535</td></tr><tr><td>内核参数</td><td><code>sysctl</code></td><td>按需调整</td></tr><tr><td>后端选择</td><td>epoll/kqueue</td><td>优先使用</td></tr><tr><td>线程安全</td><td><code>evthread_use_pthreads</code></td><td>多线程必须</td></tr></tbody></table></section><section><h3>11.2 运行时优化<a href="#112-运行时优化"><span>#</span></a></h3>

<table><thead><tr><th>项目</th><th>优化项</th><th>说明</th></tr></thead><tbody><tr><td>缓冲区</td><td>设置合适的水位</td><td>避免频繁回调</td></tr><tr><td>超时</td><td>设置合理的超时</td><td>避免资源泄漏</td></tr><tr><td>DNS</td><td>使用缓存</td><td>减少DNS查询</td></tr><tr><td>连接</td><td>复用连接</td><td>减少连接开销</td></tr></tbody></table></section><section><h3>11.3 监控指标<a href="#113-监控指标"><span>#</span></a></h3>

<table><thead><tr><th>指标</th><th>说明</th><th>告警阈值</th></tr></thead><tbody><tr><td>连接数</td><td>当前活跃连接</td><td>&gt; 80% 容量</td></tr><tr><td>内存</td><td>进程内存使用</td><td>&gt; 80% 限制</td></tr><tr><td>延迟</td><td>请求处理时间</td><td>&gt; 100ms</td></tr><tr><td>错误率</td><td>请求失败比例</td><td>&gt; 1%</td></tr></tbody></table></section></section><section><h2>十二、生产环境优化最佳实践<a href="#十二生产环境优化最佳实践"><span>#</span></a></h2><section><h3>12.1 部署策略<a href="#121-部署策略"><span>#</span></a></h3><ol>
<li><strong>多实例部署</strong>：使用多个进程实例，充分利用多核CPU</li>
<li><strong>负载均衡</strong>：使用Nginx或HAProxy进行负载均衡</li>
<li><strong>容器化</strong>：使用Docker容器化部署，便于水平扩展</li>
<li><strong>自动扩缩容</strong>：根据负载自动调整实例数量</li>
</ol></section><section><h3>12.2 配置管理<a href="#122-配置管理"><span>#</span></a></h3><div><div><div><figure><figcaption><span>/etc/libevent-server.conf</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 生产环境配置文件示例</span></div></div><div><div><div>2</div></div><div><span># 服务器配置</span></div></div><div><div><div>3</div></div><div><span>PORT</span><span>=</span><span>8080</span></div></div><div><div><div>4</div></div><div><span>WORKER_PROCESSES</span><span>=</span><span>4</span></div></div><div><div><div>5</div></div><div><span>MAX_CONNECTIONS</span><span>=</span><span>10000</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 性能配置</span></div></div><div><div><div>8</div></div><div><span>BUFFER_SIZE</span><span>=</span><span>65536</span></div></div><div><div><div>9</div></div><div><span>READ_TIMEOUT</span><span>=</span><span>30</span></div></div><div><div><div>10</div></div><div><span>WRITE_TIMEOUT</span><span>=</span><span>30</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 日志配置</span></div></div><div><div><div>13</div></div><div><span>LOG_LEVEL</span><span>=</span><span>info</span></div></div><div><div><div>14</div></div><div><span>LOG_FILE</span><span>=</span><span>/var/log/libevent-server.log</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 监控配置</span></div></div><div><div><div>17</div></div><div><span>STATS_INTERVAL</span><span>=</span><span>60</span></div></div><div><div><div>18</div></div><div><span>METRICS_PORT</span><span>=</span><span>9100</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>12.3 故障恢复<a href="#123-故障恢复"><span>#</span></a></h3><ol>
<li><strong>优雅重启</strong>：支持平滑重启，不中断服务</li>
<li><strong>健康检查</strong>：定期检查服务状态</li>
<li><strong>自动恢复</strong>：进程崩溃后自动重启</li>
<li><strong>数据持久化</strong>：关键数据定期持久化</li>
</ol></section><section><h3>12.4 安全优化<a href="#124-安全优化"><span>#</span></a></h3><ol>
<li><strong>连接限制</strong>：防止DoS攻击，限制单IP连接数</li>
<li><strong>输入验证</strong>：严格验证输入数据，防止注入攻击</li>
<li><strong>SSL/TLS</strong>：使用HTTPS，保护数据传输</li>
<li><strong>权限控制</strong>：最小权限原则，限制进程权限</li>
</ol></section><section><h3>12.5 性能测试与基准<a href="#125-性能测试与基准"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用wrk进行性能测试</span></div></div><div><div><div>2</div></div><div><span>wrk</span><span> </span><span>-t12</span><span> </span><span>-c400</span><span> </span><span>-d30s</span><span> </span><span>http://localhost:8080/</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用ab进行基准测试</span></div></div><div><div><div>5</div></div><div><span>ab</span><span> </span><span>-n</span><span> </span><span>10000</span><span> </span><span>-c</span><span> </span><span>100</span><span> </span><span>http://localhost:8080/</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 使用siege进行压力测试</span></div></div><div><div><div>8</div></div><div><span>siege</span><span> </span><span>-c</span><span> </span><span>100</span><span> </span><span>-t</span><span> </span><span>30S</span><span> </span><span>http://localhost:8080/</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>12.6 常见问题排查<a href="#126-常见问题排查"><span>#</span></a></h3>

<table><thead><tr><th>问题</th><th>症状</th><th>排查方法</th><th>解决方案</th></tr></thead><tbody><tr><td>连接数过多</td><td>系统负载高，响应慢</td><td>`netstat -ant</td><td>grep ESTABLISHED</td></tr><tr><td>内存泄漏</td><td>内存使用持续增长</td><td><code>pmap -x &lt;pid&gt;</code>, <code>valgrind</code></td><td>检查内存分配，使用内存池</td></tr><tr><td>CPU使用率高</td><td>系统负载高</td><td><code>top</code>, <code>perf</code></td><td>分析热点函数，优化算法</td></tr><tr><td>网络延迟高</td><td>响应时间长</td><td><code>ping</code>, <code>traceroute</code></td><td>检查网络链路，优化TCP参数</td></tr></tbody></table></section></section><section><h2>十三、性能调优案例<a href="#十三性能调优案例"><span>#</span></a></h2><section><h3>13.1 高并发Web服务器优化<a href="#131-高并发web服务器优化"><span>#</span></a></h3><p><strong>场景</strong>：处理10000+并发连接的Web服务器</p><p><strong>优化措施</strong>：</p><ol>
<li>使用epoll后端，启用changelist</li>
<li>设置合理的缓冲区水位：读高水位64KB，写低水位4KB</li>
<li>使用线程池处理CPU密集型任务</li>
<li>实现连接池，复用HTTP连接</li>
<li>启用DNS缓存，减少DNS查询</li>
</ol><p><strong>效果</strong>：</p><ul>
<li>并发连接数提升300%</li>
<li>响应时间降低60%</li>
<li>系统资源使用率降低40%</li>
</ul></section><section><h3>13.2 实时聊天服务器优化<a href="#132-实时聊天服务器优化"><span>#</span></a></h3><p><strong>场景</strong>：实时消息推送，要求低延迟</p><p><strong>优化措施</strong>：</p><ol>
<li>使用kqueue后端（FreeBSD/macOS）</li>
<li>减少事件回调处理时间，使用非阻塞IO</li>
<li>批量处理消息，减少系统调用</li>
<li>使用无锁数据结构，减少锁竞争</li>
<li>优化内存分配，使用内存池</li>
</ol><p><strong>效果</strong>：</p><ul>
<li>消息延迟降低70%</li>
<li>系统吞吐量提升250%</li>
<li>内存使用降低30%</li>
</ul><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-rate-limiting/</id>
      <title type="text">⚡ 速率限制 - libevent源码深度解析系列（二十三）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-rate-limiting/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>⚡ 速率限制<a href="#-速率限制"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent提供了速率限制（Rate Limiting）功能，用于控制数据传输的带宽。这对于防止服务器过载、公平分配资源、实现流量整形等场景非常有用。</p></section><section><h2>二、速率限制结构体<a href="#二速率限制结构体"><span>#</span></a></h2><section><h3>2.1 ev_token_bucket_cfg<a href="#21-ev_token_bucket_cfg"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> ev_token_bucket_cfg {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>size_t</span><span> read_rate;</span><span>       // 读取速率（字节/秒）</span></div></div><div><div><div>3</div></div><div><span>    </span><span>size_t</span><span> read_burst;</span><span>      // 读取突发大小（字节）</span></div></div><div><div><div>4</div></div><div><span>    </span><span>size_t</span><span> write_rate;</span><span>      // 写入速率（字节/秒）</span></div></div><div><div><div>5</div></div><div><span>    </span><span>size_t</span><span> write_burst;</span><span>     // 写入突发大小（字节）</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> timeval tick_len;</span><span> // 时间片长度</span></div></div><div><div><div>7</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 bufferevent_rate_limit_group<a href="#22-bufferevent_rate_limit_group"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> bufferevent_rate_limit_group {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg cfg;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket bucket;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>**</span><span>members;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>int</span><span> n_members;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>// ...</span></div></div><div><div><div>8</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、令牌桶配置<a href="#三令牌桶配置"><span>#</span></a></h2><section><h3>3.1 创建配置<a href="#31-创建配置"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>/* 创建令牌桶配置</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param read_rate   读取速率（字节/秒），0表示无限制</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param read_burst  读取突发大小（字节）</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* @param write_rate  写入速率（字节/秒），0表示无限制</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* @param write_burst 写入突发大小（字节）</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>* @param tick_len    时间片长度（NULL使用默认值）</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>10</div></div><div><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>11</div></div><div><span>    </span><span>size_t</span><span><span> </span><span>read_rate</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>read_burst</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>size_t</span><span><span> </span><span>write_rate</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>write_burst</span><span>,</span></span></div></div><div><div><div>13</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tick_len</span><span>);</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>/* 释放配置 */</span></div></div><div><div><div>16</div></div><div><span>void</span><span> </span><span>ev_token_bucket_cfg_free</span><span>(</span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span><span>cfg</span><span>);</span></span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>/* 设置时间片长度 */</span></div></div><div><div><div>19</div></div><div><span>int</span><span> </span><span>ev_token_bucket_cfg_set_tick_len</span><span>(</span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span><span>cfg</span><span>,</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> timeval </span><span>*</span><span><span>tick_len</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 配置示例<a href="#32-配置示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>// 创建配置：限制读取100KB/s，写入50KB/s</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>8</div></div><div><span>        </span><span>102400</span><span>,</span><span>    // 读取速率：100KB/s</span></div></div><div><div><div>9</div></div><div><span>        </span><span>102400</span><span>,</span><span>    // 读取突发：100KB</span></div></div><div><div><div>10</div></div><div><span>        </span><span>51200</span><span>,</span><span>     // 写入速率：50KB/s</span></div></div><div><div><div>11</div></div><div><span>        </span><span>51200</span><span>,</span><span>     // 写入突发：50KB</span></div></div><div><div><div>12</div></div><div><span>        </span><span>NULL</span><span>       // 使用默认时间片</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>cfg) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create token bucket config</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>17</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Token bucket config created</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Read rate: 100KB/s, Write rate: 50KB/s</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(cfg);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、单个bufferevent限速<a href="#四单个bufferevent限速"><span>#</span></a></h2><section><h3>4.1 设置限速<a href="#41-设置限速"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 为bufferevent设置速率限制 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>bufferevent_set_rate_limit</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span></span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span><span>cfg</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 取消速率限制 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>bufferevent_set_rate_limit</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>NULL</span><span>);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 获取速率限制状态 */</span></div></div><div><div><div>9</div></div><div><span>ev_ssize_t</span><span> </span><span>bufferevent_get_read_limit</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span>ev_ssize_t</span><span> </span><span>bufferevent_get_write_limit</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 获取当前速率 */</span></div></div><div><div><div>13</div></div><div><span>ev_ssize_t</span><span> </span><span>bufferevent_get_max_to_read</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>14</div></div><div><span>ev_ssize_t</span><span> </span><span>bufferevent_get_max_to_write</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 获取已传输字节数 */</span></div></div><div><div><div>17</div></div><div><span>ev_uint64_t</span><span> </span><span>bufferevent_get_bytes_read</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>18</div></div><div><span>ev_uint64_t</span><span> </span><span>bufferevent_get_bytes_written</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 单连接限速示例<a href="#42-单连接限速示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>g_rate_cfg </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>12</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>// 回显数据</span></div></div><div><div><div>16</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, buf, n);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>// 打印统计信息</span></div></div><div><div><div>20</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Bytes read: </span><span>%llu</span><span>, written: </span><span>%llu</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>21</div></div><div><span><span>           </span></span><span>(</span><span>unsigned</span><span> </span><span>long</span><span> </span><span>long</span><span>)</span><span>bufferevent_get_bytes_read</span><span>(bev),</span></div></div><div><div><div>22</div></div><div><span><span>           </span></span><span>(</span><span>unsigned</span><span> </span><span>long</span><span> </span><span>long</span><span>)</span><span>bufferevent_get_bytes_written</span><span>(bev));</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>26</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>28</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>33</div></div><div><span>                      </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>34</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>// 应用速率限制</span></div></div><div><div><div>41</div></div><div><span>    </span><span>bufferevent_set_rate_limit</span><span>(bev, g_rate_cfg);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>45</div></div><div><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>48</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>// 创建速率限制配置：限制每个连接100KB/s</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>g_rate_cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>52</div></div><div><span>        </span><span>102400</span><span>, </span><span>102400</span><span>,</span><span>  // 读取：100KB/s</span></div></div><div><div><div>53</div></div><div><span>        </span><span>102400</span><span>, </span><span>102400</span><span>,</span><span>  // 写入：100KB/s</span></div></div><div><div><div>54</div></div><div><span>        </span><span>NULL</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>58</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>59</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>60</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>61</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>64</div></div><div><span><span>        </span></span><span>base, accept_cb, base,</span></div></div><div><div><div>65</div></div><div><span><span>        </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>66</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>67</div></div><div>
</div></div><div><div><div>68</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server listening on port 8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>69</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Rate limit: 100KB/s per connection</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>72</div></div><div>
</div></div><div><div><div>73</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>74</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(g_rate_cfg);</span></div></div><div><div><div>75</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>76</div></div><div>
</div></div><div><div><div>77</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>78</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、速率限制组<a href="#五速率限制组"><span>#</span></a></h2><section><h3>5.1 创建速率限制组<a href="#51-创建速率限制组"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建速率限制组 */</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span>bufferevent_rate_limit_group_new</span><span>(</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span><span>cfg</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 释放速率限制组 */</span></div></div><div><div><div>7</div></div><div><span>void</span><span> </span><span>bufferevent_rate_limit_group_free</span><span>(</span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span><span>grp</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 将bufferevent加入组 */</span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>bufferevent_add_to_rate_limit_group</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span><span>grp</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>/* 将bufferevent从组中移除 */</span></div></div><div><div><div>14</div></div><div><span>int</span><span> </span><span>bufferevent_remove_from_rate_limit_group</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 获取组成员数量 */</span></div></div><div><div><div>17</div></div><div><span>int</span><span> </span><span>bufferevent_rate_limit_group_get_members</span><span>(</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span><span>grp</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>**</span><span><span>members</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n_members</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 组级别限速示例<a href="#52-组级别限速示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span>g_rate_group </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>12</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, buf, n);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div><span>        </span><span>bufferevent_remove_from_rate_limit_group</span><span>(bev);</span></div></div><div><div><div>23</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>28</div></div><div><span>                      </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>// 加入速率限制组</span></div></div><div><div><div>36</div></div><div><span>    </span><span>bufferevent_add_to_rate_limit_group</span><span>(bev, g_rate_group);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>43</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// 创建组级别速率限制：总带宽1MB/s</span></div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>47</div></div><div><span>        </span><span>1048576</span><span>, </span><span>1048576</span><span>,</span><span>  // 读取：1MB/s</span></div></div><div><div><div>48</div></div><div><span>        </span><span>1048576</span><span>, </span><span>1048576</span><span>,</span><span>  // 写入：1MB/s</span></div></div><div><div><div>49</div></div><div><span>        </span><span>NULL</span></div></div><div><div><div>50</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>g_rate_group </span><span>=</span><span> </span><span>bufferevent_rate_limit_group_new</span><span>(base, cfg);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>55</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>56</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>57</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>58</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>61</div></div><div><span><span>        </span></span><span>base, accept_cb, base,</span></div></div><div><div><div>62</div></div><div><span><span>        </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>63</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server listening on port 8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>66</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total rate limit: 1MB/s for all connections</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>67</div></div><div>
</div></div><div><div><div>68</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>71</div></div><div><span>    </span><span>bufferevent_rate_limit_group_free</span><span>(g_rate_group);</span></div></div><div><div><div>72</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(cfg);</span></div></div><div><div><div>73</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>74</div></div><div>
</div></div><div><div><div>75</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>76</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、动态调整速率<a href="#六动态调整速率"><span>#</span></a></h2><section><h3>6.1 调整API<a href="#61-调整api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取组当前的令牌桶状态 */</span></div></div><div><div><div>2</div></div><div><span>void</span><span> </span><span>bufferevent_rate_limit_group_get_totals</span><span>(</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span><span>grp</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>ev_uint64_t</span><span> </span><span>*</span><span><span>total_read</span><span>,</span><span> </span></span><span>ev_uint64_t</span><span> </span><span>*</span><span><span>total_written</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 设置最小传输速率（保证最低带宽） */</span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>bufferevent_rate_limit_group_set_min_share</span><span>(</span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span><span>grp</span><span>,</span><span> </span></span><span>size_t</span><span><span> </span><span>min_share</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 动态调整示例<a href="#62-动态调整示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span>g_rate_group </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>g_cfg </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>g_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>int</span><span> g_rate_level </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>adjust_rate</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>static</span><span> </span><span>const</span><span> </span><span>size_t</span><span> rates</span><span>[]</span><span> </span><span>=</span><span> {</span><span>102400</span><span>, </span><span>512000</span><span>, </span><span>1048576</span><span>, </span><span>2097152</span><span>};</span><span>  // 100KB, 500KB, 1MB, 2MB</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>g_rate_level </span><span>=</span><span> (g_rate_level </span><span>+</span><span> </span><span>1</span><span>) </span><span>%</span><span> </span><span>4</span><span>;</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// 创建新的配置</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>new_cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>19</div></div><div><span>        </span><span>rates</span><span>[g_rate_level], </span><span>rates</span><span>[g_rate_level],</span></div></div><div><div><div>20</div></div><div><span>        </span><span>rates</span><span>[g_rate_level], </span><span>rates</span><span>[g_rate_level],</span></div></div><div><div><div>21</div></div><div><span>        </span><span>NULL</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>// 更新组的配置</span></div></div><div><div><div>25</div></div><div><span>    </span><span>bufferevent_rate_limit_group_set_cfg</span><span>(g_rate_group, new_cfg);</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Rate adjusted to: </span><span>%zu</span><span> KB/s</span><span>\n</span><span>"</span><span>, </span><span>rates</span><span>[g_rate_level] </span><span>/</span><span> </span><span>1024</span><span>);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(g_cfg);</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>g_cfg </span><span>=</span><span> new_cfg;</span></div></div><div><div><div>31</div></div><div><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>print_stats</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>34</div></div><div><span>    </span><span>ev_uint64_t</span><span> total_read, total_written;</span></div></div><div><div><div>35</div></div><div><span>    </span><span>bufferevent_rate_limit_group_get_totals</span><span>(g_rate_group, </span><span>&amp;</span><span>total_read, </span><span>&amp;</span><span>total_written);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total read: </span><span>%llu</span><span> bytes, written: </span><span>%llu</span><span> bytes</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>38</div></div><div><span><span>           </span></span><span>(</span><span>unsigned</span><span> </span><span>long</span><span> </span><span>long</span><span>)total_read, (</span><span>unsigned</span><span> </span><span>long</span><span> </span><span>long</span><span>)total_written);</span></div></div><div><div><div>39</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、令牌桶原理<a href="#七令牌桶原理"><span>#</span></a></h2><section><h3>7.1 工作原理<a href="#71-工作原理"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>                    </span></span><span>┌─────────────────┐</span></div></div><div><div><div>2</div></div><div><span><span>                    </span></span><span>│   令牌桶        │</span></div></div><div><div><div>3</div></div><div><span><span>                    </span></span><span>│   (Burst)       │</span></div></div><div><div><div>4</div></div><div><span><span>                    </span></span><span>│   容量: B       │</span></div></div><div><div><div>5</div></div><div><span><span>                    </span></span><span>└────────┬────────┘</span></div></div><div><div><div>6</div></div><div><span><span>                             </span></span><span>│</span></div></div><div><div><div>7</div></div><div><span><span>                             </span></span><span>│ 令牌消耗</span></div></div><div><div><div>8</div></div><div><span><span>                             </span></span><span>▼</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>┌──────────────┐    ┌─────────────┐    ┌──────────────┐</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>│  令牌生成    │───▶│  数据传输   │───▶│  数据包发送  │</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>│  速率: R/s   │    │  检查令牌   │    │              │</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>└──────────────┘    └─────────────┘    └──────────────┘</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>R = Rate (速率，字节/秒)</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>B = Burst (突发大小，字节)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>7.2 参数说明<a href="#72-参数说明"><span>#</span></a></h3>

<table><thead><tr><th>参数</th><th>说明</th><th>建议值</th></tr></thead><tbody><tr><td><code>read_rate</code></td><td>平均读取速率</td><td>根据业务需求</td></tr><tr><td><code>read_burst</code></td><td>最大突发读取</td><td>通常等于rate或稍大</td></tr><tr><td><code>write_rate</code></td><td>平均写入速率</td><td>根据业务需求</td></tr><tr><td><code>write_burst</code></td><td>最大突发写入</td><td>通常等于rate或稍大</td></tr><tr><td><code>tick_len</code></td><td>时间片长度</td><td>默认1秒，可设更小</td></tr></tbody></table></section></section><section><h2>八、公平共享<a href="#八公平共享"><span>#</span></a></h2><section><h3>8.1 公平共享机制<a href="#81-公平共享机制"><span>#</span></a></h3><p>当多个连接共享一个速率限制组时，libevent会自动实现公平共享：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span>g_group </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>11</div></div><div><span>    </span><span>int</span><span> id </span><span>=</span><span> </span><span>*</span><span>(</span><span>int</span><span> </span><span>*</span><span>)arg;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, buf, n);</span></div></div><div><div><div>17</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection </span><span>%d</span><span>: read </span><span>%zu</span><span> bytes</span><span>\n</span><span>"</span><span>, id, n);</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>int</span><span> id </span><span>=</span><span> </span><span>*</span><span>(</span><span>int</span><span> </span><span>*</span><span>)arg;</span></div></div><div><div><div>24</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection </span><span>%d</span><span> closed</span><span>\n</span><span>"</span><span>, id);</span></div></div><div><div><div>25</div></div><div><span>        </span><span>free</span><span>(arg);</span></div></div><div><div><div>26</div></div><div><span>        </span><span>bufferevent_remove_from_rate_limit_group</span><span>(bev);</span></div></div><div><div><div>27</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>static</span><span> </span><span>int</span><span> g_conn_id </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>34</div></div><div><span>                      </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>int</span><span> </span><span>*</span><span>id </span><span>=</span><span> </span><span>malloc</span><span>(</span><span>sizeof</span><span>(</span><span>int</span><span>));</span></div></div><div><div><div>38</div></div><div><span>    </span><span>*</span><span>id </span><span>=</span><span> </span><span>++</span><span>g_conn_id;</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>*</span><span>id);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>bufferevent_add_to_rate_limit_group</span><span>(bev, g_group);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, id);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>51</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>// 总带宽限制为1MB/s，所有连接共享</span></div></div><div><div><div>54</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>55</div></div><div><span>        </span><span>1048576</span><span>, </span><span>1048576</span><span>,</span></div></div><div><div><div>56</div></div><div><span>        </span><span>1048576</span><span>, </span><span>1048576</span><span>,</span></div></div><div><div><div>57</div></div><div><span>        </span><span>NULL</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>g_group </span><span>=</span><span> </span><span>bufferevent_rate_limit_group_new</span><span>(base, cfg);</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>    </span><span>// 设置每个连接的最小保证带宽（可选）</span></div></div><div><div><div>63</div></div><div><span>    </span><span>bufferevent_rate_limit_group_set_min_share</span><span>(g_group, </span><span>10240</span><span>);</span><span>  // 10KB/s</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>66</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>67</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>68</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>69</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>72</div></div><div><span><span>        </span></span><span>base, accept_cb, base,</span></div></div><div><div><div>73</div></div><div><span><span>        </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>74</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server listening on port 8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>77</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total bandwidth: 1MB/s, min per connection: 10KB/s</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>82</div></div><div><span>    </span><span>bufferevent_rate_limit_group_free</span><span>(g_group);</span></div></div><div><div><div>83</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(cfg);</span></div></div><div><div><div>84</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>87</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>九、完整示例：带宽控制服务器<a href="#九完整示例带宽控制服务器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>#define</span><span> </span><span>TOTAL_BANDWIDTH</span><span> (</span><span>2</span><span> </span><span>*</span><span> </span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>)</span><span>  // 2MB/s</span></div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>PER_CONN_BANDWIDTH</span><span> (</span><span>100</span><span> </span><span>*</span><span> </span><span>1024</span><span>)</span><span>    // 100KB/s</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>MIN_SHARE</span><span> (</span><span>10</span><span> </span><span>*</span><span> </span><span>1024</span><span>)</span><span>              // 10KB/s</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>g_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent_rate_limit_group </span><span>*</span><span>g_group </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>g_conn_cfg </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>print_stats</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>ev_uint64_t</span><span> total_read, total_written;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>bufferevent_rate_limit_group_get_totals</span><span>(g_group, </span><span>&amp;</span><span>total_read, </span><span>&amp;</span><span>total_written);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>=== Statistics ===</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total read: </span><span>%llu</span><span> bytes (</span><span>%.2f</span><span> MB)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>23</div></div><div><span><span>           </span></span><span>(</span><span>unsigned</span><span> </span><span>long</span><span> </span><span>long</span><span>)total_read, (</span><span>double</span><span>)total_read </span><span>/</span><span> (</span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>));</span></div></div><div><div><div>24</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total written: </span><span>%llu</span><span> bytes (</span><span>%.2f</span><span> MB)</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>25</div></div><div><span><span>           </span></span><span>(</span><span>unsigned</span><span> </span><span>long</span><span> </span><span>long</span><span>)total_written, (</span><span>double</span><span>)total_written </span><span>/</span><span> (</span><span>1024</span><span> </span><span>*</span><span> </span><span>1024</span><span>));</span></div></div><div><div><div>26</div></div><div><span>    </span><span>printf</span><span>(</span><span>"==================</span><span>\n\n</span><span>"</span><span>);</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>30</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>8192</span><span>];</span></div></div><div><div><div>31</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf))) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>34</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, buf, n);</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>36</div></div><div><span>}</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>39</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>40</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Client connected</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>42</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Client disconnected</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>43</div></div><div><span>        </span><span>bufferevent_remove_from_rate_limit_group</span><span>(bev);</span></div></div><div><div><div>44</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>46</div></div><div><span>}</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>49</div></div><div><span>                      </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>// 设置单个连接的速率限制</span></div></div><div><div><div>55</div></div><div><span>    </span><span>bufferevent_set_rate_limit</span><span>(bev, g_conn_cfg);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>// 加入组级别限制</span></div></div><div><div><div>58</div></div><div><span>    </span><span>bufferevent_add_to_rate_limit_group</span><span>(bev, g_group);</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>61</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>62</div></div><div><span>}</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>65</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Shutting down...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>66</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(g_base);</span></div></div><div><div><div>67</div></div><div><span>}</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>70</div></div><div><span><span>    </span></span><span>g_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>    </span><span>// 单连接速率限制配置</span></div></div><div><div><div>73</div></div><div><span><span>    </span></span><span>g_conn_cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>74</div></div><div><span><span>        </span></span><span>PER_CONN_BANDWIDTH, PER_CONN_BANDWIDTH,</span></div></div><div><div><div>75</div></div><div><span><span>        </span></span><span>PER_CONN_BANDWIDTH, PER_CONN_BANDWIDTH,</span></div></div><div><div><div>76</div></div><div><span>        </span><span>NULL</span></div></div><div><div><div>77</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>    </span><span>// 组级别速率限制配置</span></div></div><div><div><div>80</div></div><div><span>    </span><span>struct</span><span> ev_token_bucket_cfg </span><span>*</span><span>group_cfg </span><span>=</span><span> </span><span>ev_token_bucket_cfg_new</span><span>(</span></div></div><div><div><div>81</div></div><div><span><span>        </span></span><span>TOTAL_BANDWIDTH, TOTAL_BANDWIDTH,</span></div></div><div><div><div>82</div></div><div><span><span>        </span></span><span>TOTAL_BANDWIDTH, TOTAL_BANDWIDTH,</span></div></div><div><div><div>83</div></div><div><span>        </span><span>NULL</span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>);</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span><span>    </span></span><span>g_group </span><span>=</span><span> </span><span>bufferevent_rate_limit_group_new</span><span>(g_base, group_cfg);</span></div></div><div><div><div>87</div></div><div><span>    </span><span>bufferevent_rate_limit_group_set_min_share</span><span>(g_group, MIN_SHARE);</span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>    </span><span>// 设置信号处理</span></div></div><div><div><div>90</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigint </span><span>=</span><span> </span><span>evsignal_new</span><span>(g_base, SIGINT, signal_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>91</div></div><div><span>    </span><span>event_add</span><span>(sigint, </span><span>NULL</span><span>);</span></div></div><div><div><div>92</div></div><div>
</div></div><div><div><div>93</div></div><div><span>    </span><span>// 设置定时器打印统计信息</span></div></div><div><div><div>94</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>stats_timer </span><span>=</span><span> </span><span>event_new</span><span>(g_base, </span><span>-</span><span>1</span><span>, EV_PERSIST, print_stats, </span><span>NULL</span><span>);</span></div></div><div><div><div>95</div></div><div><span>    </span><span>struct</span><span> timeval stats_interval </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span><span>  // 每5秒</span></div></div><div><div><div>96</div></div><div><span>    </span><span>event_add</span><span>(stats_timer, </span><span>&amp;</span><span>stats_interval);</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>    </span><span>// 创建监听器</span></div></div><div><div><div>99</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>100</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>101</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>102</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8080</span><span>);</span></div></div><div><div><div>103</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>104</div></div><div>
</div></div><div><div><div>105</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>106</div></div><div><span><span>        </span></span><span>g_base, accept_cb, g_base,</span></div></div><div><div><div>107</div></div><div><span><span>        </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>108</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Bandwidth-limited echo server</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>111</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Listening on port 8080</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>112</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total bandwidth: </span><span>%d</span><span> KB/s</span><span>\n</span><span>"</span><span>, TOTAL_BANDWIDTH </span><span>/</span><span> </span><span>1024</span><span>);</span></div></div><div><div><div>113</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Per connection: </span><span>%d</span><span> KB/s</span><span>\n</span><span>"</span><span>, PER_CONN_BANDWIDTH </span><span>/</span><span> </span><span>1024</span><span>);</span></div></div><div><div><div>114</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Min share: </span><span>%d</span><span> KB/s</span><span>\n</span><span>"</span><span>, MIN_SHARE </span><span>/</span><span> </span><span>1024</span><span>);</span></div></div><div><div><div>115</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to stop</span><span>\n\n</span><span>"</span><span>);</span></div></div><div><div><div>116</div></div><div>
</div></div><div><div><div>117</div></div><div><span>    </span><span>event_base_dispatch</span><span>(g_base);</span></div></div><div><div><div>118</div></div><div>
</div></div><div><div><div>119</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>120</div></div><div><span>    </span><span>event_free</span><span>(stats_timer);</span></div></div><div><div><div>121</div></div><div><span>    </span><span>event_free</span><span>(sigint);</span></div></div><div><div><div>122</div></div><div><span>    </span><span>bufferevent_rate_limit_group_free</span><span>(g_group);</span></div></div><div><div><div>123</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(group_cfg);</span></div></div><div><div><div>124</div></div><div><span>    </span><span>ev_token_bucket_cfg_free</span><span>(g_conn_cfg);</span></div></div><div><div><div>125</div></div><div><span>    </span><span>event_base_free</span><span>(g_base);</span></div></div><div><div><div>126</div></div><div>
</div></div><div><div><div>127</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>128</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十、最佳实践<a href="#十最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>单连接限制</strong></td><td>使用<code>bufferevent_set_rate_limit</code></td></tr><tr><td><strong>全局限制</strong></td><td>使用速率限制组</td></tr><tr><td><strong>突发处理</strong></td><td>设置合理的burst值</td></tr><tr><td><strong>公平分配</strong></td><td>设置min_share保证最小带宽</td></tr><tr><td><strong>动态调整</strong></td><td>根据负载动态调整速率</td></tr><tr><td><strong>监控统计</strong></td><td>定期打印统计信息</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-series-index/</id>
      <title type="text">📚 libevent源码深度解析</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-series-index/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>libevent源码深度解析<a href="#libevent源码深度解析"><span>#</span></a></h1><blockquote><p>Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库，支持跨平台、多种I/O多路复用机制，广泛应用于高性能网络服务器开发。</p></blockquote><section><h2>� libevent整体架构<a href="#-libevent整体架构"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌────────────────────────────────────────────────────────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│                          应用层                                     │</span></div></div><div><div><div>3</div></div><div><span>├────────────────────────────────────────────────────────────────────────┤</span></div></div><div><div><div>4</div></div><div><span>│  ┌───────────┐  ┌───────────┐  ┌───────────┐  ┌───────────┐         │</span></div></div><div><div><div>5</div></div><div><span>│  │ HTTP      │  │ DNS       │  │ 信号处理  │  │ 定时器    │         │</span></div></div><div><div><div>6</div></div><div><span>│  └────┬──────┘  └────┬──────┘  └────┬──────┘  └────┬──────┘         │</span></div></div><div><div><div>7</div></div><div><span>│       │              │              │              │                   │</span></div></div><div><div><div>8</div></div><div><span>├───────┼──────────────┼──────────────┼──────────────┼───────────────────┤</span></div></div><div><div><div>9</div></div><div><span>│                          核心层                                     │</span></div></div><div><div><div>10</div></div><div><span>├────────────────────────────────────────────────────────────────────────┤</span></div></div><div><div><div>11</div></div><div><span>│  ┌────────────────────────────────────────────────────────────────┐   │</span></div></div><div><div><div>12</div></div><div><span>│  │                     event_base                               │   │</span></div></div><div><div><div>13</div></div><div><span>│  ├────────────────────────────────────────────────────────────────┤   │</span></div></div><div><div><div>14</div></div><div><span>│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐         │   │</span></div></div><div><div><div>15</div></div><div><span>│  │  │  event  │  │bufferev│  │ 事件队列 │  │ 定时器堆 │         │   │</span></div></div><div><div><div>16</div></div><div><span>│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘         │   │</span></div></div><div><div><div>17</div></div><div><span>│  └────────────────────────────────────────────────────────────────┘   │</span></div></div><div><div><div>18</div></div><div><span>├────────────────────────────────────────────────────────────────────────┤</span></div></div><div><div><div>19</div></div><div><span>│                          底层驱动                                   │</span></div></div><div><div><div>20</div></div><div><span>├────────────────────────────────────────────────────────────────────────┤</span></div></div><div><div><div>21</div></div><div><span>│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐    │</span></div></div><div><div><div>22</div></div><div><span>│  │  epoll  │  │  kqueue │  │  poll   │  │ select  │  │  IOCP   │    │</span></div></div><div><div><div>23</div></div><div><span>│  └─────────┘  └─────────┘  └─────────┘  └─────────┘  └─────────┘    │</span></div></div><div><div><div>24</div></div><div><span>└────────────────────────────────────────────────────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>🔍 架构说明<a href="#-架构说明"><span>#</span></a></h2><ol>
<li><strong>应用层</strong>：提供HTTP、DNS、信号处理、定时器等高级功能</li>
<li><strong>核心层</strong>：event_base作为核心，管理event、bufferevent、事件队列和定时器堆</li>
<li><strong>底层驱动</strong>：自动选择最优的I/O多路复用机制（epoll、kqueue、poll、select、IOCP）</li>
</ol></section><section><h2>�📚 文章目录<a href="#-文章目录"><span>#</span></a></h2><section><h3>一、基础入门<a href="#一基础入门"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>01</td><td><a href="libevent-introduction.html">序言与简介</a></td><td>高性能服务器、Reactor模型、libevent特点</td></tr><tr><td>02</td><td><a href="libevent-installation.html">安装与编译</a></td><td>libevent下载、编译、安装步骤</td></tr><tr><td>03</td><td><a href="libevent-source-organization.html">源码文件组织</a></td><td>源码目录结构、文件分类说明</td></tr><tr><td>04</td><td><a href="libevent-core-concepts.html">核心概念与API</a></td><td>事件驱动模型、核心概念介绍</td></tr></tbody></table></section><section><h3>二、核心组件<a href="#二核心组件"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>05</td><td><a href="libevent-event-base.html">event_base详解</a></td><td>事件基结构体、API、使用方法</td></tr><tr><td>06</td><td><a href="libevent-event.html">event详解</a></td><td>事件结构体、API、使用方法</td></tr><tr><td>07</td><td><a href="libevent-bufferevent.html">bufferevent详解</a></td><td>缓冲事件、水位机制、API</td></tr><tr><td>08</td><td><a href="libevent-logging.html">日志模块</a></td><td>日志级别、回调设置、调试方法</td></tr></tbody></table></section><section><h3>三、网络编程<a href="#三网络编程"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>09</td><td><a href="libevent-tcp-listener.html">TCP监听器</a></td><td>evconnlistener使用、TCP服务端</td></tr><tr><td>10</td><td><a href="libevent-tcp-server-client.html">TCP服务端与客户端</a></td><td>完整的TCP回射程序示例</td></tr><tr><td>11</td><td><a href="libevent-http-server.html">HTTP服务器</a></td><td>HTTP服务端实现、请求处理</td></tr></tbody></table></section><section><h3>四、事件类型<a href="#四事件类型"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>12</td><td><a href="libevent-timer-usage.html">定时器使用</a></td><td>定时器事件创建与使用</td></tr><tr><td>13</td><td><a href="libevent-signal-events.html">信号事件</a></td><td>信号事件处理</td></tr></tbody></table></section><section><h3>五、高级特性<a href="#五高级特性"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>14</td><td><a href="libevent-multi-threaded.html">多线程编程</a></td><td>多线程使用原则、HTTP服务器C++封装</td></tr><tr><td>15</td><td><a href="libevent-event-flow.html">事件触发流程</a></td><td>事件循环内部流程分析</td></tr><tr><td>16</td><td><a href="libevent-watch-module.html">watch模块</a></td><td>事件循环监控、prepare/check回调</td></tr><tr><td>17</td><td><a href="libevent-event-count.html">事件数量分析</a></td><td>虚拟事件、事件计数</td></tr></tbody></table></section><section><h3>六、扩展模块<a href="#六扩展模块"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>18</td><td><a href="libevent-evbuffer.html">evbuffer详解</a></td><td>缓冲区操作完整API、零拷贝、回调机制</td></tr><tr><td>19</td><td><a href="libevent-evdns.html">evdns异步DNS</a></td><td>DNS查询与服务器、正向/反向解析</td></tr><tr><td>20</td><td><a href="libevent-evutil.html">evutil工具函数</a></td><td>跨平台工具函数、Socket/地址/时间操作</td></tr><tr><td>21</td><td><a href="libevent-http-client.html">HTTP客户端</a></td><td>evhttp客户端编程、GET/POST请求</td></tr></tbody></table></section><section><h3>七、安全与优化<a href="#七安全与优化"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>22</td><td><a href="libevent-ssl-tls.html">SSL/TLS加密通信</a></td><td>安全通信实现、OpenSSL集成</td></tr><tr><td>23</td><td><a href="libevent-rate-limiting.html">速率限制</a></td><td>带宽控制、令牌桶算法</td></tr><tr><td>24</td><td><a href="libevent-bufferevent-advanced.html">bufferevent高级特性</a></td><td>pair/filter/链式过滤器</td></tr><tr><td>25</td><td><a href="libevent-performance-optimization.html">性能调优指南</a></td><td>生产环境优化、多线程扩展</td></tr></tbody></table></section><section><h3>八、问题排查<a href="#八问题排查"><span>#</span></a></h3>

<table><thead><tr><th>序号</th><th>文章</th><th>说明</th></tr></thead><tbody><tr><td>26</td><td><a href="libevent-faq.html">常见问题FAQ</a></td><td>编译安装、事件循环、网络问题排查</td></tr></tbody></table><hr /></section></section><section><h2>🔗 参考资料<a href="#-参考资料"><span>#</span></a></h2><ul>
<li><a href="https://libevent.org/" target="_blank">libevent官方网站</a></li>
<li><a href="https://github.com/libevent/libevent" target="_blank">libevent源码</a></li>
<li><a href="http://www.kegel.com/c10k.html" target="_blank">The C10K Problem</a></li>
</ul><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-signal-events/</id>
      <title type="text">📡 信号事件 - libevent源码深度解析系列（十三）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-signal-events/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📡 信号事件<a href="#-信号事件"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent支持信号事件处理，允许在接收到特定信号时执行回调函数。这对于优雅地关闭服务器、重新加载配置等场景非常有用。</p></section><section><h2>二、信号API<a href="#二信号api"><span>#</span></a></h2><section><h3>2.1 创建信号事件<a href="#21-创建信号事件"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建信号事件（宏定义）</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base  event_base</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param x     信号值</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param cb    回调函数</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param arg   回调参数</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>evsignal_new</span><span><span>(</span><span>base</span><span>, </span><span>x</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>\</span></div></div><div><div><div>8</div></div><div><span>    </span><span>event_new</span><span><span>(</span><span>(</span><span>base</span><span>),</span><span> </span><span>(</span><span>x</span><span>),</span><span> EV_SIGNAL</span></span><span>|</span><span><span>EV_PERSIST</span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>/* 分配信号事件到现有结构体 */</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>evsignal_assign</span><span><span>(</span><span>ev</span><span>, </span><span>base</span><span>, </span><span>x</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>\</span></div></div><div><div><div>12</div></div><div><span>    </span><span>event_assign</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>base</span><span>),</span><span> </span><span>(</span><span>x</span><span>),</span><span> EV_SIGNAL</span></span><span>|</span><span><span>EV_PERSIST</span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 添加和删除<a href="#22-添加和删除"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加信号事件 */</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>evsignal_add</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>) </span></span><span>event_add</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 删除信号事件 */</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>evsignal_del</span><span><span>(</span><span>ev</span><span>) </span></span><span>event_del</span><span>(ev)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.3 检查状态<a href="#23-检查状态"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 检查信号事件是否pending */</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>evsignal_pending</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>) </span></span><span>event_pending</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> EV_SIGNAL</span><span>,</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 检查信号事件是否已初始化 */</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>evsignal_initialized</span><span><span>(</span><span>ev</span><span>) </span></span><span>event_initialized</span><span>(ev)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、常用信号<a href="#三常用信号"><span>#</span></a></h2>

<table><thead><tr><th>信号</th><th>值</th><th>说明</th></tr></thead><tbody><tr><td>SIGINT</td><td>2</td><td>中断信号（Ctrl+C）</td></tr><tr><td>SIGTERM</td><td>15</td><td>终止信号</td></tr><tr><td>SIGUSR1</td><td>10</td><td>用户自定义信号1</td></tr><tr><td>SIGUSR2</td><td>12</td><td>用户自定义信号2</td></tr><tr><td>SIGHUP</td><td>1</td><td>挂起信号（重载配置）</td></tr><tr><td>SIGPIPE</td><td>13</td><td>管道破裂（通常忽略）</td></tr></tbody></table></section><section><h2>四、基本示例<a href="#四基本示例"><span>#</span></a></h2><section><h3>4.1 捕获SIGINT<a href="#41-捕获sigint"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught SIGINT (Ctrl+C), shutting down...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>// 创建信号事件</span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>signal_int </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, base);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_add</span><span>(signal_int, </span><span>NULL</span><span>);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to exit</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>event_free</span><span>(signal_int);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 捕获多个信号<a href="#42-捕获多个信号"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>volatile</span><span> </span><span>int</span><span> running </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sigint_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught SIGINT, graceful shutdown...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sigterm_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught SIGTERM, immediate shutdown...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(base);</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sighup_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught SIGHUP, reloading configuration...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>// 这里可以重新加载配置</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sigusr1_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught SIGUSR1, custom action...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>// 自定义操作</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>// 创建多个信号事件</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigint </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, sigint_cb, base);</span></div></div><div><div><div>34</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigterm </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGTERM, sigterm_cb, base);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sighup </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGHUP, sighup_cb, base);</span></div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigusr1 </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGUSR1, sigusr1_cb, base);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>event_add</span><span>(sigint, </span><span>NULL</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>event_add</span><span>(sigterm, </span><span>NULL</span><span>);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>event_add</span><span>(sighup, </span><span>NULL</span><span>);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>event_add</span><span>(sigusr1, </span><span>NULL</span><span>);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Signal handlers installed:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  SIGINT (Ctrl+C)  - Graceful shutdown</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  SIGTERM          - Immediate shutdown</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  SIGHUP           - Reload config</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  SIGUSR1          - Custom action</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>event_free</span><span>(sigint);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>event_free</span><span>(sigterm);</span></div></div><div><div><div>53</div></div><div><span>    </span><span>event_free</span><span>(sighup);</span></div></div><div><div><div>54</div></div><div><span>    </span><span>event_free</span><span>(sigusr1);</span></div></div><div><div><div>55</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>58</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、忽略SIGPIPE<a href="#五忽略sigpipe"><span>#</span></a></h2><p>在网络编程中，当向已关闭的socket写入数据时，会触发SIGPIPE信号，默认行为是终止程序。通常需要忽略这个信号：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>ignore_sigpipe</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>7</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigpipe </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGPIPE, </span><span>NULL</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>event_add</span><span>(sigpipe, </span><span>NULL</span><span>);</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>// 或者直接使用系统调用</span></div></div><div><div><div>11</div></div><div><span>    </span><span>signal</span><span>(SIGPIPE, SIG_IGN);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>event_free</span><span>(sigpipe);</span></div></div><div><div><div>14</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>// 更简单的方法</span></div></div><div><div><div>18</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>setup_signals</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>    </span><span>// 忽略SIGPIPE</span></div></div><div><div><div>20</div></div><div><span>    </span><span>signal</span><span>(SIGPIPE, SIG_IGN);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>六、完整服务器示例<a href="#六完整服务器示例"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>PORT</span><span> </span><span>8080</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>g_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>int</span><span> g_reload_config </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>20</div></div><div><span>    </span><span>evbuffer_add_buffer</span><span>(output, input);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>24</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>30</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>36</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>37</div></div><div><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sigint_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>40</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Caught SIGINT, shutting down gracefully...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>41</div></div><div><span>    </span><span>event_base_loopexit</span><span>(g_base, </span><span>NULL</span><span>);</span></div></div><div><div><div>42</div></div><div><span>}</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sigterm_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>45</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Caught SIGTERM, shutting down...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>event_base_loopexit</span><span>(g_base, </span><span>NULL</span><span>);</span></div></div><div><div><div>47</div></div><div><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>sighup_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>50</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Caught SIGHUP, reloading configuration...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>g_reload_config </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>52</div></div><div><span>    </span><span>// 在实际应用中，这里可以重新读取配置文件</span></div></div><div><div><div>53</div></div><div><span>}</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>56</div></div><div><span>    </span><span>// 忽略SIGPIPE</span></div></div><div><div><div>57</div></div><div><span>    </span><span>signal</span><span>(SIGPIPE, SIG_IGN);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>g_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>// 设置信号处理</span></div></div><div><div><div>62</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigint </span><span>=</span><span> </span><span>evsignal_new</span><span>(g_base, SIGINT, sigint_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigterm </span><span>=</span><span> </span><span>evsignal_new</span><span>(g_base, SIGTERM, sigterm_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>64</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sighup </span><span>=</span><span> </span><span>evsignal_new</span><span>(g_base, SIGHUP, sighup_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>event_add</span><span>(sigint, </span><span>NULL</span><span>);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>event_add</span><span>(sigterm, </span><span>NULL</span><span>);</span></div></div><div><div><div>68</div></div><div><span>    </span><span>event_add</span><span>(sighup, </span><span>NULL</span><span>);</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>    </span><span>// 创建监听器</span></div></div><div><div><div>71</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>72</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>73</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>74</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(PORT);</span></div></div><div><div><div>75</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>76</div></div><div>
</div></div><div><div><div>77</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(g_base,</span></div></div><div><div><div>78</div></div><div><span><span>        </span></span><span>accept_cb, </span><span>NULL</span><span>, LEV_OPT_REUSEABLE </span><span>|</span><span> LEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>79</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>listener) {</span></div></div><div><div><div>82</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create listener</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>83</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server running on port </span><span>%d</span><span>\n</span><span>"</span><span>, PORT);</span></div></div><div><div><div>87</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to shutdown</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>88</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Send SIGHUP to reload config (kill -HUP </span><span>%d</span><span>)</span><span>\n</span><span>"</span><span>, </span><span>getpid</span><span>());</span></div></div><div><div><div>89</div></div><div>
</div></div><div><div><div>90</div></div><div><span>    </span><span>event_base_dispatch</span><span>(g_base);</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Cleaning up...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>95</div></div><div><span>    </span><span>event_free</span><span>(sigint);</span></div></div><div><div><div>96</div></div><div><span>    </span><span>event_free</span><span>(sigterm);</span></div></div><div><div><div>97</div></div><div><span>    </span><span>event_free</span><span>(sighup);</span></div></div><div><div><div>98</div></div><div><span>    </span><span>event_base_free</span><span>(g_base);</span></div></div><div><div><div>99</div></div><div>
</div></div><div><div><div>100</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>101</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>102</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>七、使用注意事项<a href="#七使用注意事项"><span>#</span></a></h2>

<table><thead><tr><th>注意事项</th><th>说明</th></tr></thead><tbody><tr><td><strong>单event_base限制</strong></td><td>每个进程只能有一个event_base监控信号</td></tr><tr><td><strong>信号自动恢复</strong></td><td>libevent会自动恢复信号处理</td></tr><tr><td><strong>EV_PERSIST</strong></td><td>信号事件通常需要EV_PERSIST标志</td></tr><tr><td><strong>SIGPIPE</strong></td><td>网络编程中通常需要忽略SIGPIPE</td></tr><tr><td><strong>线程安全</strong></td><td>信号处理函数中避免调用非异步安全函数</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-source-organization/</id>
      <title type="text">📁 源码文件组织 - libevent源码深度解析系列（三）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-source-organization/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📁 源码文件组织<a href="#-源码文件组织"><span>#</span></a></h1><section><h2>一、源码目录结构<a href="#一源码目录结构"><span>#</span></a></h2><p><code>libevent</code> 源码目录结构如下所示：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>libevent/</span></div></div><div><div><div>2</div></div><div><span>├── include/                # 包含头文件</span></div></div><div><div><div>3</div></div><div><span>│   ├── event2/</span></div></div><div><div><div>4</div></div><div><span>│   │   ├── event.h         # 核心API头文件</span></div></div><div><div><div>5</div></div><div><span>│   │   ├── event-config.h  # 配置宏定义</span></div></div><div><div><div>6</div></div><div><span>│   │   ├── bufferevent.h   # 缓冲事件</span></div></div><div><div><div>7</div></div><div><span>│   │   ├── buffer.h        # 缓冲区</span></div></div><div><div><div>8</div></div><div><span>│   │   ├── http.h          # HTTP支持</span></div></div><div><div><div>9</div></div><div><span>│   │   ├── dns.h           # DNS支持</span></div></div><div><div><div>10</div></div><div><span>│   │   ├── rpc.h           # RPC支持</span></div></div><div><div><div>11</div></div><div><span>│   │   └── util.h          # 工具函数</span></div></div><div><div><div>12</div></div><div><span>│   └── event.h             # 旧版本兼容头文件</span></div></div><div><div><div>13</div></div><div><span>├── src/                    # 源代码文件</span></div></div><div><div><div>14</div></div><div><span>│   ├── event.c             # 事件核心实现</span></div></div><div><div><div>15</div></div><div><span>│   ├── evmap.c             # 事件映射</span></div></div><div><div><div>16</div></div><div><span>│   ├── evthread.c          # 线程支持</span></div></div><div><div><div>17</div></div><div><span>│   ├── evutil.c            # 工具函数</span></div></div><div><div><div>18</div></div><div><span>│   ├── buffer.c            # 缓冲区实现</span></div></div><div><div><div>19</div></div><div><span>│   ├── bufferevent.c       # 缓冲事件实现</span></div></div><div><div><div>20</div></div><div><span>│   ├── http.c              # HTTP实现</span></div></div><div><div><div>21</div></div><div><span>│   ├── evdns.c             # DNS实现</span></div></div><div><div><div>22</div></div><div><span>│   ├── evrpc.c             # RPC实现</span></div></div><div><div><div>23</div></div><div><span>│   └── ...</span></div></div><div><div><div>24</div></div><div><span>├── test/                   # 测试代码</span></div></div><div><div><div>25</div></div><div><span>├── sample/                 # 示例代码</span></div></div><div><div><div>26</div></div><div><span>├── configure               # 自动配置脚本</span></div></div><div><div><div>27</div></div><div><span>├── Makefile                # 编译规则</span></div></div><div><div><div>28</div></div><div><span>└── README                  # 项目说明文档</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>二、头文件分类<a href="#二头文件分类"><span>#</span></a></h2><p>新版本使用的头文件是在 <code>include/event2</code> 文件夹中，但是旧版本的头文件仍然存在。</p>

<table><thead><tr><th>新版本头文件</th><th>旧版本头文件</th></tr></thead><tbody><tr><td>event2/event*.h, event2/buffer*.h, event2/bufferevent*.h, event2/tag*.h</td><td>event.h</td></tr><tr><td>event2/dns*.h</td><td>evdns.h</td></tr><tr><td>event2/rpc*.h</td><td>evrpc.h</td></tr><tr><td>event2/http*.h</td><td>evhttp.h</td></tr><tr><td>event2/util*.h</td><td>evutil.h</td></tr></tbody></table></section><section><h2>三、源码文件分类<a href="#三源码文件分类"><span>#</span></a></h2><section><h3>3.1 I/O多路复用机制封装<a href="#31-io多路复用机制封装"><span>#</span></a></h3>

<table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td>epoll.c</td><td>对linux I/O复用机制 epoll 的封装</td></tr><tr><td>select.c</td><td>对 linux I/O复用机制 select 的封装</td></tr><tr><td>poll.c</td><td>对 linux I/O复用机制 poll 的封装</td></tr><tr><td>devpoll.c</td><td>对 dev/poll 的封装</td></tr><tr><td>kqueue.c</td><td>对 kqueue 的封装</td></tr><tr><td>evport.c</td><td>Solaris 10 的新增加的特性</td></tr></tbody></table></section><section><h3>3.2 基本数据结构<a href="#32-基本数据结构"><span>#</span></a></h3><p><code>compat/sys</code> 下的两个源文件：<code>queue.h</code> 是 libevent 基本数据结构的实现，包括链表，双向链表，队列等。</p></section><section><h3>3.3 信号管理<a href="#33-信号管理"><span>#</span></a></h3><p><code>signal.c</code>：对信号事件的处理。</p></section><section><h3>3.4 辅助功能函数<a href="#34-辅助功能函数"><span>#</span></a></h3>

<table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td>evutil.c</td><td>一些辅助功能函数，包括创建 socket pair 和一些时间操作函数</td></tr><tr><td>arc4random.c, evutil_rand.c</td><td>随机数的封装</td></tr><tr><td>evutil_time.c</td><td>时间函数的封装</td></tr></tbody></table></section><section><h3>3.5 日志<a href="#35-日志"><span>#</span></a></h3><p><code>log.h</code> 和 <code>log.c</code>：log 日志函数。</p></section><section><h3>3.6 线程<a href="#36-线程"><span>#</span></a></h3><p><code>evthread.c</code>、<code>evthread_pthread.c</code>、<code>evthread_win32.c</code>：对系统的线程使用的封装。</p></section><section><h3>3.7 事件的实现与管理<a href="#37-事件的实现与管理"><span>#</span></a></h3>

<table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td>event.c</td><td>event 整体框架的代码实现，包含event和event_base</td></tr><tr><td>evmap.c</td><td>将每个文件描述符或者信号映射为事件，私有函数</td></tr></tbody></table></section><section><h3>3.8 缓冲区管理<a href="#38-缓冲区管理"><span>#</span></a></h3><p><code>bufferevent.c</code>、<code>bufferevent_async.c</code>、<code>bufferevent_filter.c</code>、<code>bufferevent_openssl.c</code>、<code>bufferevent_pair.c</code>、<code>bufferevent_ratelim.c</code>、<code>bufferevent_sock.c</code> 和 <code>buffer.c</code>：libevent 对缓冲区的封装。</p></section><section><h3>3.9 定时事件管理<a href="#39-定时事件管理"><span>#</span></a></h3><p><code>min-heap.h</code>：其实就是一个以时间作为 key 的小根堆结构。</p></section><section><h3>3.10 网络库<a href="#310-网络库"><span>#</span></a></h3>

<table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td>evdns.c</td><td>异步 dns 查询库</td></tr><tr><td>http.c</td><td>http 服务器的实现</td></tr><tr><td>evrpc.c</td><td>rpc的实现</td></tr></tbody></table></section></section><section><h2>四、官方示例<a href="#四官方示例"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sample/</span></div></div><div><div><div>2</div></div><div><span>├── dns-example.c</span></div></div><div><div><div>3</div></div><div><span>├── event-read-fifo.c</span></div></div><div><div><div>4</div></div><div><span>├── hello-world.c</span></div></div><div><div><div>5</div></div><div><span>├── hostcheck.c</span></div></div><div><div><div>6</div></div><div><span>├── hostcheck.h</span></div></div><div><div><div>7</div></div><div><span>├── http-connect.c</span></div></div><div><div><div>8</div></div><div><span>├── https-client.c</span></div></div><div><div><div>9</div></div><div><span>├── http-server.c</span></div></div><div><div><div>10</div></div><div><span>├── le-proxy.c</span></div></div><div><div><div>11</div></div><div><span>├── openssl_hostname_validation.c</span></div></div><div><div><div>12</div></div><div><span>├── openssl_hostname_validation.h</span></div></div><div><div><div>13</div></div><div><span>├── signal-test.c</span></div></div><div><div><div>14</div></div><div><span>└── time-test.c</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>五、模块划分<a href="#五模块划分"><span>#</span></a></h2><section><h3>5.1 事件基 (event_base)<a href="#51-事件基-event_base"><span>#</span></a></h3><p>事件基是 <code>libevent</code> 的核心组件，它负责管理事件的调度。<code>event_base</code> 提供了事件的注册、取消、启动事件循环等功能。</p></section><section><h3>5.2 事件 (event)<a href="#52-事件-event"><span>#</span></a></h3><p>事件代表了一个可以被监控的对象，如文件描述符或信号。事件可以设置为监听读、写、定时器等不同的事件类型，并且可以指定事件触发时的回调函数。</p></section><section><h3>5.3 缓冲区 (evbuffer)<a href="#53-缓冲区-evbuffer"><span>#</span></a></h3><p><code>evbuffer</code> 是一个灵活的缓冲区管理工具，用于在网络通信中处理数据流。</p></section><section><h3>5.4 线程安全 (evthread)<a href="#54-线程安全-evthread"><span>#</span></a></h3><p>提供了线程安全的支持，使得 <code>libevent</code> 可以在多线程环境中安全使用。</p></section><section><h3>5.5 I/O 多路复用机制封装<a href="#55-io-多路复用机制封装"><span>#</span></a></h3><p><code>libevent</code> 支持多种 I/O 多路复用机制，如 epoll、kqueue、select 等。这些机制在不同操作系统上的实现略有不同，因此 <code>libevent</code> 通过条件编译选择最适合当前操作系统的实现。</p></section></section><section><h2>六、跨平台兼容性<a href="#六跨平台兼容性"><span>#</span></a></h2><p><code>libevent</code> 的设计考虑到了跨平台兼容性，因此源码中包含了许多条件编译指令来适应不同的操作系统和编译器特性。例如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#if</span><span> </span><span>defined</span><span>(</span><span>EVENT__HAVE_EPOLL</span><span>)</span></div></div><div><div><div>2</div></div><div><span>// 使用 epoll 的实现</span></div></div><div><div><div>3</div></div><div><span>#elif</span><span> </span><span>defined</span><span>(</span><span>EVENT__HAVE_KQUEUE</span><span>)</span></div></div><div><div><div>4</div></div><div><span>// 使用 kqueue 的实现</span></div></div><div><div><div>5</div></div><div><span>#else</span></div></div><div><div><div>6</div></div><div><span>// 使用 select 或其他机制的实现</span></div></div><div><div><div>7</div></div><div><span>#endif</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这种条件编译允许 libevent 在不同的平台上选择最适合的事件驱动机制。</p><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-ssl-tls/</id>
      <title type="text">🔐 SSL/TLS加密通信 - libevent源码深度解析系列（二十二）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-ssl-tls/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔐 SSL/TLS加密通信<a href="#-ssltls加密通信"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent通过OpenSSL库提供SSL/TLS加密通信支持。通过bufferevent_openssl模块，可以轻松地为网络连接添加安全层，实现数据加密传输。</p></section><section><h2>二、SSL/TLS基础<a href="#二ssltls基础"><span>#</span></a></h2><section><h3>2.1 SSL/TLS协议版本<a href="#21-ssltls协议版本"><span>#</span></a></h3>

<table><thead><tr><th>版本</th><th>说明</th><th>安全性</th></tr></thead><tbody><tr><td>SSLv2</td><td>已废弃</td><td>不安全</td></tr><tr><td>SSLv3</td><td>已废弃</td><td>POODLE攻击</td></tr><tr><td>TLSv1.0</td><td>已弃用</td><td>BEAST攻击</td></tr><tr><td>TLSv1.1</td><td>已弃用</td><td>较弱</td></tr><tr><td>TLSv1.2</td><td>推荐</td><td>安全</td></tr><tr><td>TLSv1.3</td><td>最新</td><td>最安全</td></tr></tbody></table></section><section><h3>2.2 常用加密套件<a href="#22-常用加密套件"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</span></div></div><div><div><div>2</div></div><div><span>TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384</span></div></div><div><div><div>3</div></div><div><span>TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256</span></div></div><div><div><div>4</div></div><div><span>TLS_AES_128_GCM_SHA256 (TLS 1.3)</span></div></div><div><div><div>5</div></div><div><span>TLS_AES_256_GCM_SHA384 (TLS 1.3)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、OpenSSL初始化<a href="#三openssl初始化"><span>#</span></a></h2><section><h3>3.1 初始化API<a href="#31-初始化api"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;openssl/err.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;openssl/rand.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 初始化OpenSSL库 */</span></div></div><div><div><div>6</div></div><div><span>void</span><span> </span><span>SSL_library_init</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 加载错误字符串 */</span></div></div><div><div><div>9</div></div><div><span>void</span><span> </span><span>SSL_load_error_strings</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 加载算法 */</span></div></div><div><div><div>12</div></div><div><span>void</span><span> </span><span>OpenSSL_add_all_algorithms</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 清理资源 */</span></div></div><div><div><div>15</div></div><div><span>void</span><span> </span><span>EVP_cleanup</span><span>(</span><span>void</span><span>);</span></div></div><div><div><div>16</div></div><div><span>void</span><span> </span><span>ERR_free_strings</span><span>(</span><span>void</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3.2 初始化示例<a href="#32-初始化示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;openssl/err.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>init_openssl</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>SSL_library_init</span><span>();</span></div></div><div><div><div>7</div></div><div><span>    </span><span>SSL_load_error_strings</span><span>();</span></div></div><div><div><div>8</div></div><div><span>    </span><span>OpenSSL_add_all_algorithms</span><span>();</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>printf</span><span>(</span><span>"OpenSSL initialized, version: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>           </span><span>SSLeay_version</span><span>(SSLEAY_VERSION));</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>void</span><span> </span><span>cleanup_openssl</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>EVP_cleanup</span><span>();</span></div></div><div><div><div>16</div></div><div><span>    </span><span>ERR_free_strings</span><span>();</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>init_openssl</span><span>();</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>printf</span><span>(</span><span>"SSL/TLS support ready</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>cleanup_openssl</span><span>();</span></div></div><div><div><div>25</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、SSL上下文<a href="#四ssl上下文"><span>#</span></a></h2><section><h3>4.1 创建SSL_CTX<a href="#41-创建ssl_ctx"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建SSL上下文 */</span></div></div><div><div><div>2</div></div><div><span>SSL_CTX </span><span>*</span><span>SSL_CTX_new</span><span>(</span><span>const</span><span> SSL_METHOD </span><span>*</span><span><span>method</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 释放SSL上下文 */</span></div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>SSL_CTX_free</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>);</span></span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>/* 获取TLS方法 */</span></div></div><div><div><div>8</div></div><div><span>const</span><span> SSL_METHOD </span><span>*</span><span>TLS_server_method</span><span>(</span><span>void</span><span>);</span><span>    // 服务器端</span></div></div><div><div><div>9</div></div><div><span>const</span><span> SSL_METHOD </span><span>*</span><span>TLS_client_method</span><span>(</span><span>void</span><span>);</span><span>    // 客户端</span></div></div><div><div><div>10</div></div><div><span>const</span><span> SSL_METHOD </span><span>*</span><span>TLS_method</span><span>(</span><span>void</span><span>);</span><span>           // 通用（推荐）</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>/* 设置协议版本 */</span></div></div><div><div><div>13</div></div><div><span>int</span><span> </span><span>SSL_CTX_set_min_proto_version</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>version</span><span>);</span></span></div></div><div><div><div>14</div></div><div><span>int</span><span> </span><span>SSL_CTX_set_max_proto_version</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>version</span><span>);</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/* 协议版本常量 */</span></div></div><div><div><div>17</div></div><div><span>#define</span><span> </span><span>TLS1_VERSION</span><span>       </span><span><span>0x</span><span>0301</span></span></div></div><div><div><div>18</div></div><div><span>#define</span><span> </span><span>TLS1_1_VERSION</span><span>     </span><span><span>0x</span><span>0302</span></span></div></div><div><div><div>19</div></div><div><span>#define</span><span> </span><span>TLS1_2_VERSION</span><span>     </span><span><span>0x</span><span>0303</span></span></div></div><div><div><div>20</div></div><div><span>#define</span><span> </span><span>TLS1_3_VERSION</span><span>     </span><span><span>0x</span><span>0304</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 配置SSL_CTX<a href="#42-配置ssl_ctx"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 设置证书 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>SSL_CTX_use_certificate_file</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>file</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>type</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>int</span><span> </span><span>SSL_CTX_use_certificate_chain_file</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>file</span><span>);</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>/* 设置私钥 */</span></div></div><div><div><div>6</div></div><div><span>int</span><span> </span><span>SSL_CTX_use_PrivateKey_file</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>file</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>type</span><span>);</span></span></div></div><div><div><div>7</div></div><div><span>int</span><span> </span><span>SSL_CTX_check_private_key</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>);</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>/* 设置CA证书 */</span></div></div><div><div><div>10</div></div><div><span>int</span><span> </span><span>SSL_CTX_load_verify_locations</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>CAfile</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span>                                   </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>CApath</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>/* 设置验证模式 */</span></div></div><div><div><div>14</div></div><div><span>void</span><span> </span><span>SSL_CTX_set_verify</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>mode</span><span>,</span></span></div></div><div><div><div>15</div></div><div><span>                        </span><span>int</span><span><span> </span><span>(</span></span><span>*</span><span><span>verify_callback</span><span>)(</span></span><span>int</span><span><span>,</span><span> X509_STORE_CTX </span></span><span>*</span><span>));</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>/* 验证模式 */</span></div></div><div><div><div>18</div></div><div><span>#define</span><span> </span><span>SSL_VERIFY_NONE</span><span>                 </span><span><span>0x</span><span>00</span></span></div></div><div><div><div>19</div></div><div><span>#define</span><span> </span><span>SSL_VERIFY_PEER</span><span>                 </span><span><span>0x</span><span>01</span></span></div></div><div><div><div>20</div></div><div><span>#define</span><span> </span><span>SSL_VERIFY_FAIL_IF_NO_PEER_CERT</span><span> </span><span><span>0x</span><span>02</span></span></div></div><div><div><div>21</div></div><div><span>#define</span><span> </span><span>SSL_VERIFY_CLIENT_ONCE</span><span>          </span><span><span>0x</span><span>04</span></span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>/* 设置验证深度 */</span></div></div><div><div><div>24</div></div><div><span>void</span><span> </span><span>SSL_CTX_set_verify_depth</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>depth</span><span>);</span></span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>/* 设置密码套件 */</span></div></div><div><div><div>27</div></div><div><span>int</span><span> </span><span>SSL_CTX_set_cipher_list</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str</span><span>);</span></span></div></div><div><div><div>28</div></div><div><span>int</span><span> </span><span>SSL_CTX_set_ciphersuites</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>str</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.3 SSL_CTX示例<a href="#43-ssl_ctx示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>SSL_CTX </span><span>*</span><span>create_server_context</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>TLS_server_method</span><span>());</span></div></div><div><div><div>6</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ctx) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>perror</span><span>(</span><span>"SSL_CTX_new"</span><span>);</span></div></div><div><div><div>8</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 设置最低TLS版本为1.2</span></div></div><div><div><div>12</div></div><div><span>    </span><span>SSL_CTX_set_min_proto_version</span><span>(ctx, TLS1_2_VERSION);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>// 加载证书</span></div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_use_certificate_file</span><span>(ctx, </span><span>"server.crt"</span><span>, SSL_FILETYPE_PEM) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load certificate</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>17</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>18</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 加载私钥</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_use_PrivateKey_file</span><span>(ctx, </span><span>"server.key"</span><span>, SSL_FILETYPE_PEM) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load private key</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>24</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>25</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>// 验证私钥</span></div></div><div><div><div>29</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_check_private_key</span><span>(ctx) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>30</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Private key check failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>31</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>32</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>// 设置密码套件</span></div></div><div><div><div>36</div></div><div><span>    </span><span>SSL_CTX_set_cipher_list</span><span>(ctx,</span></div></div><div><div><div>37</div></div><div><span>        </span><span>"ECDHE-ECDSA-AES128-GCM-SHA256:"</span></div></div><div><div><div>38</div></div><div><span>        </span><span>"ECDHE-RSA-AES128-GCM-SHA256:"</span></div></div><div><div><div>39</div></div><div><span>        </span><span>"ECDHE-ECDSA-AES256-GCM-SHA384:"</span></div></div><div><div><div>40</div></div><div><span>        </span><span>"ECDHE-RSA-AES256-GCM-SHA384"</span><span>);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server SSL context created</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>return</span><span> ctx;</span></div></div><div><div><div>44</div></div><div><span>}</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>SSL_CTX </span><span>*</span><span>create_client_context</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>TLS_client_method</span><span>());</span></div></div><div><div><div>48</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ctx) {</span></div></div><div><div><div>49</div></div><div><span>        </span><span>perror</span><span>(</span><span>"SSL_CTX_new"</span><span>);</span></div></div><div><div><div>50</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>// 设置最低TLS版本</span></div></div><div><div><div>54</div></div><div><span>    </span><span>SSL_CTX_set_min_proto_version</span><span>(ctx, TLS1_2_VERSION);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>// 设置验证模式</span></div></div><div><div><div>57</div></div><div><span>    </span><span>SSL_CTX_set_verify</span><span>(ctx, SSL_VERIFY_PEER, </span><span>NULL</span><span>);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>// 加载CA证书</span></div></div><div><div><div>60</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_load_verify_locations</span><span>(ctx, </span><span>"ca.crt"</span><span>, </span><span>NULL</span><span>) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>61</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load CA certificate</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>62</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>63</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>64</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Client SSL context created</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>return</span><span> ctx;</span></div></div><div><div><div>68</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、bufferevent_openssl<a href="#五bufferevent_openssl"><span>#</span></a></h2><section><h3>5.1 创建SSL bufferevent<a href="#51-创建ssl-bufferevent"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent_ssl.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>/* 创建SSL bufferevent（基于socket） */</span></div></div><div><div><div>4</div></div><div><span>struct</span><span> bufferevent </span><span>*</span><span>bufferevent_openssl_socket_new</span><span>(</span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>SSL </span><span>*</span><span><span>ssl</span><span>,</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>enum</span><span><span> bufferevent_ssl_state </span><span>state</span><span>,</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>int</span><span><span> </span><span>options</span><span>);</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>/* 创建SSL bufferevent（基于底层bufferevent） */</span></div></div><div><div><div>12</div></div><div><span>struct</span><span> bufferevent </span><span>*</span><span>bufferevent_openssl_filter_new</span><span>(</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span><span>underlying</span><span>,</span></span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>SSL </span><span>*</span><span><span>ssl</span><span>,</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>enum</span><span><span> bufferevent_ssl_state </span><span>state</span><span>,</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>int</span><span><span> </span><span>options</span><span>);</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>/* SSL状态 */</span></div></div><div><div><div>20</div></div><div><span>enum</span><span> bufferevent_ssl_state {</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>BUFFEREVENT_SSL_CONNECTING </span><span>=</span><span> </span><span>0</span><span>,</span><span>  // 正在握手</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>BUFFEREVENT_SSL_ACCEPTING,</span><span>       // 服务器接受连接</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>BUFFEREVENT_SSL_OPEN</span><span>             // SSL连接已建立</span></div></div><div><div><div>24</div></div><div><span>};</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>/* 获取SSL对象 */</span></div></div><div><div><div>27</div></div><div><span>SSL </span><span>*</span><span>bufferevent_openssl_get_ssl</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bufev</span><span>);</span></span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>/* 允许底层缓冲区重新协商 */</span></div></div><div><div><div>30</div></div><div><span>int</span><span> </span><span>bufferevent_openssl_get_allow_dirty_shutdown</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>);</span></span></div></div><div><div><div>31</div></div><div><span>void</span><span> </span><span>bufferevent_openssl_set_allow_dirty_shutdown</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span></span></div></div><div><div><div>32</div></div><div><span>    </span><span>int</span><span><span> </span><span>allow_dirty_shutdown</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 选项标志<a href="#52-选项标志"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>BEV_OPT_CLOSE_ON_FREE</span><span>        </span><span><span>0x</span><span>01</span></span><span>  // 释放时关闭底层</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>BEV_OPT_THREADSAFE</span><span>           </span><span><span>0x</span><span>02</span></span><span>  // 线程安全</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>BEV_OPT_DEFER_CALLBACKS</span><span>      </span><span><span>0x</span><span>04</span></span><span>  // 延迟回调</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>BEV_OPT_UNLOCK_CALLBACKS</span><span>     </span><span><span>0x</span><span>08</span></span><span>  // 回调时不加锁</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、SSL客户端<a href="#六ssl客户端"><span>#</span></a></h2><section><h3>6.1 客户端示例<a href="#61-客户端示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent_ssl.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;openssl/err.h&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>init_openssl</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>SSL_library_init</span><span>();</span></div></div><div><div><div>16</div></div><div><span>    </span><span>SSL_load_error_strings</span><span>();</span></div></div><div><div><div>17</div></div><div><span>    </span><span>OpenSSL_add_all_algorithms</span><span>();</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> SSL_CTX </span><span>*</span><span>create_client_ssl_ctx</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>TLS_client_method</span><span>());</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ctx) </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>SSL_CTX_set_min_proto_version</span><span>(ctx, TLS1_2_VERSION);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// 使用系统默认CA证书</span></div></div><div><div><div>27</div></div><div><span>    </span><span>SSL_CTX_set_default_verify_paths</span><span>(ctx);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>SSL_CTX_set_verify</span><span>(ctx, SSL_VERIFY_PEER, </span><span>NULL</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>return</span><span> ctx;</span></div></div><div><div><div>31</div></div><div><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>34</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>37</div></div><div><span>        </span><span>printf</span><span>(</span><span>"SSL connection established</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>        </span><span>// 发送HTTP请求</span></div></div><div><div><div>40</div></div><div><span>        </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>request </span><span>=</span></div></div><div><div><div>41</div></div><div><span>            </span><span>"GET / HTTP/1.1</span><span>\r\n</span><span>"</span></div></div><div><div><div>42</div></div><div><span>            </span><span>"Host: www.google.com</span><span>\r\n</span><span>"</span></div></div><div><div><div>43</div></div><div><span>            </span><span>"Connection: close</span><span>\r\n</span><span>"</span></div></div><div><div><div>44</div></div><div><span>            </span><span>"</span><span>\r\n</span><span>"</span><span>;</span></div></div><div><div><div>45</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, request, </span><span>strlen</span><span>(request));</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>48</div></div><div><span>        </span><span>unsigned</span><span> </span><span>long</span><span> err;</span></div></div><div><div><div>49</div></div><div><span>        </span><span>while</span><span> ((err </span><span>=</span><span> </span><span>bufferevent_get_openssl_error</span><span>(bev))) {</span></div></div><div><div><div>50</div></div><div><span>            </span><span>char</span><span> </span><span>msg</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>51</div></div><div><span>            </span><span>ERR_error_string_n</span><span>(err, msg, </span><span>sizeof</span><span>(msg));</span></div></div><div><div><div>52</div></div><div><span>            </span><span>fprintf</span><span>(stderr, </span><span>"SSL error: </span><span>%s</span><span>\n</span><span>"</span><span>, msg);</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_TIMEOUT)) {</span></div></div><div><div><div>57</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>58</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>59</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>60</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>64</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>4096</span><span>];</span></div></div><div><div><div>65</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>68</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>69</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>70</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>71</div></div><div><span>}</span></div></div><div><div><div>72</div></div><div>
</div></div><div><div><div>73</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>74</div></div><div><span>    </span><span>init_openssl</span><span>();</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>77</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ssl_ctx </span><span>=</span><span> </span><span>create_client_ssl_ctx</span><span>();</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ssl_ctx) {</span></div></div><div><div><div>80</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create SSL context</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>81</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>82</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>83</div></div><div>
</div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>SSL </span><span>*</span><span>ssl </span><span>=</span><span> </span><span>SSL_new</span><span>(ssl_ctx);</span></div></div><div><div><div>85</div></div><div><span>    </span><span>SSL_set_tlsext_host_name</span><span>(ssl, </span><span>"www.google.com"</span><span>);</span><span>  // SNI</span></div></div><div><div><div>86</div></div><div>
</div></div><div><div><div>87</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_openssl_socket_new</span><span>(</span></div></div><div><div><div>88</div></div><div><span><span>        </span></span><span>base, </span><span>-</span><span>1</span><span>, ssl, BUFFEREVENT_SSL_CONNECTING,</span></div></div><div><div><div>89</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> BEV_OPT_DEFER_CALLBACKS);</span></div></div><div><div><div>90</div></div><div>
</div></div><div><div><div>91</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, base);</span></div></div><div><div><div>92</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>    </span><span>// 连接服务器</span></div></div><div><div><div>95</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>96</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>97</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>98</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>443</span><span>);</span></div></div><div><div><div>99</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, </span><span>"142.250.185.68"</span><span>, </span><span>&amp;</span><span>sin</span><span>.</span><span>sin_addr</span><span>);</span><span>  // google.com</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>if</span><span> (</span><span>bufferevent_socket_connect</span><span>(bev, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin)) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>102</div></div><div><span>        </span><span>perror</span><span>(</span><span>"connect"</span><span>);</span></div></div><div><div><div>103</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>104</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ssl_ctx);</span></div></div><div><div><div>105</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>106</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>107</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>108</div></div><div>
</div></div><div><div><div>109</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connecting to www.google.com:443...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>110</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>111</div></div><div>
</div></div><div><div><div>112</div></div><div><span>    </span><span>SSL_CTX_free</span><span>(ssl_ctx);</span></div></div><div><div><div>113</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>114</div></div><div>
</div></div><div><div><div>115</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>116</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>七、SSL服务器<a href="#七ssl服务器"><span>#</span></a></h2><section><h3>7.1 服务器示例<a href="#71-服务器示例"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent_ssl.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;openssl/err.h&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>static</span><span> SSL_CTX </span><span>*</span><span>g_ssl_ctx </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>init_openssl</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>    </span><span>SSL_library_init</span><span>();</span></div></div><div><div><div>16</div></div><div><span>    </span><span>SSL_load_error_strings</span><span>();</span></div></div><div><div><div>17</div></div><div><span>    </span><span>OpenSSL_add_all_algorithms</span><span>();</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> SSL_CTX </span><span>*</span><span>create_server_ssl_ctx</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>TLS_server_method</span><span>());</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ctx) </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>SSL_CTX_set_min_proto_version</span><span>(ctx, TLS1_2_VERSION);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// 加载证书和私钥</span></div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_use_certificate_file</span><span>(ctx, </span><span>"server.crt"</span><span>, SSL_FILETYPE_PEM) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load certificate</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>29</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>30</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_use_PrivateKey_file</span><span>(ctx, </span><span>"server.key"</span><span>, SSL_FILETYPE_PEM) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>34</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load private key</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>35</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>36</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_check_private_key</span><span>(ctx) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>40</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Private key check failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>41</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>42</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// 设置密码套件</span></div></div><div><div><div>46</div></div><div><span>    </span><span>SSL_CTX_set_cipher_list</span><span>(ctx,</span></div></div><div><div><div>47</div></div><div><span>        </span><span>"ECDHE-RSA-AES128-GCM-SHA256:"</span></div></div><div><div><div>48</div></div><div><span>        </span><span>"ECDHE-RSA-AES256-GCM-SHA384"</span><span>);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>// 可选：要求客户端证书</span></div></div><div><div><div>51</div></div><div><span>    </span><span>// SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>// SSL_CTX_load_verify_locations(ctx, "ca.crt", NULL);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>return</span><span> ctx;</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>58</div></div><div><span>    </span><span>char</span><span> </span><span>buf</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>59</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>bufferevent_read</span><span>(bev, buf, </span><span>sizeof</span><span>(buf) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>62</div></div><div><span>        </span><span>buf</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>63</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received: </span><span>%s</span><span>"</span><span>, buf);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>        </span><span>// 回显</span></div></div><div><div><div>66</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, buf, n);</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>68</div></div><div><span>}</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>71</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>72</div></div><div><span>        </span><span>printf</span><span>(</span><span>"SSL handshake completed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>73</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>74</div></div><div><span>        </span><span>unsigned</span><span> </span><span>long</span><span> err;</span></div></div><div><div><div>75</div></div><div><span>        </span><span>while</span><span> ((err </span><span>=</span><span> </span><span>bufferevent_get_openssl_error</span><span>(bev))) {</span></div></div><div><div><div>76</div></div><div><span>            </span><span>char</span><span> </span><span>msg</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>77</div></div><div><span>            </span><span>ERR_error_string_n</span><span>(err, msg, </span><span>sizeof</span><span>(msg));</span></div></div><div><div><div>78</div></div><div><span>            </span><span>fprintf</span><span>(stderr, </span><span>"SSL error: </span><span>%s</span><span>\n</span><span>"</span><span>, msg);</span></div></div><div><div><div>79</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>80</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>81</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_EOF) {</span></div></div><div><div><div>82</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Client disconnected</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>83</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>84</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>85</div></div><div><span>}</span></div></div><div><div><div>86</div></div><div>
</div></div><div><div><div>87</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span></span></div></div><div><div><div>88</div></div><div><span>                      </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>89</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>90</div></div><div>
</div></div><div><div><div>91</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection from </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>92</div></div><div><span>           </span><span>inet_ntoa</span><span>(((</span><span>struct</span><span> sockaddr_in </span><span>*</span><span>)addr)-&gt;</span><span>sin_addr</span><span>),</span></div></div><div><div><div>93</div></div><div><span>           </span><span>ntohs</span><span>(((</span><span>struct</span><span> sockaddr_in </span><span>*</span><span>)addr)-&gt;</span><span>sin_port</span><span>));</span></div></div><div><div><div>94</div></div><div>
</div></div><div><div><div>95</div></div><div><span><span>    </span></span><span>SSL </span><span>*</span><span>ssl </span><span>=</span><span> </span><span>SSL_new</span><span>(g_ssl_ctx);</span></div></div><div><div><div>96</div></div><div>
</div></div><div><div><div>97</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_openssl_socket_new</span><span>(</span></div></div><div><div><div>98</div></div><div><span><span>        </span></span><span>base, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,</span></div></div><div><div><div>99</div></div><div><span><span>        </span></span><span>BEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> BEV_OPT_DEFER_CALLBACKS);</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>102</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>103</div></div><div><span>}</span></div></div><div><div><div>104</div></div><div>
</div></div><div><div><div>105</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>106</div></div><div><span>    </span><span>init_openssl</span><span>();</span></div></div><div><div><div>107</div></div><div>
</div></div><div><div><div>108</div></div><div><span><span>    </span></span><span>g_ssl_ctx </span><span>=</span><span> </span><span>create_server_ssl_ctx</span><span>();</span></div></div><div><div><div>109</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>g_ssl_ctx) {</span></div></div><div><div><div>110</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create SSL context</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>111</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>112</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>113</div></div><div>
</div></div><div><div><div>114</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>115</div></div><div>
</div></div><div><div><div>116</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>117</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>118</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>119</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>8443</span><span>);</span></div></div><div><div><div>120</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>121</div></div><div>
</div></div><div><div><div>122</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(</span></div></div><div><div><div>123</div></div><div><span><span>        </span></span><span>base, accept_cb, base,</span></div></div><div><div><div>124</div></div><div><span><span>        </span></span><span>LEV_OPT_CLOSE_ON_FREE </span><span>|</span><span> LEV_OPT_REUSEABLE,</span></div></div><div><div><div>125</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>126</div></div><div>
</div></div><div><div><div>127</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>listener) {</span></div></div><div><div><div>128</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create listener</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>129</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(g_ssl_ctx);</span></div></div><div><div><div>130</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>131</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>132</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>133</div></div><div>
</div></div><div><div><div>134</div></div><div><span>    </span><span>printf</span><span>(</span><span>"SSL server listening on port 8443</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>135</div></div><div>
</div></div><div><div><div>136</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>137</div></div><div>
</div></div><div><div><div>138</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>139</div></div><div><span>    </span><span>SSL_CTX_free</span><span>(g_ssl_ctx);</span></div></div><div><div><div>140</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>141</div></div><div>
</div></div><div><div><div>142</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>143</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>八、证书验证<a href="#八证书验证"><span>#</span></a></h2><section><h3>8.1 自定义验证回调<a href="#81-自定义验证回调"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;openssl/x509.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;openssl/x509v3.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>verify_callback</span><span>(</span><span>int</span><span><span> </span><span>preverify_ok</span><span>,</span><span> X509_STORE_CTX </span></span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>X509 </span><span>*</span><span>cert </span><span>=</span><span> </span><span>X509_STORE_CTX_get_current_cert</span><span>(ctx);</span></div></div><div><div><div>8</div></div><div><span>    </span><span>int</span><span> depth </span><span>=</span><span> </span><span>X509_STORE_CTX_get_error_depth</span><span>(ctx);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>int</span><span> err </span><span>=</span><span> </span><span>X509_STORE_CTX_get_error</span><span>(ctx);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>char</span><span> </span><span>subject</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>12</div></div><div><span>    </span><span>X509_NAME_oneline</span><span>(</span><span>X509_get_subject_name</span><span>(cert), subject, </span><span>sizeof</span><span>(subject));</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Certificate depth </span><span>%d</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>, depth, subject);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>preverify_ok) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Verification failed: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>18</div></div><div><span>               </span><span>X509_verify_cert_error_string</span><span>(err));</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>        </span><span>// 可以在这里实现自定义验证逻辑</span></div></div><div><div><div>21</div></div><div><span>        </span><span>// 返回1继续验证，返回0终止</span></div></div><div><div><div>22</div></div><div><span>        </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Verification passed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>void</span><span> </span><span>setup_verification</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>30</div></div><div><span>    </span><span>SSL_CTX_set_verify</span><span>(ctx, SSL_VERIFY_PEER, verify_callback);</span></div></div><div><div><div>31</div></div><div><span>    </span><span>SSL_CTX_set_verify_depth</span><span>(ctx, </span><span>4</span><span>);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>// 加载CA证书</span></div></div><div><div><div>34</div></div><div><span>    </span><span>SSL_CTX_load_verify_locations</span><span>(ctx, </span><span>"ca-bundle.crt"</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>// 或使用系统默认CA</span></div></div><div><div><div>37</div></div><div><span>    </span><span>SSL_CTX_set_default_verify_paths</span><span>(ctx);</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>8.2 获取证书信息<a href="#82-获取证书信息"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;openssl/x509.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>print_certificate_info</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>X509 </span><span>*</span><span>cert </span><span>=</span><span> </span><span>SSL_get_peer_certificate</span><span>(ssl);</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>cert) {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>printf</span><span>(</span><span>"No peer certificate</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>9</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>char</span><span> </span><span>subject</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>13</div></div><div><span>    </span><span>char</span><span> </span><span>issuer</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>X509_NAME_oneline</span><span>(</span><span>X509_get_subject_name</span><span>(cert), subject, </span><span>sizeof</span><span>(subject));</span></div></div><div><div><div>16</div></div><div><span>    </span><span>X509_NAME_oneline</span><span>(</span><span>X509_get_issuer_name</span><span>(cert), issuer, </span><span>sizeof</span><span>(issuer));</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Subject: </span><span>%s</span><span>\n</span><span>"</span><span>, subject);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Issuer: </span><span>%s</span><span>\n</span><span>"</span><span>, issuer);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>// 检查证书有效期</span></div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> ASN1_TIME </span><span>*</span><span>not_before </span><span>=</span><span> </span><span>X509_get0_notBefore</span><span>(cert);</span></div></div><div><div><div>23</div></div><div><span>    </span><span>const</span><span> ASN1_TIME </span><span>*</span><span>not_after </span><span>=</span><span> </span><span>X509_get0_notAfter</span><span>(cert);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>// 验证结果</span></div></div><div><div><div>26</div></div><div><span>    </span><span>long</span><span> verify_result </span><span>=</span><span> </span><span>SSL_get_verify_result</span><span>(ssl);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (verify_result </span><span>==</span><span> X509_V_OK) {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Certificate verified successfully</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>30</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Certificate verification failed: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>31</div></div><div><span>               </span><span>X509_verify_cert_error_string</span><span>(verify_result));</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>X509_free</span><span>(cert);</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>九、SNI支持<a href="#九sni支持"><span>#</span></a></h2><section><h3>9.1 客户端SNI<a href="#91-客户端sni"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>void</span><span> </span><span>set_sni</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>hostname</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 设置SNI（Server Name Indication）</span></div></div><div><div><div>5</div></div><div><span>    </span><span>SSL_set_tlsext_host_name</span><span>(ssl, hostname);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>// 设置主机名验证</span></div></div><div><div><div>8</div></div><div><span>    </span><span>SSL_set1_host</span><span>(ssl, hostname);</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>9.2 服务器SNI回调<a href="#92-服务器sni回调"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>sni_callback</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>,</span><span> </span></span><span>int</span><span> </span><span>*</span><span><span>al</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>servername </span><span>=</span><span> </span><span>SSL_get_servername</span><span>(ssl, TLSEXT_NAMETYPE_host_name);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span> (servername) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>printf</span><span>(</span><span>"SNI: </span><span>%s</span><span>\n</span><span>"</span><span>, servername);</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>        </span><span>// 根据域名选择不同的证书</span></div></div><div><div><div>12</div></div><div><span>        </span><span>if</span><span> (</span><span>strcmp</span><span>(servername, </span><span>"example.com"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>13</div></div><div><span>            </span><span>SSL_set_SSL_CTX</span><span>(ssl, </span><span>get_example_com_ctx</span><span>());</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>strcmp</span><span>(servername, </span><span>"test.com"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>            </span><span>SSL_set_SSL_CTX</span><span>(ssl, </span><span>get_test_com_ctx</span><span>());</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>    </span><span>return</span><span> SSL_TLSEXT_ERR_OK;</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>void</span><span> </span><span>setup_sni_callback</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>23</div></div><div><span>    </span><span>SSL_CTX_set_tlsext_servername_callback</span><span>(ctx, sni_callback);</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十、ALPN支持<a href="#十alpn支持"><span>#</span></a></h2><section><h3>10.1 ALPN协商<a href="#101-alpn协商"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>// 客户端设置ALPN</span></div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>alpn_client_select_proto</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>,</span><span> </span></span><span>unsigned</span><span> </span><span>char</span><span> </span><span>**</span><span><span>out</span><span>,</span></span></div></div><div><div><div>5</div></div><div><span>                                     </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span><span>outlen</span><span>,</span></span></div></div><div><div><div>6</div></div><div><span>                                     </span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span><span>server</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span>                                     </span><span>unsigned</span><span> </span><span>int</span><span><span> </span><span>server_len</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span>client </span><span>=</span><span> (</span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span>)</span><span>"</span><span>\x02</span><span>h2</span><span>\x08</span><span>http/1.1"</span><span>;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>unsigned</span><span> </span><span>int</span><span> client_len </span><span>=</span><span> </span><span>12</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_select_next_proto</span><span>(out, outlen, server, server_len,</span></div></div><div><div><div>12</div></div><div><span><span>                               </span></span><span>client, client_len) </span><span>==</span><span> OPENSSL_NPN_NEGOTIATED) {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>return</span><span> SSL_TLSEXT_ERR_OK;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>return</span><span> SSL_TLSEXT_ERR_ALERT_FATAL;</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>void</span><span> </span><span>setup_client_alpn</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>SSL_CTX_set_alpn_protos</span><span>(ctx,</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>(</span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span>)</span><span>"</span><span>\x02</span><span>h2</span><span>\x08</span><span>http/1.1"</span><span>, </span><span>12</span><span>);</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>// 服务器设置ALPN回调</span></div></div><div><div><div>25</div></div><div><span>static</span><span> </span><span>int</span><span> </span><span>alpn_server_select_proto</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>,</span><span> </span></span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>**</span><span><span>out</span><span>,</span></span></div></div><div><div><div>26</div></div><div><span>                                     </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span><span>outlen</span><span>,</span></span></div></div><div><div><div>27</div></div><div><span>                                     </span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span><span>in</span><span>,</span></span></div></div><div><div><div>28</div></div><div><span>                                     </span><span>unsigned</span><span> </span><span>int</span><span><span> </span><span>inlen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>// 优先选择h2</span></div></div><div><div><div>30</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_select_next_proto</span><span>((</span><span>unsigned</span><span> </span><span>char</span><span> </span><span>**</span><span>)out, outlen,</span></div></div><div><div><div>31</div></div><div><span><span>                               </span></span><span>(</span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span>)</span><span>"</span><span>\x02</span><span>h2</span><span>\x08</span><span>http/1.1"</span><span>, </span><span>12</span><span>,</span></div></div><div><div><div>32</div></div><div><span><span>                               </span></span><span>in, inlen) </span><span>==</span><span> OPENSSL_NPN_NEGOTIATED) {</span></div></div><div><div><div>33</div></div><div><span>        </span><span>return</span><span> SSL_TLSEXT_ERR_OK;</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>*</span><span>out </span><span>=</span><span> (</span><span>const</span><span> </span><span>unsigned</span><span> </span><span>char</span><span> </span><span>*</span><span>)</span><span>"http/1.1"</span><span>;</span></div></div><div><div><div>37</div></div><div><span>    </span><span>*</span><span>outlen </span><span>=</span><span> </span><span>8</span><span>;</span></div></div><div><div><div>38</div></div><div><span>    </span><span>return</span><span> SSL_TLSEXT_ERR_OK;</span></div></div><div><div><div>39</div></div><div><span>}</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>void</span><span> </span><span>setup_server_alpn</span><span><span>(</span><span>SSL_CTX </span></span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>42</div></div><div><span>    </span><span>SSL_CTX_set_alpn_select_cb</span><span>(ctx, alpn_server_select_proto, </span><span>NULL</span><span>);</span></div></div><div><div><div>43</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十一、错误处理<a href="#十一错误处理"><span>#</span></a></h2><section><h3>11.1 SSL错误处理<a href="#111-ssl错误处理"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;openssl/err.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>void</span><span> </span><span>print_ssl_error</span><span><span>(</span><span>SSL </span></span><span>*</span><span><span>ssl</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>ret</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>int</span><span> err </span><span>=</span><span> </span><span>SSL_get_error</span><span>(ssl, ret);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>switch</span><span> (err) {</span></div></div><div><div><div>9</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_NONE:</span></div></div><div><div><div>10</div></div><div><span>            </span><span>printf</span><span>(</span><span>"No error</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>12</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_ZERO_RETURN:</span></div></div><div><div><div>13</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL connection closed cleanly</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>14</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_WANT_READ:</span></div></div><div><div><div>16</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL needs more data to read</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>17</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>18</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_WANT_WRITE:</span></div></div><div><div><div>19</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL needs to write more data</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>20</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>21</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_WANT_CONNECT:</span></div></div><div><div><div>22</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL connect not completed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>23</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>24</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_WANT_ACCEPT:</span></div></div><div><div><div>25</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL accept not completed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>27</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_WANT_X509_LOOKUP:</span></div></div><div><div><div>28</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL certificate lookup needed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>29</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>30</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_SYSCALL:</span></div></div><div><div><div>31</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL I/O error: </span><span>%s</span><span>\n</span><span>"</span><span><span>,</span><span> </span></span><span>strerror</span><span><span>(</span><span>errno</span><span>));</span></span></div></div><div><div><div>32</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>33</div></div><div><span>        </span><span>case</span><span> SSL_ERROR_SSL:</span></div></div><div><div><div>34</div></div><div><span>            </span><span>printf</span><span>(</span><span>"SSL protocol error</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>35</div></div><div><span>            </span><span>ERR_print_errors_fp</span><span><span>(</span><span>stderr</span><span>);</span></span></div></div><div><div><div>36</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>37</div></div><div><span>        </span><span>default</span><span>:</span></div></div><div><div><div>38</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Unknown SSL error: </span><span>%d</span><span>\n</span><span>"</span><span><span>,</span><span> err</span><span>);</span></span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>void</span><span> </span><span>print_all_ssl_errors</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>43</div></div><div><span>    </span><span>unsigned</span><span> </span><span>long</span><span> err;</span></div></div><div><div><div>44</div></div><div><span>    </span><span>while</span><span> ((err </span><span>=</span><span> </span><span>ERR_get_error</span><span>())) {</span></div></div><div><div><div>45</div></div><div><span>        </span><span>char</span><span> </span><span>msg</span><span>[</span><span>256</span><span>];</span></div></div><div><div><div>46</div></div><div><span>        </span><span>ERR_error_string_n</span><span>(err, msg, </span><span>sizeof</span><span>(msg));</span></div></div><div><div><div>47</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"SSL Error: </span><span>%s</span><span>\n</span><span>"</span><span>, msg);</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>十二、完整示例：HTTPS服务器<a href="#十二完整示例https服务器"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent_ssl.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;openssl/ssl.h&gt;</span></div></div><div><div><div>11</div></div><div><span>#include</span><span> </span><span>&lt;openssl/err.h&gt;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> SSL_CTX </span><span>*</span><span>g_ssl_ctx </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>struct</span><span> event_base </span><span>*</span><span>g_base </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span>    </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Shutting down...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(g_base);</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>init_openssl</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>SSL_library_init</span><span>();</span></div></div><div><div><div>23</div></div><div><span>    </span><span>SSL_load_error_strings</span><span>();</span></div></div><div><div><div>24</div></div><div><span>    </span><span>OpenSSL_add_all_algorithms</span><span>();</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>static</span><span> SSL_CTX </span><span>*</span><span>create_ssl_ctx</span><span>(</span><span>void</span><span>) {</span></div></div><div><div><div>28</div></div><div><span><span>    </span></span><span>SSL_CTX </span><span>*</span><span>ctx </span><span>=</span><span> </span><span>SSL_CTX_new</span><span>(</span><span>TLS_server_method</span><span>());</span></div></div><div><div><div>29</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>ctx) </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>SSL_CTX_set_min_proto_version</span><span>(ctx, TLS1_2_VERSION);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>// 加载证书链</span></div></div><div><div><div>34</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_use_certificate_chain_file</span><span>(ctx, </span><span>"server.crt"</span><span>) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>35</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load certificate chain</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>36</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>37</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>// 加载私钥</span></div></div><div><div><div>41</div></div><div><span>    </span><span>if</span><span> (</span><span>SSL_CTX_use_PrivateKey_file</span><span>(ctx, </span><span>"server.key"</span><span>, SSL_FILETYPE_PEM) </span><span>!=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>42</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to load private key</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>43</div></div><div><span>        </span><span>SSL_CTX_free</span><span>(ctx);</span></div></div><div><div><div>44</div></div><div><span>        </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>// 设置ECDH自动选择曲线</span></div></div><div><div><div>48</div></div><div><span>    </span><span>SSL_CTX_set_ecdh_auto</span><span>(ctx, </span><span>1</span><span>);</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>    </span><span>// 设置密码套件</span></div></div><div><div><div>51</div></div><div><span>    </span><span>SSL_CTX_set_cipher_list</span><span>(ctx,</span></div></div><div><div><div>52</div></div><div><span>        </span><span>"ECDHE-ECDSA-AES128-GCM-SHA256:"</span></div></div><div><div><div>53</div></div><div><span>        </span><span>"ECDHE-RSA-AES128-GCM-SHA256:"</span></div></div><div><div><div>54</div></div><div><span>        </span><span>"ECDHE-ECDSA-AES256-GCM-SHA384:"</span></div></div><div><div><div>55</div></div><div><span>        </span><span>"ECDHE-RSA-AES256-GCM-SHA384:"</span></div></div><div><div><div>56</div></div><div><span>        </span><span>"ECDHE-ECDSA-CHACHA20-POLY1305:"</span></div></div><div><div><div>57</div></div><div><span>        </span><span>"ECDHE-RSA-CHACHA20-POLY1305"</span><span>);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>    </span><span>printf</span><span>(</span><span>"SSL context configured</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>return</span><span> ctx;</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>http_request_cb</span><span>(</span><span>struct</span><span> evhttp_request </span><span>*</span><span><span>req</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>64</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>buf </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(buf,</span></div></div><div><div><div>67</div></div><div><span>        </span><span>"&lt;html&gt;&lt;body&gt;"</span></div></div><div><div><div>68</div></div><div><span>        </span><span>"&lt;h1&gt;HTTPS Server&lt;/h1&gt;"</span></div></div><div><div><div>69</div></div><div><span>        </span><span>"&lt;p&gt;This is a secure connection!&lt;/p&gt;"</span></div></div><div><div><div>70</div></div><div><span>        </span><span>"&lt;p&gt;Protocol: </span><span>%s</span><span>&lt;/p&gt;"</span></div></div><div><div><div>71</div></div><div><span>        </span><span>"&lt;p&gt;Cipher: </span><span>%s</span><span>&lt;/p&gt;"</span></div></div><div><div><div>72</div></div><div><span>        </span><span>"&lt;/body&gt;&lt;/html&gt;"</span><span>,</span></div></div><div><div><div>73</div></div><div><span>        </span><span>SSL_get_version</span><span>(</span><span>SSL_get_current_compression</span><span>(</span><span>SSL_get_current_compression</span><span>(</span><span>NULL</span><span>))),</span></div></div><div><div><div>74</div></div><div><span>        </span><span>SSL_get_cipher_name</span><span>(</span><span>SSL_get_current_compression</span><span>(</span><span>SSL_get_current_compression</span><span>(</span><span>NULL</span><span>))));</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>evhttp_request_get_output_headers</span><span>(req),</span></div></div><div><div><div>77</div></div><div><span>                      </span><span>"Content-Type"</span><span>, </span><span>"text/html"</span><span>);</span></div></div><div><div><div>78</div></div><div><span>    </span><span>evhttp_send_reply</span><span>(req, HTTP_OK, </span><span>"OK"</span><span>, buf);</span></div></div><div><div><div>79</div></div><div>
</div></div><div><div><div>80</div></div><div><span>    </span><span>evbuffer_free</span><span>(buf);</span></div></div><div><div><div>81</div></div><div><span>}</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>84</div></div><div><span>    </span><span>init_openssl</span><span>();</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span><span>    </span></span><span>g_ssl_ctx </span><span>=</span><span> </span><span>create_ssl_ctx</span><span>();</span></div></div><div><div><div>87</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>g_ssl_ctx) {</span></div></div><div><div><div>88</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create SSL context</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>89</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>90</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span><span>    </span></span><span>g_base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>    </span><span>// 设置信号处理</span></div></div><div><div><div>95</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sigint </span><span>=</span><span> </span><span>evsignal_new</span><span>(g_base, SIGINT, signal_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>96</div></div><div><span>    </span><span>event_add</span><span>(sigint, </span><span>NULL</span><span>);</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>    </span><span>// 创建HTTP服务器</span></div></div><div><div><div>99</div></div><div><span>    </span><span>struct</span><span> evhttp </span><span>*</span><span>http </span><span>=</span><span> </span><span>evhttp_new</span><span>(g_base);</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>evhttp_set_cb</span><span>(http, </span><span>"/"</span><span>, http_request_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>102</div></div><div>
</div></div><div><div><div>103</div></div><div><span>    </span><span>// 绑定地址</span></div></div><div><div><div>104</div></div><div><span>    </span><span>if</span><span> (</span><span>evhttp_bind_socket</span><span>(http, </span><span>"0.0.0.0"</span><span>, </span><span>8443</span><span>) </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>105</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to bind socket</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>106</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>107</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>108</div></div><div>
</div></div><div><div><div>109</div></div><div><span>    </span><span>printf</span><span>(</span><span>"HTTPS server listening on https://0.0.0.0:8443/</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>110</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to stop</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>111</div></div><div>
</div></div><div><div><div>112</div></div><div><span>    </span><span>event_base_dispatch</span><span>(g_base);</span></div></div><div><div><div>113</div></div><div>
</div></div><div><div><div>114</div></div><div><span>    </span><span>evhttp_free</span><span>(http);</span></div></div><div><div><div>115</div></div><div><span>    </span><span>event_free</span><span>(sigint);</span></div></div><div><div><div>116</div></div><div><span>    </span><span>SSL_CTX_free</span><span>(g_ssl_ctx);</span></div></div><div><div><div>117</div></div><div><span>    </span><span>event_base_free</span><span>(g_base);</span></div></div><div><div><div>118</div></div><div>
</div></div><div><div><div>119</div></div><div><span>    </span><span>EVP_cleanup</span><span>();</span></div></div><div><div><div>120</div></div><div><span>    </span><span>ERR_free_strings</span><span>();</span></div></div><div><div><div>121</div></div><div>
</div></div><div><div><div>122</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>123</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>十三、最佳实践<a href="#十三最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td><strong>协议版本</strong></td><td>禁用SSLv3和TLS1.0，使用TLS1.2+</td></tr><tr><td><strong>密码套件</strong></td><td>优先使用ECDHE和AES-GCM</td></tr><tr><td><strong>证书验证</strong></td><td>始终验证服务器证书</td></tr><tr><td><strong>SNI</strong></td><td>客户端设置SNI以支持虚拟主机</td></tr><tr><td><strong>错误处理</strong></td><td>详细记录SSL错误信息</td></tr><tr><td><strong>证书更新</strong></td><td>自动化证书续期流程</td></tr><tr><td><strong>HSTS</strong></td><td>服务器启用HSTS头部</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-tcp-listener/</id>
      <title type="text">🔌 TCP监听器 - libevent源码深度解析系列（九）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-tcp-listener/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔌 TCP监听器<a href="#-tcp监听器"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>evconnlistener是libevent提供的高级抽象，用于监听TCP连接。它封装了底层的socket创建、绑定、监听和接受连接的过程，使得开发者可以更方便地创建TCP服务端。</p></section><section><h2>二、创建监听器<a href="#二创建监听器"><span>#</span></a></h2><section><h3>2.1 创建函数<a href="#21-创建函数"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建监听器（已绑定socket）</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base     event_base</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param cb       接受连接的回调函数</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param ptr      回调函数的参数</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* @param flags    选项标志</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* @param backlog  listen()的backlog参数，-1表示使用默认值</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* @param sa       绑定的地址</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>* @param socklen  地址长度</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>10</div></div><div><span>struct</span><span> evconnlistener </span><span>*</span><span>evconnlistener_new_bind</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>evconnlistener_cb </span><span>cb</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>,</span><span> </span></span><span>unsigned</span><span><span> </span><span>flags</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>backlog</span><span>,</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>const</span><span> </span><span>struct</span><span> sockaddr </span><span>*</span><span><span>sa</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>);</span></span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>/* 创建监听器（已有socket）</span></div></div><div><div><div>15</div></div><div><span><span> </span></span><span>* @param fd       已绑定的socket</span></div></div><div><div><div>16</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>17</div></div><div><span>struct</span><span> evconnlistener </span><span>*</span><span>evconnlistener_new</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span></span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>evconnlistener_cb </span><span>cb</span><span>,</span><span> </span><span>void</span><span> </span><span>*</span><span><span>ptr</span><span>,</span><span> </span></span><span>unsigned</span><span><span> </span><span>flags</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>backlog</span><span>,</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>);</span></span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>/* 释放监听器 */</span></div></div><div><div><div>22</div></div><div><span>void</span><span> </span><span>evconnlistener_free</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>lev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.2 选项标志<a href="#22-选项标志"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>LEV_OPT_LEAVE_SOCKETS_BLOCKED</span><span>  </span><span><span>0x</span><span>01</span></span><span>  // 不设置非阻塞</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>LEV_OPT_CLOSE_ON_FREE</span><span>          </span><span><span>0x</span><span>02</span></span><span>  // 释放时关闭socket</span></div></div><div><div><div>3</div></div><div><span>#define</span><span> </span><span>LEV_OPT_CLOSE_ON_EXEC</span><span>          </span><span><span>0x</span><span>04</span></span><span>  // 设置FD_CLOEXEC</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>LEV_OPT_REUSEABLE</span><span>              </span><span><span>0x</span><span>08</span></span><span>  // 设置SO_REUSEADDR</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>LEV_OPT_THREADSAFE</span><span>             </span><span><span>0x</span><span>10</span></span><span>  // 线程安全</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>LEV_OPT_DISABLED</span><span>               </span><span><span>0x</span><span>20</span></span><span>  // 创建时禁用</span></div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>LEV_OPT_DEFERRED_ACCEPT</span><span>        </span><span><span>0x</span><span>40</span></span><span>  // 延迟accept（Linux）</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、回调函数<a href="#三回调函数"><span>#</span></a></h2><section><h3>3.1 回调函数类型<a href="#31-回调函数类型"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>listener</span><span>  监听器</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>fd</span><span>        新连接的socket</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>addr</span><span>      客户端地址</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>socklen</span><span>   地址长度</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>* </span><span>@param</span><span> </span><span>userarg</span><span>   用户参数</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>8</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evconnlistener_cb)(</span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>evutil_socket_t</span><span> fd, </span><span>struct</span><span> sockaddr </span><span>*</span><span>addr, </span><span>int</span><span> socklen, </span><span>void</span><span> </span><span>*</span><span>userarg);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 错误回调<a href="#32-错误回调"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evconnlistener_errorcb)(</span><span>struct</span><span> evconnlistener </span><span>*</span><span>lis, </span><span>void</span><span> </span><span>*</span><span>ptr);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>/* 设置错误回调 */</span></div></div><div><div><div>4</div></div><div><span>void</span><span> </span><span>evconnlistener_set_error_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>lev</span><span>,</span></span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>evconnlistener_errorcb </span><span>errorcb</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>四、监听器操作<a href="#四监听器操作"><span>#</span></a></h2><section><h3>4.1 启用和禁用<a href="#41-启用和禁用"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 启用监听器 */</span></div></div><div><div><div>2</div></div><div><span>int</span><span> </span><span>evconnlistener_enable</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>lev</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 禁用监听器 */</span></div></div><div><div><div>5</div></div><div><span>int</span><span> </span><span>evconnlistener_disable</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>lev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 获取信息<a href="#42-获取信息"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 获取监听socket */</span></div></div><div><div><div>2</div></div><div><span>evutil_socket_t</span><span> </span><span>evconnlistener_get_fd</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>lev</span><span>);</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 获取所属的event_base */</span></div></div><div><div><div>5</div></div><div><span>struct</span><span> event_base </span><span>*</span><span>evconnlistener_get_base</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>lev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>五、完整示例<a href="#五完整示例"><span>#</span></a></h2><section><h3>5.1 回射服务器<a href="#51-回射服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>static</span><span> </span><span>const</span><span> </span><span>int</span><span> PORT </span><span>=</span><span> </span><span>8080</span><span>;</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>echo_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>evbuffer_add_buffer</span><span>(output, input);</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>echo_event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Error from bufferevent"</span><span>);</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>27</div></div><div><span>}</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_conn_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>30</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection: fd=</span><span>%d</span><span>\n</span><span>"</span><span>, fd);</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>35</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, echo_read_cb, </span><span>NULL</span><span>, echo_event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>39</div></div><div><span>}</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_error_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>int</span><span> err </span><span>=</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>();</span></div></div><div><div><div>44</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Got an error </span><span>%d</span><span> (</span><span>%s</span><span>) on the listener. Shutting down.</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>45</div></div><div><span><span>            </span></span><span>err, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>46</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>47</div></div><div><span>}</span></div></div><div><div><div>48</div></div><div>
</div></div><div><div><div>49</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sig</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>user_data</span><span>) {</span></span></div></div><div><div><div>50</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> user_data;</span></div></div><div><div><div>51</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught signal </span><span>%d</span><span>, shutting down.</span><span>\n</span><span>"</span><span>, sig);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>53</div></div><div><span>}</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>**</span><span><span>argv</span><span>) {</span></span></div></div><div><div><div>56</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>57</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener;</span></div></div><div><div><div>58</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>signal_event;</span></div></div><div><div><div>59</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>62</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>63</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Could not initialize libevent!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>64</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>68</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>69</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(PORT);</span></div></div><div><div><div>70</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span><span>    </span></span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(base, accept_conn_cb, </span><span>NULL</span><span>,</span></div></div><div><div><div>73</div></div><div><span><span>        </span></span><span>LEV_OPT_REUSEABLE </span><span>|</span><span> LEV_OPT_CLOSE_ON_FREE, </span><span>-</span><span>1</span><span>,</span></div></div><div><div><div>74</div></div><div><span><span>        </span></span><span>(</span><span>struct</span><span> sockaddr</span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>listener) {</span></div></div><div><div><div>77</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Could not create a listener!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>78</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>79</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>    </span><span>evconnlistener_set_error_cb</span><span>(listener, accept_error_cb);</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span><span>    </span></span><span>signal_event </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, (</span><span>void*</span><span>)base);</span></div></div><div><div><div>84</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>signal_event </span><span>||</span><span> </span><span>event_add</span><span>(signal_event, </span><span>NULL</span><span>) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>85</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Could not create/add a signal event!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>86</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>87</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server listening on port </span><span>%d</span><span>\n</span><span>"</span><span>, PORT);</span></div></div><div><div><div>90</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>93</div></div><div><span>    </span><span>event_free</span><span>(signal_event);</span></div></div><div><div><div>94</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>95</div></div><div>
</div></div><div><div><div>96</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>97</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>98</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 编译运行<a href="#52-编译运行"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译</span></div></div><div><div><div>2</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>echo_server</span><span> </span><span>echo_server.c</span><span> </span><span>-levent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 运行</span></div></div><div><div><div>5</div></div><div><span>./echo_server</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 测试</span></div></div><div><div><div>8</div></div><div><span>telnet</span><span> </span><span>127.0.0.1</span><span> </span><span>8080</span></div></div><div><div><div>9</div></div><div><span># 或</span></div></div><div><div><div>10</div></div><div><span>nc</span><span> </span><span>127.0.0.1</span><span> </span><span>8080</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、多端口监听<a href="#六多端口监听"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>evbuffer_add_buffer</span><span>(output, input);</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>16</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>}</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>21</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>int</span><span> port </span><span>=</span><span> </span><span>*</span><span>(</span><span>int*</span><span>)ctx;</span></div></div><div><div><div>24</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connection on port </span><span>%d</span><span>, fd=</span><span>%d</span><span>\n</span><span>"</span><span>, port, fd);</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>30</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>31</div></div><div><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>34</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>int</span><span> ports</span><span>[]</span><span> </span><span>=</span><span> {</span><span>8080</span><span>, </span><span>8081</span><span>, </span><span>8082</span><span>};</span></div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listeners</span><span>[</span><span>3</span><span>];</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>3</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>40</div></div><div><span>        </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>41</div></div><div><span>        </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>42</div></div><div><span>        </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>43</div></div><div><span>        </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>ports</span><span>[i]);</span></div></div><div><div><div>44</div></div><div><span>        </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>        </span><span>listeners</span><span>[i] </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(base, accept_cb, </span><span>&amp;</span><span>ports</span><span>[i],</span></div></div><div><div><div>47</div></div><div><span><span>            </span></span><span>LEV_OPT_REUSEABLE </span><span>|</span><span> LEV_OPT_CLOSE_ON_FREE, </span><span>-</span><span>1</span><span>,</span></div></div><div><div><div>48</div></div><div><span><span>            </span></span><span>(</span><span>struct</span><span> sockaddr</span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Listening on port </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>ports</span><span>[i]);</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> </span><span>3</span><span>; i</span><span>++</span><span>) {</span></div></div><div><div><div>56</div></div><div><span>        </span><span>evconnlistener_free</span><span>(</span><span>listeners</span><span>[i]);</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>58</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>七、使用注意事项<a href="#七使用注意事项"><span>#</span></a></h2>

<table><thead><tr><th>注意事项</th><th>说明</th></tr></thead><tbody><tr><td><strong>backlog参数</strong></td><td>-1使用默认值128，高并发场景建议设置更大的值</td></tr><tr><td><strong>SO_REUSEADDR</strong></td><td>建议启用，允许端口重用</td></tr><tr><td><strong>错误处理</strong></td><td>必须设置错误回调，处理监听错误</td></tr><tr><td><strong>资源释放</strong></td><td>使用LEV_OPT_CLOSE_ON_FREE自动关闭socket</td></tr><tr><td><strong>线程安全</strong></td><td>多线程环境下使用LEV_OPT_THREADSAFE</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-tcp-server-client/</id>
      <title type="text">🌐 TCP服务端与客户端 - libevent源码深度解析系列（十）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-tcp-server-client/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🌐 TCP服务端与客户端<a href="#-tcp服务端与客户端"><span>#</span></a></h1><section><h2>一、TCP服务端<a href="#一tcp服务端"><span>#</span></a></h2><section><h3>1.1 使用event实现<a href="#11-使用event实现"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>#define</span><span> </span><span>PORT</span><span> </span><span>8080</span></div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>BUFFER_SIZE</span><span> </span><span>1024</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>struct</span><span> connection {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>read_event;</span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>write_event;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>char</span><span> </span><span>buffer</span><span>[BUFFER_SIZE];</span></div></div><div><div><div>16</div></div><div><span>    </span><span>size_t</span><span> buffer_len;</span></div></div><div><div><div>17</div></div><div><span>};</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>struct</span><span> connection </span><span>*</span><span>conn </span><span>=</span><span> (</span><span>struct</span><span> connection </span><span>*</span><span>)arg;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>ssize_t</span><span> n </span><span>=</span><span> </span><span>recv</span><span>(fd, </span><span>conn</span><span>-&gt;</span><span>buffer</span><span>, BUFFER_SIZE </span><span>-</span><span> </span><span>1</span><span>, </span><span>0</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span> (n </span><span>&lt;=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>24</div></div><div><span>        </span><span>if</span><span> (n </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Client disconnected</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>26</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>27</div></div><div><span>            </span><span>perror</span><span>(</span><span>"recv"</span><span>);</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span>        </span><span>event_free</span><span>(</span><span>conn</span><span>-&gt;</span><span>read_event</span><span>);</span></div></div><div><div><div>30</div></div><div><span>        </span><span>close</span><span>(fd);</span></div></div><div><div><div>31</div></div><div><span>        </span><span>free</span><span>(conn);</span></div></div><div><div><div>32</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>buffer</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>36</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Received: </span><span>%s</span><span>"</span><span>, </span><span>conn</span><span>-&gt;</span><span>buffer</span><span>);</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span>    </span><span>// 回显</span></div></div><div><div><div>39</div></div><div><span>    </span><span>send</span><span>(fd, </span><span>conn</span><span>-&gt;</span><span>buffer</span><span>, n, </span><span>0</span><span>);</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>43</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> (</span><span>struct</span><span> event_base </span><span>*</span><span>)arg;</span></div></div><div><div><div>44</div></div><div><span>    </span><span>struct</span><span> sockaddr_in client_addr;</span></div></div><div><div><div>45</div></div><div><span>    </span><span>socklen_t</span><span> client_len </span><span>=</span><span> </span><span>sizeof</span><span>(client_addr);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>int</span><span> client_fd </span><span>=</span><span> </span><span>accept</span><span>(fd, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>client_addr, </span><span>&amp;</span><span>client_len);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>if</span><span> (client_fd </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>49</div></div><div><span>        </span><span>perror</span><span>(</span><span>"accept"</span><span>);</span></div></div><div><div><div>50</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection from </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>54</div></div><div><span>           </span><span>inet_ntoa</span><span>(</span><span>client_addr</span><span>.</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>client_addr</span><span>.</span><span>sin_port</span><span>));</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>evutil_make_socket_nonblocking</span><span>(client_fd);</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>struct</span><span> connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>calloc</span><span>(</span><span>1</span><span>, </span><span>sizeof</span><span>(</span><span>struct</span><span> connection));</span></div></div><div><div><div>59</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>read_event</span><span> </span><span>=</span><span> </span><span>event_new</span><span>(base, client_fd, EV_READ </span><span>|</span><span> EV_PERSIST, read_cb, conn);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>event_add</span><span>(</span><span>conn</span><span>-&gt;</span><span>read_event</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>64</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>int</span><span> listen_fd </span><span>=</span><span> </span><span>socket</span><span>(AF_INET, SOCK_STREAM, </span><span>0</span><span>);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>evutil_make_socket_nonblocking</span><span>(listen_fd);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>int</span><span> reuse </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>70</div></div><div><span>    </span><span>setsockopt</span><span>(listen_fd, SOL_SOCKET, SO_REUSEADDR, </span><span>&amp;</span><span>reuse, </span><span>sizeof</span><span>(reuse));</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>    </span><span>struct</span><span> sockaddr_in server_addr;</span></div></div><div><div><div>73</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>server_addr, </span><span>0</span><span>, </span><span>sizeof</span><span>(server_addr));</span></div></div><div><div><div>74</div></div><div><span>    </span><span>server_addr</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>75</div></div><div><span>    </span><span>server_addr</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(PORT);</span></div></div><div><div><div>76</div></div><div><span>    </span><span>server_addr</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> INADDR_ANY;</span></div></div><div><div><div>77</div></div><div>
</div></div><div><div><div>78</div></div><div><span>    </span><span>bind</span><span>(listen_fd, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>server_addr, </span><span>sizeof</span><span>(server_addr));</span></div></div><div><div><div>79</div></div><div><span>    </span><span>listen</span><span>(listen_fd, </span><span>10</span><span>);</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server listening on port </span><span>%d</span><span>\n</span><span>"</span><span>, PORT);</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>accept_event </span><span>=</span><span> </span><span>event_new</span><span>(base, listen_fd, EV_READ </span><span>|</span><span> EV_PERSIST, accept_cb, base);</span></div></div><div><div><div>84</div></div><div><span>    </span><span>event_add</span><span>(accept_event, </span><span>NULL</span><span>);</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>87</div></div><div>
</div></div><div><div><div>88</div></div><div><span>    </span><span>event_free</span><span>(accept_event);</span></div></div><div><div><div>89</div></div><div><span>    </span><span>close</span><span>(listen_fd);</span></div></div><div><div><div>90</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>93</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>1.2 使用bufferevent实现<a href="#12-使用bufferevent实现"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>PORT</span><span> </span><span>8080</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>line;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>size_t</span><span> len;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>while</span><span> ((line </span><span>=</span><span> </span><span>evbuffer_readln</span><span>(input, </span><span>&amp;</span><span>len, EVBUFFER_EOL_LF)) </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>21</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received: </span><span>%s</span><span>\n</span><span>"</span><span>, line);</span></div></div><div><div><div>22</div></div><div><span>        </span><span>evbuffer_add_printf</span><span>(output, </span><span>"Echo: </span><span>%s</span><span>\n</span><span>"</span><span>, line);</span></div></div><div><div><div>23</div></div><div><span>        </span><span>free</span><span>(line);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>28</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_EOF) {</span></div></div><div><div><div>29</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed.</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>31</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Got an error on the connection: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>strerror</span><span>(errno));</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>33</div></div><div><span>    </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_conn_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>37</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> sockaddr_in </span><span>*</span><span>sin </span><span>=</span><span> (</span><span>struct</span><span> sockaddr_in </span><span>*</span><span>)address;</span></div></div><div><div><div>40</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection from </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>41</div></div><div><span>           </span><span>inet_ntoa</span><span>(</span><span>sin</span><span>-&gt;</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>sin</span><span>-&gt;</span><span>sin_port</span><span>));</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_error_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>51</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>int</span><span> err </span><span>=</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>();</span></div></div><div><div><div>53</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Listener error </span><span>%d</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>, err, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>54</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sig</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>58</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> ctx;</span></div></div><div><div><div>59</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught signal </span><span>%d</span><span>, exiting...</span><span>\n</span><span>"</span><span>, sig);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>61</div></div><div><span>}</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>64</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>67</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>68</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>69</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(PORT);</span></div></div><div><div><div>70</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(base,</span></div></div><div><div><div>73</div></div><div><span><span>        </span></span><span>accept_conn_cb, </span><span>NULL</span><span>, LEV_OPT_REUSEABLE </span><span>|</span><span> LEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>74</div></div><div><span>        </span><span>-</span><span>1</span><span>, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>listener) {</span></div></div><div><div><div>77</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Couldn't create listener"</span><span>);</span></div></div><div><div><div>78</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>79</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>80</div></div><div>
</div></div><div><div><div>81</div></div><div><span>    </span><span>evconnlistener_set_error_cb</span><span>(listener, accept_error_cb);</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>signal_event </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, base);</span></div></div><div><div><div>84</div></div><div><span>    </span><span>event_add</span><span>(signal_event, </span><span>NULL</span><span>);</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server listening on port </span><span>%d</span><span>\n</span><span>"</span><span>, PORT);</span></div></div><div><div><div>87</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>90</div></div><div><span>    </span><span>event_free</span><span>(signal_event);</span></div></div><div><div><div>91</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>92</div></div><div>
</div></div><div><div><div>93</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>94</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>二、TCP客户端<a href="#二tcp客户端"><span>#</span></a></h2><section><h3>2.1 使用bufferevent实现<a href="#21-使用bufferevent实现"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>SERVER_IP</span><span> </span><span>"127.0.0.1"</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>SERVER_PORT</span><span> </span><span>8080</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>    </span><span>char</span><span> </span><span>buffer</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>17</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(input, buffer, </span><span>sizeof</span><span>(buffer) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>buffer</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>20</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Server: </span><span>%s</span><span>"</span><span>, buffer);</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>25</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> ctx;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected to server!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>        </span><span>// 发送消息</span></div></div><div><div><div>31</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, </span><span>"Hello, Server!</span><span>\n</span><span>"</span><span>, </span><span>15</span><span>);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>        </span><span>// 设置读回调</span></div></div><div><div><div>34</div></div><div><span>        </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, base);</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_EOF) {</span></div></div><div><div><div>36</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed.</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>37</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>39</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>strerror</span><span>(errno));</span></div></div><div><div><div>40</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>42</div></div><div><span>}</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>45</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, </span><span>-</span><span>1</span><span>, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>49</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>50</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>51</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(SERVER_PORT);</span></div></div><div><div><div>52</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, SERVER_IP, </span><span>&amp;</span><span>sin</span><span>.</span><span>sin_addr</span><span>);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, </span><span>NULL</span><span>, </span><span>NULL</span><span>, event_cb, base);</span></div></div><div><div><div>55</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>if</span><span> (</span><span>bufferevent_socket_connect</span><span>(bev, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin)) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>58</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Connection failed"</span><span>);</span></div></div><div><div><div>59</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>60</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>61</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>62</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>70</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.2 交互式客户端<a href="#22-交互式客户端"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>SERVER_IP</span><span> </span><span>"127.0.0.1"</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>SERVER_PORT</span><span> </span><span>8080</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>struct</span><span> bufferevent </span><span>*</span><span>g_bev </span><span>=</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>stdin_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>16</div></div><div><span>    </span><span>char</span><span> </span><span>buffer</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>17</div></div><div><span>    </span><span>ssize_t</span><span> n </span><span>=</span><span> </span><span>read</span><span>(fd, buffer, </span><span>sizeof</span><span>(buffer) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (n </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>buffer</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>20</div></div><div><span>        </span><span>if</span><span> (g_bev) {</span></div></div><div><div><div>21</div></div><div><span>            </span><span>bufferevent_write</span><span>(g_bev, buffer, n);</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>server_read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>27</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>char</span><span> </span><span>buffer</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>30</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>31</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(input, buffer, </span><span>sizeof</span><span>(buffer) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>32</div></div><div><span>        </span><span>buffer</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>33</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>%s</span><span>"</span><span>, buffer);</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span>    </span><span>printf</span><span>(</span><span>"&gt; "</span><span>);</span></div></div><div><div><div>36</div></div><div><span>    </span><span>fflush</span><span>(stdout);</span></div></div><div><div><div>37</div></div><div><span>}</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>server_event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> ctx;</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>43</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected to server!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>44</div></div><div><span>        </span><span>printf</span><span>(</span><span>"&gt; "</span><span>);</span></div></div><div><div><div>45</div></div><div><span>        </span><span>fflush</span><span>(stdout);</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> (BEV_EVENT_EOF </span><span>|</span><span> BEV_EVENT_ERROR)) {</span></div></div><div><div><div>47</div></div><div><span>        </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Connection closed.</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>48</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>50</div></div><div><span>}</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>53</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>g_bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, </span><span>-</span><span>1</span><span>, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>57</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>58</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>59</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(SERVER_PORT);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, SERVER_IP, </span><span>&amp;</span><span>sin</span><span>.</span><span>sin_addr</span><span>);</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(g_bev, server_read_cb, </span><span>NULL</span><span>, server_event_cb, base);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>bufferevent_enable</span><span>(g_bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>// 监听标准输入</span></div></div><div><div><div>66</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>stdin_event </span><span>=</span><span> </span><span>event_new</span><span>(base, STDIN_FILENO, EV_READ </span><span>|</span><span> EV_PERSIST, stdin_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>event_add</span><span>(stdin_event, </span><span>NULL</span><span>);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>if</span><span> (</span><span>bufferevent_socket_connect</span><span>(g_bev, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin)) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>70</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Connection failed"</span><span>);</span></div></div><div><div><div>71</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>72</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>75</div></div><div>
</div></div><div><div><div>76</div></div><div><span>    </span><span>event_free</span><span>(stdin_event);</span></div></div><div><div><div>77</div></div><div><span>    </span><span>bufferevent_free</span><span>(g_bev);</span></div></div><div><div><div>78</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>79</div></div><div>
</div></div><div><div><div>80</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>81</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、编译运行<a href="#三编译运行"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译服务端</span></div></div><div><div><div>2</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>server</span><span> </span><span>server.c</span><span> </span><span>-levent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 编译客户端</span></div></div><div><div><div>5</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>client</span><span> </span><span>client.c</span><span> </span><span>-levent</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 运行服务端</span></div></div><div><div><div>8</div></div><div><span>./server</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 运行客户端</span></div></div><div><div><div>11</div></div><div><span>./client</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>四、测试方法<a href="#四测试方法"><span>#</span></a></h2><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用telnet测试</span></div></div><div><div><div>2</div></div><div><span>telnet</span><span> </span><span>127.0.0.1</span><span> </span><span>8080</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用nc测试</span></div></div><div><div><div>5</div></div><div><span>nc</span><span> </span><span>127.0.0.1</span><span> </span><span>8080</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 使用自定义客户端</span></div></div><div><div><div>8</div></div><div><span>./client</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>五、高级实践案例<a href="#五高级实践案例"><span>#</span></a></h2><section><h3>5.1 带配置管理的TCP服务器<a href="#51-带配置管理的tcp服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// 服务器配置</span></div></div><div><div><div>13</div></div><div><span>struct</span><span> server_config {</span></div></div><div><div><div>14</div></div><div><span>    </span><span>int</span><span> port;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>int</span><span> backlog;</span></div></div><div><div><div>16</div></div><div><span>    </span><span>int</span><span> max_connections;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>int</span><span> timeout;</span></div></div><div><div><div>18</div></div><div><span>};</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>// 连接管理</span></div></div><div><div><div>21</div></div><div><span>struct</span><span> connection {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>int</span><span> fd;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev;</span></div></div><div><div><div>24</div></div><div><span>    </span><span>struct</span><span> sockaddr_in addr;</span></div></div><div><div><div>25</div></div><div><span>    </span><span>time_t</span><span> last_activity;</span></div></div><div><div><div>26</div></div><div><span>};</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>static</span><span> </span><span>struct</span><span> server_config config </span><span>=</span><span> {</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>.port </span><span>=</span><span> </span><span>8080</span><span>,</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>.backlog </span><span>=</span><span> </span><span>10</span><span>,</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>.max_connections </span><span>=</span><span> </span><span>100</span><span>,</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>.timeout </span><span>=</span><span> </span><span>300</span><span> // 5分钟超时</span></div></div><div><div><div>33</div></div><div><span>};</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>static</span><span> </span><span>int</span><span> connection_count </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>38</div></div><div><span>    </span><span>struct</span><span> connection </span><span>*</span><span>conn </span><span>=</span><span> (</span><span>struct</span><span> connection </span><span>*</span><span>)ctx;</span></div></div><div><div><div>39</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>line;</span></div></div><div><div><div>43</div></div><div><span>    </span><span>size_t</span><span> len;</span></div></div><div><div><div>44</div></div><div><span>    </span><span>while</span><span> ((line </span><span>=</span><span> </span><span>evbuffer_readln</span><span>(input, </span><span>&amp;</span><span>len, EVBUFFER_EOL_LF)) </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>45</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Received from </span><span>%s</span><span>:</span><span>%d</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>46</div></div><div><span>               </span><span>inet_ntoa</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_port</span><span>), line);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>        </span><span>// 处理命令</span></div></div><div><div><div>49</div></div><div><span>        </span><span>if</span><span> (</span><span>strcmp</span><span>(line, </span><span>"ping"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>50</div></div><div><span>            </span><span>evbuffer_add_printf</span><span>(output, </span><span>"pong</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>51</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>strcmp</span><span>(line, </span><span>"info"</span><span>) </span><span>==</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>52</div></div><div><span>            </span><span>evbuffer_add_printf</span><span>(output, </span><span>"Server: libevent TCP server</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>53</div></div><div><span>            </span><span>evbuffer_add_printf</span><span>(output, </span><span>"Connections: </span><span>%d</span><span>\n</span><span>"</span><span>, connection_count);</span></div></div><div><div><div>54</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>55</div></div><div><span>            </span><span>evbuffer_add_printf</span><span>(output, </span><span>"Echo: </span><span>%s</span><span>\n</span><span>"</span><span>, line);</span></div></div><div><div><div>56</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>57</div></div><div><span>        </span><span>free</span><span>(line);</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>// 更新活动时间</span></div></div><div><div><div>61</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>last_activity</span><span> </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>62</div></div><div><span>}</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>65</div></div><div><span>    </span><span>struct</span><span> connection </span><span>*</span><span>conn </span><span>=</span><span> (</span><span>struct</span><span> connection </span><span>*</span><span>)ctx;</span></div></div><div><div><div>66</div></div><div>
</div></div><div><div><div>67</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_EOF) {</span></div></div><div><div><div>68</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed by </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>69</div></div><div><span>               </span><span>inet_ntoa</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_port</span><span>));</span></div></div><div><div><div>70</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>71</div></div><div><span>        </span><span>int</span><span> err </span><span>=</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>();</span></div></div><div><div><div>72</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Error on connection from </span><span>%s</span><span>:</span><span>%d</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>73</div></div><div><span>               </span><span>inet_ntoa</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_port</span><span>),</span></div></div><div><div><div>74</div></div><div><span>               </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>75</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_TIMEOUT) {</span></div></div><div><div><div>76</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection timeout from </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>77</div></div><div><span>               </span><span>inet_ntoa</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_port</span><span>));</span></div></div><div><div><div>78</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>79</div></div><div>
</div></div><div><div><div>80</div></div><div><span>    </span><span>bufferevent_free</span><span>(</span><span>conn</span><span>-&gt;</span><span>bev</span><span>);</span></div></div><div><div><div>81</div></div><div><span>    </span><span>free</span><span>(conn);</span></div></div><div><div><div>82</div></div><div><span><span>    </span></span><span>connection_count</span><span>--</span><span>;</span></div></div><div><div><div>83</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connection count: </span><span>%d</span><span>\n</span><span>"</span><span>, connection_count);</span></div></div><div><div><div>84</div></div><div><span>}</span></div></div><div><div><div>85</div></div><div>
</div></div><div><div><div>86</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_conn_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>87</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>88</div></div><div>
</div></div><div><div><div>89</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>90</div></div><div><span>    </span><span>struct</span><span> connection </span><span>*</span><span>conn </span><span>=</span><span> </span><span>calloc</span><span>(</span><span>1</span><span>, </span><span>sizeof</span><span>(</span><span>struct</span><span> connection));</span></div></div><div><div><div>91</div></div><div>
</div></div><div><div><div>92</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>conn) {</span></div></div><div><div><div>93</div></div><div><span>        </span><span>perror</span><span>(</span><span>"calloc"</span><span>);</span></div></div><div><div><div>94</div></div><div><span>        </span><span>close</span><span>(fd);</span></div></div><div><div><div>95</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>96</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>    </span><span>// 检查连接数限制</span></div></div><div><div><div>99</div></div><div><span>    </span><span>if</span><span> (connection_count </span><span>&gt;=</span><span> </span><span>config</span><span>.</span><span>max_connections</span><span>) {</span></div></div><div><div><div>100</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection limit reached, rejecting connection</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>101</div></div><div><span>        </span><span>close</span><span>(fd);</span></div></div><div><div><div>102</div></div><div><span>        </span><span>free</span><span>(conn);</span></div></div><div><div><div>103</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>104</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>105</div></div><div>
</div></div><div><div><div>106</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>fd</span><span> </span><span>=</span><span> fd;</span></div></div><div><div><div>107</div></div><div><span>    </span><span>memcpy</span><span>(</span><span>&amp;</span><span>conn</span><span>-&gt;</span><span>addr</span><span>, address, </span><span>sizeof</span><span>(</span><span>struct</span><span> sockaddr_in));</span></div></div><div><div><div>108</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>last_activity</span><span> </span><span>=</span><span> </span><span>time</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span>    </span><span>// 创建bufferevent</span></div></div><div><div><div>111</div></div><div><span>    </span><span>conn</span><span>-&gt;</span><span>bev</span><span> </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>112</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>conn</span><span>-&gt;</span><span>bev</span><span>) {</span></div></div><div><div><div>113</div></div><div><span>        </span><span>perror</span><span>(</span><span>"bufferevent_socket_new"</span><span>);</span></div></div><div><div><div>114</div></div><div><span>        </span><span>close</span><span>(fd);</span></div></div><div><div><div>115</div></div><div><span>        </span><span>free</span><span>(conn);</span></div></div><div><div><div>116</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>117</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>118</div></div><div>
</div></div><div><div><div>119</div></div><div><span>    </span><span>// 设置回调</span></div></div><div><div><div>120</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(</span><span>conn</span><span>-&gt;</span><span>bev</span><span>, read_cb, </span><span>NULL</span><span>, event_cb, conn);</span></div></div><div><div><div>121</div></div><div>
</div></div><div><div><div>122</div></div><div><span>    </span><span>// 设置超时</span></div></div><div><div><div>123</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>config</span><span>.</span><span>timeout</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>124</div></div><div><span>    </span><span>bufferevent_set_timeouts</span><span>(</span><span>conn</span><span>-&gt;</span><span>bev</span><span>, </span><span>&amp;</span><span>tv, </span><span>NULL</span><span>);</span></div></div><div><div><div>125</div></div><div>
</div></div><div><div><div>126</div></div><div><span>    </span><span>// 启用读写</span></div></div><div><div><div>127</div></div><div><span>    </span><span>bufferevent_enable</span><span>(</span><span>conn</span><span>-&gt;</span><span>bev</span><span>, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>128</div></div><div>
</div></div><div><div><div>129</div></div><div><span><span>    </span></span><span>connection_count</span><span>++</span><span>;</span></div></div><div><div><div>130</div></div><div><span>    </span><span>printf</span><span>(</span><span>"New connection from </span><span>%s</span><span>:</span><span>%d</span><span>, count: </span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>131</div></div><div><span>           </span><span>inet_ntoa</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_addr</span><span>), </span><span>ntohs</span><span>(</span><span>conn</span><span>-&gt;</span><span>addr</span><span>.</span><span>sin_port</span><span>), connection_count);</span></div></div><div><div><div>132</div></div><div><span>}</span></div></div><div><div><div>133</div></div><div>
</div></div><div><div><div>134</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_error_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>135</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evconnlistener_get_base</span><span>(listener);</span></div></div><div><div><div>136</div></div><div><span>    </span><span>int</span><span> err </span><span>=</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>();</span></div></div><div><div><div>137</div></div><div><span>    </span><span>fprintf</span><span>(stderr, </span><span>"Listener error </span><span>%d</span><span>: </span><span>%s</span><span>\n</span><span>"</span><span>, err, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>138</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>139</div></div><div><span>}</span></div></div><div><div><div>140</div></div><div>
</div></div><div><div><div>141</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sig</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>142</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> ctx;</span></div></div><div><div><div>143</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught signal </span><span>%d</span><span>, shutting down...</span><span>\n</span><span>"</span><span>, sig);</span></div></div><div><div><div>144</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>145</div></div><div><span>}</span></div></div><div><div><div>146</div></div><div>
</div></div><div><div><div>147</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>load_config</span><span>(</span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span><span>config_file</span><span>) {</span></span></div></div><div><div><div>148</div></div><div><span>    </span><span>// 这里可以实现从配置文件加载配置</span></div></div><div><div><div>149</div></div><div><span>    </span><span>// 简化版本，使用默认配置</span></div></div><div><div><div>150</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Loaded config: port=</span><span>%d</span><span>, backlog=</span><span>%d</span><span>, max_conn=</span><span>%d</span><span>, timeout=</span><span>%d</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>151</div></div><div><span>           </span><span>config</span><span>.</span><span>port</span><span>, </span><span>config</span><span>.</span><span>backlog</span><span>, </span><span>config</span><span>.</span><span>max_connections</span><span>, </span><span>config</span><span>.</span><span>timeout</span><span>);</span></div></div><div><div><div>152</div></div><div><span>}</span></div></div><div><div><div>153</div></div><div>
</div></div><div><div><div>154</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>char</span><span> </span><span>**</span><span><span>argv</span><span>) {</span></span></div></div><div><div><div>155</div></div><div><span>    </span><span>// 加载配置</span></div></div><div><div><div>156</div></div><div><span>    </span><span>if</span><span> (argc </span><span>&gt;</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>157</div></div><div><span>        </span><span>load_config</span><span>(</span><span>argv</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>158</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>159</div></div><div><span>        </span><span>load_config</span><span>(</span><span>NULL</span><span>);</span></div></div><div><div><div>160</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>161</div></div><div>
</div></div><div><div><div>162</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>163</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>164</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>165</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>166</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>167</div></div><div>
</div></div><div><div><div>168</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>169</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>170</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>171</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(</span><span>config</span><span>.</span><span>port</span><span>);</span></div></div><div><div><div>172</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>173</div></div><div>
</div></div><div><div><div>174</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(base,</span></div></div><div><div><div>175</div></div><div><span><span>        </span></span><span>accept_conn_cb, </span><span>NULL</span><span>,</span></div></div><div><div><div>176</div></div><div><span><span>        </span></span><span>LEV_OPT_REUSEABLE </span><span>|</span><span> LEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>177</div></div><div><span>        </span><span>config</span><span>.</span><span>backlog</span><span>,</span></div></div><div><div><div>178</div></div><div><span><span>        </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>179</div></div><div>
</div></div><div><div><div>180</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>listener) {</span></div></div><div><div><div>181</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Couldn't create listener"</span><span>);</span></div></div><div><div><div>182</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>183</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>184</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>185</div></div><div>
</div></div><div><div><div>186</div></div><div><span>    </span><span>evconnlistener_set_error_cb</span><span>(listener, accept_error_cb);</span></div></div><div><div><div>187</div></div><div>
</div></div><div><div><div>188</div></div><div><span>    </span><span>// 信号处理</span></div></div><div><div><div>189</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>signal_event </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, base);</span></div></div><div><div><div>190</div></div><div><span>    </span><span>event_add</span><span>(signal_event, </span><span>NULL</span><span>);</span></div></div><div><div><div>191</div></div><div>
</div></div><div><div><div>192</div></div><div><span>    </span><span>printf</span><span>(</span><span>"TCP server started on port </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>config</span><span>.</span><span>port</span><span>);</span></div></div><div><div><div>193</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Max connections: </span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>config</span><span>.</span><span>max_connections</span><span>);</span></div></div><div><div><div>194</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connection timeout: </span><span>%d</span><span> seconds</span><span>\n</span><span>"</span><span>, </span><span>config</span><span>.</span><span>timeout</span><span>);</span></div></div><div><div><div>195</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to exit</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>196</div></div><div>
</div></div><div><div><div>197</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>198</div></div><div>
</div></div><div><div><div>199</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown, cleaning up...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>200</div></div><div>
</div></div><div><div><div>201</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>202</div></div><div><span>    </span><span>event_free</span><span>(signal_event);</span></div></div><div><div><div>203</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>204</div></div><div>
</div></div><div><div><div>205</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>206</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>207</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 多线程TCP服务器<a href="#52-多线程tcp服务器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;pthread.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;event2/listener.h&gt;</span></div></div><div><div><div>11</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>12</div></div><div><span>#include</span><span> </span><span>&lt;event2/thread.h&gt;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>#define</span><span> </span><span>THREAD_COUNT</span><span> </span><span>4</span></div></div><div><div><div>15</div></div><div><span>#define</span><span> </span><span>PORT</span><span> </span><span>8080</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>struct</span><span> thread_info {</span></div></div><div><div><div>18</div></div><div><span>    </span><span>pthread_t</span><span> thread_id;</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>int</span><span> </span><span>pipefd</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>21</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>pipe_event;</span></div></div><div><div><div>22</div></div><div><span>};</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>static</span><span> </span><span>struct</span><span> thread_info </span><span>*</span><span>threads;</span></div></div><div><div><div>25</div></div><div><span>static</span><span> </span><span>int</span><span> next_thread </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>26</div></div><div><span>static</span><span> </span><span>pthread_mutex_t</span><span> thread_lock </span><span>=</span><span> PTHREAD_MUTEX_INITIALIZER;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>30</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>output </span><span>=</span><span> </span><span>bufferevent_get_output</span><span>(bev);</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>line;</span></div></div><div><div><div>33</div></div><div><span>    </span><span>size_t</span><span> len;</span></div></div><div><div><div>34</div></div><div><span>    </span><span>while</span><span> ((line </span><span>=</span><span> </span><span>evbuffer_readln</span><span>(input, </span><span>&amp;</span><span>len, EVBUFFER_EOL_LF)) </span><span>!=</span><span> </span><span>NULL</span><span>) {</span></div></div><div><div><div>35</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Thread </span><span>%ld</span><span>: Received: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>pthread_self</span><span>(), line);</span></div></div><div><div><div>36</div></div><div><span>        </span><span>evbuffer_add_printf</span><span>(output, </span><span>"Echo: </span><span>%s</span><span>\n</span><span>"</span><span>, line);</span></div></div><div><div><div>37</div></div><div><span>        </span><span>free</span><span>(line);</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span>}</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>42</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_EOF) {</span></div></div><div><div><div>43</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Thread </span><span>%ld</span><span>: Connection closed</span><span>\n</span><span>"</span><span>, </span><span>pthread_self</span><span>());</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>45</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Thread </span><span>%ld</span><span>: Connection error</span><span>\n</span><span>"</span><span>, </span><span>pthread_self</span><span>());</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>47</div></div><div><span>    </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>pipe_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>51</div></div><div><span>    </span><span>struct</span><span> thread_info </span><span>*</span><span>info </span><span>=</span><span> (</span><span>struct</span><span> thread_info </span><span>*</span><span>)arg;</span></div></div><div><div><div>52</div></div><div><span>    </span><span>int</span><span> client_fd;</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>// 从管道读取客户端fd</span></div></div><div><div><div>55</div></div><div><span>    </span><span>ssize_t</span><span> n </span><span>=</span><span> </span><span>read</span><span>(fd, </span><span>&amp;</span><span>client_fd, </span><span>sizeof</span><span>(client_fd));</span></div></div><div><div><div>56</div></div><div><span>    </span><span>if</span><span> (n </span><span>!=</span><span> </span><span>sizeof</span><span>(client_fd)) {</span></div></div><div><div><div>57</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Error reading from pipe</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>58</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span>    </span><span>// 创建bufferevent</span></div></div><div><div><div>62</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(</span><span>info</span><span>-&gt;</span><span>base</span><span>, client_fd, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>bev) {</span></div></div><div><div><div>64</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Error creating bufferevent</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>65</div></div><div><span>        </span><span>close</span><span>(client_fd);</span></div></div><div><div><div>66</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>// 设置回调</span></div></div><div><div><div>70</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>71</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>72</div></div><div><span>}</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>*</span><span>thread_main</span><span>(</span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>75</div></div><div><span>    </span><span>struct</span><span> thread_info </span><span>*</span><span>info </span><span>=</span><span> (</span><span>struct</span><span> thread_info </span><span>*</span><span>)arg;</span></div></div><div><div><div>76</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Thread </span><span>%ld</span><span> started</span><span>\n</span><span>"</span><span>, </span><span>info</span><span>-&gt;</span><span>thread_id</span><span>);</span></div></div><div><div><div>77</div></div><div><span>    </span><span>event_base_dispatch</span><span>(</span><span>info</span><span>-&gt;</span><span>base</span><span>);</span></div></div><div><div><div>78</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Thread </span><span>%ld</span><span> exiting</span><span>\n</span><span>"</span><span>, </span><span>info</span><span>-&gt;</span><span>thread_id</span><span>);</span></div></div><div><div><div>79</div></div><div><span>    </span><span>return</span><span> </span><span>NULL</span><span>;</span></div></div><div><div><div>80</div></div><div><span>}</span></div></div><div><div><div>81</div></div><div>
</div></div><div><div><div>82</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>accept_conn_cb</span><span>(</span><span>struct</span><span> evconnlistener </span><span>*</span><span><span>listener</span><span>,</span></span></div></div><div><div><div>83</div></div><div><span>    </span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>struct</span><span> sockaddr </span><span>*</span><span><span>address</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>socklen</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>84</div></div><div><span>    </span><span>// 轮询选择线程</span></div></div><div><div><div>85</div></div><div><span>    </span><span>pthread_mutex_lock</span><span>(</span><span>&amp;</span><span>thread_lock);</span></div></div><div><div><div>86</div></div><div><span>    </span><span>struct</span><span> thread_info </span><span>*</span><span>info </span><span>=</span><span> </span><span>&amp;</span><span>threads</span><span>[next_thread];</span></div></div><div><div><div>87</div></div><div><span><span>    </span></span><span>next_thread </span><span>=</span><span> (next_thread </span><span>+</span><span> </span><span>1</span><span>) </span><span>%</span><span> THREAD_COUNT;</span></div></div><div><div><div>88</div></div><div><span>    </span><span>pthread_mutex_unlock</span><span>(</span><span>&amp;</span><span>thread_lock);</span></div></div><div><div><div>89</div></div><div>
</div></div><div><div><div>90</div></div><div><span>    </span><span>// 发送fd到线程</span></div></div><div><div><div>91</div></div><div><span>    </span><span>ssize_t</span><span> n </span><span>=</span><span> </span><span>write</span><span>(</span><span>info</span><span>-&gt;</span><span>pipefd</span><span>[</span><span>1</span><span>], </span><span>&amp;</span><span>fd, </span><span>sizeof</span><span>(fd));</span></div></div><div><div><div>92</div></div><div><span>    </span><span>if</span><span> (n </span><span>!=</span><span> </span><span>sizeof</span><span>(fd)) {</span></div></div><div><div><div>93</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Error writing to pipe</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>94</div></div><div><span>        </span><span>close</span><span>(fd);</span></div></div><div><div><div>95</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>96</div></div><div><span>}</span></div></div><div><div><div>97</div></div><div>
</div></div><div><div><div>98</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>sig</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>99</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> ctx;</span></div></div><div><div><div>100</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught signal </span><span>%d</span><span>, shutting down...</span><span>\n</span><span>"</span><span>, sig);</span></div></div><div><div><div>101</div></div><div>
</div></div><div><div><div>102</div></div><div><span>    </span><span>// 停止所有线程</span></div></div><div><div><div>103</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> THREAD_COUNT; i</span><span>++</span><span>) {</span></div></div><div><div><div>104</div></div><div><span>        </span><span>event_base_loopbreak</span><span>(</span><span>threads</span><span>[i].</span><span>base</span><span>);</span></div></div><div><div><div>105</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>106</div></div><div>
</div></div><div><div><div>107</div></div><div><span>    </span><span>event_base_loopbreak</span><span>(base);</span></div></div><div><div><div>108</div></div><div><span>}</span></div></div><div><div><div>109</div></div><div>
</div></div><div><div><div>110</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>111</div></div><div><span>    </span><span>// 初始化线程支持</span></div></div><div><div><div>112</div></div><div><span>    </span><span>if</span><span> (</span><span>evthread_use_pthreads</span><span>() </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>113</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to initialize thread support</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>114</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>115</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>116</div></div><div>
</div></div><div><div><div>117</div></div><div><span>    </span><span>// 创建主线程的event_base</span></div></div><div><div><div>118</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>119</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>base) {</span></div></div><div><div><div>120</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>121</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>122</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>123</div></div><div>
</div></div><div><div><div>124</div></div><div><span>    </span><span>// 创建工作线程</span></div></div><div><div><div>125</div></div><div><span><span>    </span></span><span>threads </span><span>=</span><span> </span><span>calloc</span><span>(THREAD_COUNT, </span><span>sizeof</span><span>(</span><span>struct</span><span> thread_info));</span></div></div><div><div><div>126</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> THREAD_COUNT; i</span><span>++</span><span>) {</span></div></div><div><div><div>127</div></div><div><span>        </span><span>// 创建管道用于传递fd</span></div></div><div><div><div>128</div></div><div><span>        </span><span>if</span><span> (</span><span>pipe</span><span>(</span><span>threads</span><span>[i].</span><span>pipefd</span><span>) </span><span>==</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>129</div></div><div><span>            </span><span>perror</span><span>(</span><span>"pipe"</span><span>);</span></div></div><div><div><div>130</div></div><div><span>            </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>131</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>132</div></div><div>
</div></div><div><div><div>133</div></div><div><span>        </span><span>// 设置管道非阻塞</span></div></div><div><div><div>134</div></div><div><span>        </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>threads</span><span>[i].</span><span>pipefd</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>135</div></div><div><span>        </span><span>evutil_make_socket_nonblocking</span><span>(</span><span>threads</span><span>[i].</span><span>pipefd</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>136</div></div><div>
</div></div><div><div><div>137</div></div><div><span>        </span><span>// 创建线程的event_base</span></div></div><div><div><div>138</div></div><div><span>        </span><span>threads</span><span>[i].</span><span>base</span><span> </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>139</div></div><div><span>        </span><span>if</span><span> (</span><span>!</span><span>threads</span><span>[i].</span><span>base</span><span>) {</span></div></div><div><div><div>140</div></div><div><span>            </span><span>fprintf</span><span>(stderr, </span><span>"Failed to create thread event base</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>141</div></div><div><span>            </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>142</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>143</div></div><div>
</div></div><div><div><div>144</div></div><div><span>        </span><span>// 创建管道事件</span></div></div><div><div><div>145</div></div><div><span>        </span><span>threads</span><span>[i].</span><span>pipe_event</span><span> </span><span>=</span><span> </span><span>event_new</span><span>(</span><span>threads</span><span>[i].</span><span>base</span><span>, </span><span>threads</span><span>[i].</span><span>pipefd</span><span>[</span><span>0</span><span>],</span></div></div><div><div><div>146</div></div><div><span><span>            </span></span><span>EV_READ </span><span>|</span><span> EV_PERSIST, pipe_cb, </span><span>&amp;</span><span>threads</span><span>[i]);</span></div></div><div><div><div>147</div></div><div><span>        </span><span>event_add</span><span>(</span><span>threads</span><span>[i].</span><span>pipe_event</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>148</div></div><div>
</div></div><div><div><div>149</div></div><div><span>        </span><span>// 创建线程</span></div></div><div><div><div>150</div></div><div><span>        </span><span>pthread_create</span><span>(</span><span>&amp;</span><span>threads</span><span>[i].</span><span>thread_id</span><span>, </span><span>NULL</span><span>, thread_main, </span><span>&amp;</span><span>threads</span><span>[i]);</span></div></div><div><div><div>151</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>152</div></div><div>
</div></div><div><div><div>153</div></div><div><span>    </span><span>// 创建监听器</span></div></div><div><div><div>154</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>155</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>156</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>157</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(PORT);</span></div></div><div><div><div>158</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_addr</span><span>.</span><span>s_addr</span><span> </span><span>=</span><span> </span><span>htonl</span><span>(INADDR_ANY);</span></div></div><div><div><div>159</div></div><div>
</div></div><div><div><div>160</div></div><div><span>    </span><span>struct</span><span> evconnlistener </span><span>*</span><span>listener </span><span>=</span><span> </span><span>evconnlistener_new_bind</span><span>(base,</span></div></div><div><div><div>161</div></div><div><span><span>        </span></span><span>accept_conn_cb, </span><span>NULL</span><span>,</span></div></div><div><div><div>162</div></div><div><span><span>        </span></span><span>LEV_OPT_REUSEABLE </span><span>|</span><span> LEV_OPT_CLOSE_ON_FREE,</span></div></div><div><div><div>163</div></div><div><span>        </span><span>-</span><span>1</span><span>,</span></div></div><div><div><div>164</div></div><div><span><span>        </span></span><span>(</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>165</div></div><div>
</div></div><div><div><div>166</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span>listener) {</span></div></div><div><div><div>167</div></div><div><span>        </span><span>perror</span><span>(</span><span>"Couldn't create listener"</span><span>);</span></div></div><div><div><div>168</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>169</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>170</div></div><div>
</div></div><div><div><div>171</div></div><div><span>    </span><span>// 信号处理</span></div></div><div><div><div>172</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>signal_event </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, base);</span></div></div><div><div><div>173</div></div><div><span>    </span><span>event_add</span><span>(signal_event, </span><span>NULL</span><span>);</span></div></div><div><div><div>174</div></div><div>
</div></div><div><div><div>175</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Multi-threaded TCP server started on port </span><span>%d</span><span>\n</span><span>"</span><span>, PORT);</span></div></div><div><div><div>176</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Thread count: </span><span>%d</span><span>\n</span><span>"</span><span>, THREAD_COUNT);</span></div></div><div><div><div>177</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to exit</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>178</div></div><div>
</div></div><div><div><div>179</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>180</div></div><div>
</div></div><div><div><div>181</div></div><div><span>    </span><span>// 等待所有线程退出</span></div></div><div><div><div>182</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> i </span><span>=</span><span> </span><span>0</span><span>; i </span><span>&lt;</span><span> THREAD_COUNT; i</span><span>++</span><span>) {</span></div></div><div><div><div>183</div></div><div><span>        </span><span>pthread_join</span><span>(</span><span>threads</span><span>[i].</span><span>thread_id</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>184</div></div><div><span>        </span><span>event_free</span><span>(</span><span>threads</span><span>[i].</span><span>pipe_event</span><span>);</span></div></div><div><div><div>185</div></div><div><span>        </span><span>event_base_free</span><span>(</span><span>threads</span><span>[i].</span><span>base</span><span>);</span></div></div><div><div><div>186</div></div><div><span>        </span><span>close</span><span>(</span><span>threads</span><span>[i].</span><span>pipefd</span><span>[</span><span>0</span><span>]);</span></div></div><div><div><div>187</div></div><div><span>        </span><span>close</span><span>(</span><span>threads</span><span>[i].</span><span>pipefd</span><span>[</span><span>1</span><span>]);</span></div></div><div><div><div>188</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>189</div></div><div>
</div></div><div><div><div>190</div></div><div><span>    </span><span>free</span><span>(threads);</span></div></div><div><div><div>191</div></div><div><span>    </span><span>evconnlistener_free</span><span>(listener);</span></div></div><div><div><div>192</div></div><div><span>    </span><span>event_free</span><span>(signal_event);</span></div></div><div><div><div>193</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>194</div></div><div>
</div></div><div><div><div>195</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Server shutdown complete</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>196</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>197</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.3 带超时处理的TCP客户端<a href="#53-带超时处理的tcp客户端"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;arpa/inet.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;event2/buffer.h&gt;</span></div></div><div><div><div>7</div></div><div><span>#include</span><span> </span><span>&lt;event2/bufferevent.h&gt;</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>#define</span><span> </span><span>SERVER_IP</span><span> </span><span>"127.0.0.1"</span></div></div><div><div><div>11</div></div><div><span>#define</span><span> </span><span>SERVER_PORT</span><span> </span><span>8080</span></div></div><div><div><div>12</div></div><div><span>#define</span><span> </span><span>CONNECT_TIMEOUT</span><span> </span><span>5</span></div></div><div><div><div>13</div></div><div><span>#define</span><span> </span><span>READ_TIMEOUT</span><span> </span><span>30</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>static</span><span> </span><span>int</span><span> g_connected </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>read_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> evbuffer </span><span>*</span><span>input </span><span>=</span><span> </span><span>bufferevent_get_input</span><span>(bev);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>char</span><span> </span><span>buffer</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>21</div></div><div><span>    </span><span>size_t</span><span> n;</span></div></div><div><div><div>22</div></div><div><span>    </span><span>while</span><span> ((n </span><span>=</span><span> </span><span>evbuffer_remove</span><span>(input, buffer, </span><span>sizeof</span><span>(buffer) </span><span>-</span><span> </span><span>1</span><span>)) </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>23</div></div><div><span>        </span><span>buffer</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>24</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Server: </span><span>%s</span><span>"</span><span>, buffer);</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>event_cb</span><span>(</span><span>struct</span><span> bufferevent </span><span>*</span><span><span>bev</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>ctx</span><span>) {</span></span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> ctx;</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_CONNECTED) {</span></div></div><div><div><div>32</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connected to server!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>g_connected </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>        </span><span>// 发送测试命令</span></div></div><div><div><div>36</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, </span><span>"ping</span><span>\n</span><span>"</span><span>, </span><span>5</span><span>);</span></div></div><div><div><div>37</div></div><div><span>        </span><span>bufferevent_write</span><span>(bev, </span><span>"info</span><span>\n</span><span>"</span><span>, </span><span>5</span><span>);</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_EOF) {</span></div></div><div><div><div>39</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection closed by server</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>40</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_ERROR) {</span></div></div><div><div><div>42</div></div><div><span>        </span><span>int</span><span> err </span><span>=</span><span> </span><span>EVUTIL_SOCKET_ERROR</span><span>();</span></div></div><div><div><div>43</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Connection error: </span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evutil_socket_error_to_string</span><span>(err));</span></div></div><div><div><div>44</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_TIMEOUT) {</span></div></div><div><div><div>46</div></div><div><span>        </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_READING) {</span></div></div><div><div><div>47</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Read timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (events </span><span>&amp;</span><span> BEV_EVENT_WRITING) {</span></div></div><div><div><div>49</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Write timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>51</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>52</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>53</div></div><div><span>}</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>stdin_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>56</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> (</span><span>struct</span><span> bufferevent </span><span>*</span><span>)arg;</span></div></div><div><div><div>57</div></div><div><span>    </span><span>char</span><span> </span><span>buffer</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>58</div></div><div><span>    </span><span>ssize_t</span><span> n </span><span>=</span><span> </span><span>read</span><span>(fd, buffer, </span><span>sizeof</span><span>(buffer) </span><span>-</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>if</span><span> (n </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>61</div></div><div><span>        </span><span>buffer</span><span>[n] </span><span>=</span><span> </span><span>'</span><span>\0</span><span>'</span><span>;</span></div></div><div><div><div>62</div></div><div><span>        </span><span>if</span><span> (g_connected) {</span></div></div><div><div><div>63</div></div><div><span>            </span><span>bufferevent_write</span><span>(bev, buffer, n);</span></div></div><div><div><div>64</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>65</div></div><div><span>            </span><span>printf</span><span>(</span><span>"Not connected yet</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>66</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>68</div></div><div><span>}</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>71</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>72</div></div><div><span>    </span><span>struct</span><span> bufferevent </span><span>*</span><span>bev </span><span>=</span><span> </span><span>bufferevent_socket_new</span><span>(base, </span><span>-</span><span>1</span><span>, BEV_OPT_CLOSE_ON_FREE);</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>    </span><span>// 设置超时</span></div></div><div><div><div>75</div></div><div><span>    </span><span>struct</span><span> timeval connect_tv </span><span>=</span><span> {CONNECT_TIMEOUT, </span><span>0</span><span>};</span></div></div><div><div><div>76</div></div><div><span>    </span><span>struct</span><span> timeval read_tv </span><span>=</span><span> {READ_TIMEOUT, </span><span>0</span><span>};</span></div></div><div><div><div>77</div></div><div><span>    </span><span>bufferevent_set_timeouts</span><span>(bev, </span><span>&amp;</span><span>read_tv, </span><span>NULL</span><span>);</span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>    </span><span>// 设置回调</span></div></div><div><div><div>80</div></div><div><span>    </span><span>bufferevent_setcb</span><span>(bev, read_cb, </span><span>NULL</span><span>, event_cb, base);</span></div></div><div><div><div>81</div></div><div><span>    </span><span>bufferevent_enable</span><span>(bev, EV_READ </span><span>|</span><span> EV_WRITE);</span></div></div><div><div><div>82</div></div><div>
</div></div><div><div><div>83</div></div><div><span>    </span><span>// 连接服务器</span></div></div><div><div><div>84</div></div><div><span>    </span><span>struct</span><span> sockaddr_in sin;</span></div></div><div><div><div>85</div></div><div><span>    </span><span>memset</span><span>(</span><span>&amp;</span><span>sin, </span><span>0</span><span>, </span><span>sizeof</span><span>(sin));</span></div></div><div><div><div>86</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_family</span><span> </span><span>=</span><span> AF_INET;</span></div></div><div><div><div>87</div></div><div><span>    </span><span>sin</span><span>.</span><span>sin_port</span><span> </span><span>=</span><span> </span><span>htons</span><span>(SERVER_PORT);</span></div></div><div><div><div>88</div></div><div><span>    </span><span>inet_pton</span><span>(AF_INET, SERVER_IP, </span><span>&amp;</span><span>sin</span><span>.</span><span>sin_addr</span><span>);</span></div></div><div><div><div>89</div></div><div>
</div></div><div><div><div>90</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connecting to </span><span>%s</span><span>:</span><span>%d</span><span>...</span><span>\n</span><span>"</span><span>, SERVER_IP, SERVER_PORT);</span></div></div><div><div><div>91</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Connect timeout: </span><span>%d</span><span> seconds</span><span>\n</span><span>"</span><span>, CONNECT_TIMEOUT);</span></div></div><div><div><div>92</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Read timeout: </span><span>%d</span><span> seconds</span><span>\n</span><span>"</span><span>, READ_TIMEOUT);</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>    </span><span>if</span><span> (</span><span>bufferevent_socket_connect</span><span>(bev, (</span><span>struct</span><span> sockaddr </span><span>*</span><span>)</span><span>&amp;</span><span>sin, </span><span>sizeof</span><span>(sin)) </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>95</div></div><div><span>        </span><span>fprintf</span><span>(stderr, </span><span>"Connection failed</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>96</div></div><div><span>        </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>97</div></div><div><span>        </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>98</div></div><div><span>        </span><span>return</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>// 监听标准输入</span></div></div><div><div><div>102</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>stdin_event </span><span>=</span><span> </span><span>event_new</span><span>(base, STDIN_FILENO, EV_READ </span><span>|</span><span> EV_PERSIST, stdin_cb, bev);</span></div></div><div><div><div>103</div></div><div><span>    </span><span>event_add</span><span>(stdin_event, </span><span>NULL</span><span>);</span></div></div><div><div><div>104</div></div><div>
</div></div><div><div><div>105</div></div><div><span>    </span><span>// 设置连接超时</span></div></div><div><div><div>106</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {CONNECT_TIMEOUT, </span><span>0</span><span>};</span></div></div><div><div><div>107</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timeout_event </span><span>=</span><span> </span><span>evtimer_new</span><span>(base,</span></div></div><div><div><div>108</div></div><div><span>        </span><span>[]</span><span>(</span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>arg) {</span></div></div><div><div><div>109</div></div><div><span>            </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>110</div></div><div><span>            </span><span>if</span><span> (</span><span>!</span><span>g_connected) {</span></div></div><div><div><div>111</div></div><div><span>                </span><span>printf</span><span>(</span><span>"Connection timeout</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>112</div></div><div><span>                </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>113</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>114</div></div><div><span><span>        </span></span><span>}, base);</span></div></div><div><div><div>115</div></div><div><span>    </span><span>event_add</span><span>(timeout_event, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>116</div></div><div>
</div></div><div><div><div>117</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>118</div></div><div>
</div></div><div><div><div>119</div></div><div><span>    </span><span>event_free</span><span>(stdin_event);</span></div></div><div><div><div>120</div></div><div><span>    </span><span>event_free</span><span>(timeout_event);</span></div></div><div><div><div>121</div></div><div><span>    </span><span>bufferevent_free</span><span>(bev);</span></div></div><div><div><div>122</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>123</div></div><div>
</div></div><div><div><div>124</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>125</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、编译与运行<a href="#六编译与运行"><span>#</span></a></h2><section><h3>6.1 编译高级示例<a href="#61-编译高级示例"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 编译带配置管理的TCP服务器</span></div></div><div><div><div>2</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>tcp_server_config</span><span> </span><span>tcp_server_config.c</span><span> </span><span>-levent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 编译多线程TCP服务器</span></div></div><div><div><div>5</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>tcp_server_threaded</span><span> </span><span>tcp_server_threaded.c</span><span> </span><span>-levent</span><span> </span><span>-lpthread</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 编译带超时处理的TCP客户端</span></div></div><div><div><div>8</div></div><div><span>gcc</span><span> </span><span>-o</span><span> </span><span>tcp_client_timeout</span><span> </span><span>tcp_client_timeout.c</span><span> </span><span>-levent</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.2 运行示例<a href="#62-运行示例"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 运行带配置管理的服务器</span></div></div><div><div><div>2</div></div><div><span>./tcp_server_config</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 运行多线程服务器</span></div></div><div><div><div>5</div></div><div><span>./tcp_server_threaded</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 运行带超时处理的客户端</span></div></div><div><div><div>8</div></div><div><span>./tcp_client_timeout</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-timer-usage/</id>
      <title type="text">⏰ 定时器使用 - libevent源码深度解析系列（十二）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-timer-usage/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>⏰ 定时器使用<a href="#-定时器使用"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent提供了定时器功能，允许在指定的时间后执行回调函数。定时器可以是一次性的，也可以是周期性的。</p></section><section><h2>二、定时器API<a href="#二定时器api"><span>#</span></a></h2><section><h3>2.1 创建定时器<a href="#21-创建定时器"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 创建定时器事件（宏定义）</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param base  event_base</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* @param cb    回调函数</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>* @param arg   回调参数</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>6</div></div><div><span>#define</span><span> </span><span>evtimer_new</span><span><span>(</span><span>base</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>event_new</span><span><span>(</span><span>(</span><span>base</span><span>),</span><span> </span></span><span>-</span><span>1</span><span><span>,</span><span> </span></span><span>0</span><span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>/* 分配定时器到现有结构体 */</span></div></div><div><div><div>9</div></div><div><span>#define</span><span> </span><span>evtimer_assign</span><span><span>(</span><span>ev</span><span>, </span><span>base</span><span>, </span><span>cb</span><span>, </span><span>arg</span><span>) </span></span><span>\</span></div></div><div><div><div>10</div></div><div><span>    </span><span>event_assign</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>base</span><span>),</span><span> </span></span><span>-</span><span>1</span><span><span>,</span><span> </span></span><span>0</span><span><span>,</span><span> </span><span>(</span><span>cb</span><span>),</span><span> </span><span>(</span><span>arg</span><span>)</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 添加和删除<a href="#22-添加和删除"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加定时器</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* @param tv 超时时间，NULL表示永不超时</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>4</div></div><div><span>#define</span><span> </span><span>evtimer_add</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>) </span></span><span>event_add</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 删除定时器 */</span></div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>evtimer_del</span><span><span>(</span><span>ev</span><span>) </span></span><span>event_del</span><span>(ev)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.3 检查状态<a href="#23-检查状态"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 检查定时器是否pending */</span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>evtimer_pending</span><span><span>(</span><span>ev</span><span>, </span><span>tv</span><span>) </span></span><span>event_pending</span><span><span>(</span><span>(</span><span>ev</span><span>),</span><span> EV_TIMEOUT</span><span>,</span><span> </span><span>(</span><span>tv</span><span>)</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>/* 检查定时器是否已初始化 */</span></div></div><div><div><div>5</div></div><div><span>#define</span><span> </span><span>evtimer_initialized</span><span><span>(</span><span>ev</span><span>) </span></span><span>event_initialized</span><span>(ev)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>三、时间结构体<a href="#三时间结构体"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> timeval {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>time_t</span><span>      tv_sec;</span><span>   // 秒</span></div></div><div><div><div>3</div></div><div><span>    </span><span>suseconds_t</span><span> tv_usec;</span><span>  // 微秒</span></div></div><div><div><div>4</div></div><div><span>};</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 辅助宏 */</span></div></div><div><div><div>7</div></div><div><span>#define</span><span> </span><span>EVUTIL_SEC_TO_USEC</span><span><span>(</span><span>sec</span><span>) ((sec) </span></span><span>*</span><span> </span><span>1000000</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>四、基本示例<a href="#四基本示例"><span>#</span></a></h2><section><h3>4.1 一次性定时器<a href="#41-一次性定时器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer fired!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>// 事件循环会自动退出，因为没有其他事件</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>// 创建一次性定时器（不带EV_PERSIST）</span></div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>evtimer_new</span><span>(base, timer_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>    </span><span>// 设置3秒后触发</span></div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>3</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>19</div></div><div><span>    </span><span>evtimer_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer set for 3 seconds...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>26</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 周期性定时器<a href="#42-周期性定时器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>int</span><span> counter </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>counter</span><span>++</span><span>;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer fired! Count: </span><span>%d</span><span>\n</span><span>"</span><span>, counter);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>if</span><span> (counter </span><span>&gt;=</span><span> </span><span>5</span><span>) {</span></div></div><div><div><div>13</div></div><div><span>        </span><span>printf</span><span>(</span><span>"Reached 5 ticks, exiting...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>14</div></div><div><span>        </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>signal_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>21</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Caught signal, exiting...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>22</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>26</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>// 创建周期性定时器（带EV_PERSIST）</span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer_cb, base);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>// 设置每1秒触发一次</span></div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>33</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>    </span><span>// 信号处理</span></div></div><div><div><div>36</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>sig </span><span>=</span><span> </span><span>evsignal_new</span><span>(base, SIGINT, signal_cb, base);</span></div></div><div><div><div>37</div></div><div><span>    </span><span>event_add</span><span>(sig, </span><span>NULL</span><span>);</span></div></div><div><div><div>38</div></div><div>
</div></div><div><div><div>39</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Periodic timer started (1 second interval)</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>40</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Press Ctrl+C to exit</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>    </span><span>event_free</span><span>(sig);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>49</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、高级用法<a href="#五高级用法"><span>#</span></a></h2><section><h3>5.1 精确定时器<a href="#51-精确定时器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>precise_timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>struct</span><span> timeval now;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>evutil_gettimeofday</span><span>(</span><span>&amp;</span><span>now, </span><span>NULL</span><span>);</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer at </span><span>%ld</span><span>.</span><span>%06ld</span><span>\n</span><span>"</span><span>, </span><span>now</span><span>.</span><span>tv_sec</span><span>, </span><span>now</span><span>.</span><span>tv_usec</span><span>);</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>struct</span><span> event_config </span><span>*</span><span>cfg </span><span>=</span><span> </span><span>event_config_new</span><span>();</span></div></div><div><div><div>13</div></div><div><span>    </span><span>event_config_set_flag</span><span>(cfg, EVENT_BASE_FLAG_PRECISE_TIMER);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new_with_config</span><span>(cfg);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>event_config_free</span><span>(cfg);</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, precise_timer_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>    </span><span>// 每100毫秒触发</span></div></div><div><div><div>21</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>0</span><span>, </span><span>100000</span><span>};</span></div></div><div><div><div>22</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>// 5秒后退出</span></div></div><div><div><div>25</div></div><div><span>    </span><span>struct</span><span> timeval exit_tv </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>26</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>&amp;</span><span>exit_tv);</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>31</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 多个定时器<a href="#52-多个定时器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer1_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>6</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer 1: 1 second</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer2_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer 2: 2 seconds</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer3_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer 3: 3 seconds</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>exit_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>18</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Exit timer: exiting...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>NULL</span><span>);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>24</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>t1 </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer1_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>27</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>t2 </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer2_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>28</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>t3 </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer3_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>exit_timer </span><span>=</span><span> </span><span>evtimer_new</span><span>(base, exit_cb, base);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>struct</span><span> timeval tv1 </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>32</div></div><div><span>    </span><span>struct</span><span> timeval tv2 </span><span>=</span><span> {</span><span>2</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> timeval tv3 </span><span>=</span><span> {</span><span>3</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>34</div></div><div><span>    </span><span>struct</span><span> timeval tv_exit </span><span>=</span><span> {</span><span>10</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>event_add</span><span>(t1, </span><span>&amp;</span><span>tv1);</span></div></div><div><div><div>37</div></div><div><span>    </span><span>event_add</span><span>(t2, </span><span>&amp;</span><span>tv2);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>event_add</span><span>(t3, </span><span>&amp;</span><span>tv3);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>event_add</span><span>(exit_timer, </span><span>&amp;</span><span>tv_exit);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Multiple timers started, will run for 10 seconds...</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>event_free</span><span>(t1);</span></div></div><div><div><div>46</div></div><div><span>    </span><span>event_free</span><span>(t2);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>event_free</span><span>(t3);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>event_free</span><span>(exit_timer);</span></div></div><div><div><div>49</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>52</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.3 动态调整定时器<a href="#53-动态调整定时器"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>static</span><span> </span><span>int</span><span> interval </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>dynamic_timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> arg;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer fired with interval </span><span>%d</span><span> second(s)</span><span>\n</span><span>"</span><span>, interval);</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>    </span><span>// 动态调整间隔</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>interval</span><span>++</span><span>;</span></div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (interval </span><span>&gt;</span><span> </span><span>5</span><span>) {</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>interval </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>// 重新设置定时器</span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {interval, </span><span>0</span><span>};</span></div></div><div><div><div>20</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>24</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>// 注意：不带EV_PERSIST，手动重新添加</span></div></div><div><div><div>27</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, </span><span>0</span><span>, dynamic_timer_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>// 设置回调参数为timer本身</span></div></div><div><div><div>30</div></div><div><span>    </span><span>event_assign</span><span>(</span><span>event_get_assignment</span><span>(timer, </span><span>&amp;</span><span>base, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>, </span><span>NULL</span><span>),</span></div></div><div><div><div>31</div></div><div><span><span>                 </span></span><span>base, </span><span>-</span><span>1</span><span>, </span><span>0</span><span>, dynamic_timer_cb, timer);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {interval, </span><span>0</span><span>};</span></div></div><div><div><div>34</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>    </span><span>// 20秒后退出</span></div></div><div><div><div>37</div></div><div><span>    </span><span>struct</span><span> timeval exit_tv </span><span>=</span><span> {</span><span>20</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>38</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>&amp;</span><span>exit_tv);</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>46</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>六、定时器精度<a href="#六定时器精度"><span>#</span></a></h2>

<table><thead><tr><th>因素</th><th>影响</th></tr></thead><tbody><tr><td><strong>系统时钟精度</strong></td><td>通常为毫秒级</td></tr><tr><td><strong>事件循环负载</strong></td><td>高负载可能导致延迟</td></tr><tr><td><strong>回调执行时间</strong></td><td>长时间回调影响后续定时器</td></tr><tr><td><strong>EVENT_BASE_FLAG_PRECISE_TIMER</strong></td><td>启用高精度定时器</td></tr></tbody></table></section><section><h2>七、最佳实践<a href="#七最佳实践"><span>#</span></a></h2>

<table><thead><tr><th>建议</th><th>说明</th></tr></thead><tbody><tr><td><strong>避免阻塞回调</strong></td><td>回调函数应尽快返回</td></tr><tr><td><strong>合理设置间隔</strong></td><td>避免过于频繁的定时器</td></tr><tr><td><strong>使用EV_PERSIST</strong></td><td>周期性定时器使用EV_PERSIST更高效</td></tr><tr><td><strong>及时清理</strong></td><td>不再使用的定时器应及时删除</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-watch-module/</id>
      <title type="text">👁️ watch模块 - libevent源码深度解析系列（十六）</title>
      <published>2026-04-11T00:00:00.000Z</published>
      <updated>2026-04-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-watch-module/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>👁️ watch模块<a href="#️-watch模块"><span>#</span></a></h1><section><h2>一、概述<a href="#一概述"><span>#</span></a></h2><p>libevent的watch模块提供了在事件循环的不同阶段执行回调的能力。这对于监控、调试或在特定时机执行操作非常有用。</p></section><section><h2>二、watch回调类型<a href="#二watch回调类型"><span>#</span></a></h2>

<table><thead><tr><th>回调类型</th><th>触发时机</th></tr></thead><tbody><tr><td><strong>prepare</strong></td><td>在调用I/O复用机制（如epoll_wait）之前</td></tr><tr><td><strong>check</strong></td><td>在I/O复用机制返回之后，处理活跃事件之前</td></tr><tr><td><strong>fork</strong></td><td>在fork子进程之后</td></tr></tbody></table></section><section><h2>三、API<a href="#三api"><span>#</span></a></h2><section><h3>3.1 添加watch回调<a href="#31-添加watch回调"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 添加prepare回调</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* 在每次调用I/O复用机制之前执行</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>event_base_add_virtual_event_</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>/* 添加check回调</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>* 在I/O复用机制返回之后执行</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>9</div></div><div><span>int</span><span> </span><span>event_base_add_virtual_event_</span><span>(</span><span>struct</span><span> event_base </span><span>*</span><span><span>base</span><span>,</span><span> </span></span><span>struct</span><span> event </span><span>*</span><span><span>ev</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 结构体<a href="#32-结构体"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> evwatch {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>struct</span><span> evwatch_callback callback;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>void</span><span> </span><span>*</span><span>arg;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>TAILQ_ENTRY</span><span>(evwatch) next;</span></div></div><div><div><div>6</div></div><div><span>};</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>typedef</span><span> </span><span>void</span><span> (</span><span>*</span><span>evwatch_callback)(</span><span>struct</span><span> evwatch </span><span>*</span><span>, </span><span>const</span><span> </span><span>struct</span><span> evwatch_callbacks </span><span>*</span><span>, </span><span>void</span><span> </span><span>*</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>四、使用示例<a href="#四使用示例"><span>#</span></a></h2><section><h3>4.1 监控事件循环<a href="#41-监控事件循环"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>static</span><span> </span><span>int</span><span> loop_count </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div><span>static</span><span> </span><span>struct</span><span> timeval last_time;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>prepare_cb</span><span>(</span><span>struct</span><span> evwatch </span><span>*</span><span><span>watcher</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> evwatch_callbacks </span><span>*</span><span><span>cbs</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> timeval now;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>evutil_gettimeofday</span><span>(</span><span>&amp;</span><span>now, </span><span>NULL</span><span>);</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>    </span><span>printf</span><span>(</span><span>"[PREPARE] Loop iteration </span><span>%d</span><span>\n</span><span>"</span><span>, loop_count);</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>    </span><span>// 计算距离上次循环的时间</span></div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span> (</span><span>last_time</span><span>.</span><span>tv_sec</span><span> </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>long</span><span> diff </span><span>=</span><span> (</span><span>now</span><span>.</span><span>tv_sec</span><span> </span><span>-</span><span> </span><span>last_time</span><span>.</span><span>tv_sec</span><span>) </span><span>*</span><span> </span><span>1000000</span><span> </span><span>+</span></div></div><div><div><div>18</div></div><div><span><span>                    </span></span><span>(</span><span>now</span><span>.</span><span>tv_usec</span><span> </span><span>-</span><span> </span><span>last_time</span><span>.</span><span>tv_usec</span><span>);</span></div></div><div><div><div>19</div></div><div><span>        </span><span>printf</span><span>(</span><span>"[PREPARE] Time since last loop: </span><span>%ld</span><span> us</span><span>\n</span><span>"</span><span>, diff);</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>last_time </span><span>=</span><span> now;</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>check_cb</span><span>(</span><span>struct</span><span> evwatch </span><span>*</span><span><span>watcher</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> evwatch_callbacks </span><span>*</span><span><span>cbs</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>26</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> arg;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>int</span><span> active_count </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(base, EVENT_BASE_COUNT_ACTIVE);</span></div></div><div><div><div>29</div></div><div><span>    </span><span>int</span><span> total_count </span><span>=</span><span> </span><span>event_base_get_num_events</span><span>(base, EVENT_BASE_COUNT_ADDED);</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>    </span><span>printf</span><span>(</span><span>"[CHECK] Active events: </span><span>%d</span><span>, Total events: </span><span>%d</span><span>\n</span><span>"</span><span>, active_count, total_count);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>loop_count</span><span>++</span><span>;</span></div></div><div><div><div>34</div></div><div><span>}</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>timer_cb</span><span>(</span><span>evutil_socket_t</span><span><span> </span><span>fd</span><span>,</span><span> </span></span><span>short</span><span><span> </span><span>events</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>37</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Timer fired!</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>41</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>42</div></div><div>
</div></div><div><div><div>43</div></div><div><span>    </span><span>// 添加watch回调</span></div></div><div><div><div>44</div></div><div><span>    </span><span>struct</span><span> evwatch </span><span>*</span><span>prepare_watcher </span><span>=</span><span> </span><span>evwatch_prepare_new</span><span>(base, prepare_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>45</div></div><div><span>    </span><span>struct</span><span> evwatch </span><span>*</span><span>check_watcher </span><span>=</span><span> </span><span>evwatch_check_new</span><span>(base, check_cb, base);</span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span>    </span><span>// 创建定时器</span></div></div><div><div><div>48</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST, timer_cb, </span><span>NULL</span><span>);</span></div></div><div><div><div>49</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>1</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>50</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>// 5秒后退出</span></div></div><div><div><div>53</div></div><div><span>    </span><span>struct</span><span> timeval exit_tv </span><span>=</span><span> {</span><span>5</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>54</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>&amp;</span><span>exit_tv);</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span>    </span><span>evwatch_free</span><span>(prepare_watcher);</span></div></div><div><div><div>59</div></div><div><span>    </span><span>evwatch_free</span><span>(check_watcher);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>61</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Total loop iterations: </span><span>%d</span><span>\n</span><span>"</span><span>, loop_count);</span></div></div><div><div><div>64</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>65</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 性能监控<a href="#42-性能监控"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;event2/event.h&gt;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>struct</span><span> perf_stats {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>long</span><span> total_loops;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>long</span><span> total_events;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>long</span><span> max_latency;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>struct</span><span> timeval last_prepare;</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>prepare_cb</span><span>(</span><span>struct</span><span> evwatch </span><span>*</span><span><span>watcher</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> evwatch_callbacks </span><span>*</span><span><span>cbs</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>struct</span><span> perf_stats </span><span>*</span><span>stats </span><span>=</span><span> arg;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>evutil_gettimeofday</span><span>(</span><span>&amp;</span><span>stats</span><span>-&gt;</span><span>last_prepare</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>static</span><span> </span><span>void</span><span> </span><span>check_cb</span><span>(</span><span>struct</span><span> evwatch </span><span>*</span><span><span>watcher</span><span>,</span><span> </span></span><span>const</span><span> </span><span>struct</span><span> evwatch_callbacks </span><span>*</span><span><span>cbs</span><span>,</span><span> </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>19</div></div><div><span>    </span><span>struct</span><span> perf_stats </span><span>*</span><span>stats </span><span>=</span><span> arg;</span></div></div><div><div><div>20</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>evwatch_get_base</span><span>(watcher);</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>struct</span><span> timeval now;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>evutil_gettimeofday</span><span>(</span><span>&amp;</span><span>now, </span><span>NULL</span><span>);</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>    </span><span>// 计算I/O复用耗时</span></div></div><div><div><div>26</div></div><div><span>    </span><span>long</span><span> latency </span><span>=</span><span> (</span><span>now</span><span>.</span><span>tv_sec</span><span> </span><span>-</span><span> </span><span>stats</span><span>-&gt;</span><span>last_prepare</span><span>.</span><span>tv_sec</span><span>) </span><span>*</span><span> </span><span>1000000</span><span> </span><span>+</span></div></div><div><div><div>27</div></div><div><span><span>                   </span></span><span>(</span><span>now</span><span>.</span><span>tv_usec</span><span> </span><span>-</span><span> </span><span>stats</span><span>-&gt;</span><span>last_prepare</span><span>.</span><span>tv_usec</span><span>);</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>    </span><span>if</span><span> (latency </span><span>&gt;</span><span> </span><span>stats</span><span>-&gt;</span><span>max_latency</span><span>) {</span></div></div><div><div><div>30</div></div><div><span>        </span><span>stats</span><span>-&gt;</span><span>max_latency</span><span> </span><span>=</span><span> latency;</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>stats</span><span>-&gt;</span><span>total_loops</span><span>++</span><span>;</span></div></div><div><div><div>34</div></div><div><span>    </span><span>stats</span><span>-&gt;</span><span>total_events</span><span> </span><span>+=</span><span> </span><span>event_base_get_num_events</span><span>(base, EVENT_BASE_COUNT_ACTIVE);</span></div></div><div><div><div>35</div></div><div><span>}</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>int</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>38</div></div><div><span>    </span><span>struct</span><span> event_base </span><span>*</span><span>base </span><span>=</span><span> </span><span>event_base_new</span><span>();</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>    </span><span>struct</span><span> perf_stats stats </span><span>=</span><span> {</span><span>0</span><span>};</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> evwatch </span><span>*</span><span>prepare </span><span>=</span><span> </span><span>evwatch_prepare_new</span><span>(base, prepare_cb, </span><span>&amp;</span><span>stats);</span></div></div><div><div><div>43</div></div><div><span>    </span><span>struct</span><span> evwatch </span><span>*</span><span>check </span><span>=</span><span> </span><span>evwatch_check_new</span><span>(base, check_cb, </span><span>&amp;</span><span>stats);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// 创建一些事件</span></div></div><div><div><div>46</div></div><div><span>    </span><span>struct</span><span> event </span><span>*</span><span>timer </span><span>=</span><span> </span><span>event_new</span><span>(base, </span><span>-</span><span>1</span><span>, EV_PERSIST,</span></div></div><div><div><div>47</div></div><div><span>        </span><span>[]</span><span>(</span><span>evutil_socket_t</span><span> fd, </span><span>short</span><span> events, </span><span>void</span><span> </span><span>*</span><span>arg) {}, </span><span>NULL</span><span>);</span></div></div><div><div><div>48</div></div><div><span>    </span><span>struct</span><span> timeval tv </span><span>=</span><span> {</span><span>0</span><span>, </span><span>100000</span><span>};</span><span>  // 100ms</span></div></div><div><div><div>49</div></div><div><span>    </span><span>event_add</span><span>(timer, </span><span>&amp;</span><span>tv);</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>// 运行10秒</span></div></div><div><div><div>52</div></div><div><span>    </span><span>struct</span><span> timeval exit_tv </span><span>=</span><span> {</span><span>10</span><span>, </span><span>0</span><span>};</span></div></div><div><div><div>53</div></div><div><span>    </span><span>event_base_loopexit</span><span>(base, </span><span>&amp;</span><span>exit_tv);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>    </span><span>event_base_dispatch</span><span>(base);</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>printf</span><span>(</span><span>"Performance Statistics:</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>58</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Total loops: </span><span>%ld</span><span>\n</span><span>"</span><span>, </span><span>stats</span><span>.</span><span>total_loops</span><span>);</span></div></div><div><div><div>59</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Total events processed: </span><span>%ld</span><span>\n</span><span>"</span><span>, </span><span>stats</span><span>.</span><span>total_events</span><span>);</span></div></div><div><div><div>60</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Max I/O latency: </span><span>%ld</span><span> us</span><span>\n</span><span>"</span><span>, </span><span>stats</span><span>.</span><span>max_latency</span><span>);</span></div></div><div><div><div>61</div></div><div><span>    </span><span>printf</span><span>(</span><span>"  Avg events per loop: </span><span>%.2f</span><span>\n</span><span>"</span><span>,</span></div></div><div><div><div>62</div></div><div><span><span>           </span></span><span>(</span><span>double</span><span>)</span><span>stats</span><span>.</span><span>total_events</span><span> </span><span>/</span><span> </span><span>stats</span><span>.</span><span>total_loops</span><span>);</span></div></div><div><div><div>63</div></div><div>
</div></div><div><div><div>64</div></div><div><span>    </span><span>evwatch_free</span><span>(prepare);</span></div></div><div><div><div>65</div></div><div><span>    </span><span>evwatch_free</span><span>(check);</span></div></div><div><div><div>66</div></div><div><span>    </span><span>event_free</span><span>(timer);</span></div></div><div><div><div>67</div></div><div><span>    </span><span>event_base_free</span><span>(base);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>70</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、实际应用场景<a href="#五实际应用场景"><span>#</span></a></h2>

<table><thead><tr><th>场景</th><th>说明</th></tr></thead><tbody><tr><td><strong>性能监控</strong></td><td>统计事件循环次数、延迟等指标</td></tr><tr><td><strong>调试</strong></td><td>在关键点打印调试信息</td></tr><tr><td><strong>资源管理</strong></td><td>在循环前后检查资源状态</td></tr><tr><td><strong>心跳检测</strong></td><td>检测事件循环是否正常运行</td></tr><tr><td><strong>动态调整</strong></td><td>根据负载动态调整参数</td></tr></tbody></table></section><section><h2>六、注意事项<a href="#六注意事项"><span>#</span></a></h2>

<table><thead><tr><th>注意事项</th><th>说明</th></tr></thead><tbody><tr><td><strong>回调效率</strong></td><td>watch回调在每次循环都会执行，应尽量高效</td></tr><tr><td><strong>避免阻塞</strong></td><td>回调中不应执行阻塞操作</td></tr><tr><td><strong>资源释放</strong></td><td>使用evwatch_free释放watcher</td></tr><tr><td><strong>线程安全</strong></td><td>多线程环境下需要加锁保护</td></tr></tbody></table><hr /><p><strong>其他相关资料 请移步到<a href="https://xutopia.top/">个人主页</a></strong></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/personal-company-management/</id>
      <title type="text">把人生活成一家盈利的&quot;一人公司&quot;：从内耗迷茫到清醒自洽的经营哲学</title>
      <published>2026-04-10T00:00:00.000Z</published>
      <updated>2026-04-10T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/personal-company-management/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>把人生活成一家盈利的“一人公司”：从内耗迷茫到清醒自洽的经营哲学</p>
<p>凌晨1点，刷完最后一条短视频，放下手机的瞬间，一阵空虚席卷而来——今天又在无效忙碌中度过：工作没做完，计划没执行，说好的健身、读书全被拖延取代；月底对账，工资刚到账就被房租、花呗、人情往来消耗一空，存款余额始终停留在三位数；和家人拌了嘴，明明是小事，却因为情绪失控吵得不可开交，冷战了一整天；看着身边的人要么升职加薪，要么经营起自己的小事业，而自己却在迷茫中反复内耗，不知道未来的方向在哪里。</p>
<p>这样的场景，是不是戳中了很多人的日常？我们总在抱怨生活混乱、命运不公，却从未意识到：人生本就是一场长期经营，而我们自己，就是这家“一人公司”的唯一创始人、CEO与终极责任人。就像彼得·德鲁克在90岁高龄时写下的《管理自己》中所言：“在这个时代，你不需要成为管理别人的CEO，但你必须成为自己的CEO。” 所谓“个人公司化”，从来不是让我们变得功利、冷漠、精于算计，而是用企业经营的理性思维，梳理人生的混乱，明确目标的方向，守住生活的底线，最终实现“人生盈利”——这里的盈利，无关金钱的多少，而是健康、快乐、成长、财富与家庭幸福的综合平衡。</p>
<p>在这个不确定性加剧的时代，“铁饭碗”早已不复存在，远程办公成为常态，个体崛起成为趋势，越来越多的人开始意识到：把自己当成一家公司来经营，才是对抗迷茫、抵御风险的最佳方式。它不是一句空洞的口号，而是一套可落地、可复盘、可优化的人生管理体系，早在百年前，就被无数先贤与商业大佬践行，如今更是成为普通人破局内耗的核心逻辑。</p>
<p>一、先搞懂：什么是“个人公司化”？（不是功利，是清醒）</p>
<p>很多人一听到“公司化”，就会联想到“算计”“利益至上”，甚至觉得“把人生当公司，会失去生活的温度”。但事实上，个人公司化的核心，是“用理性框架托住感性生活”，本质是将企业管理中的“目标管理、资源配置、风险控制、复盘优化”等逻辑，迁移到个人生活、家庭经营中，让我们从“被动应付”走向“主动掌控”。</p>
<p>企业经营的核心逻辑是“活下去、谋发展、创价值”，而人生经营的核心逻辑，是“活得健康、活得清醒、活得有价值、活得不后悔”——两者看似无关，底层逻辑却高度一致：都是“明确目标→整合资源→执行落地→风控止损→复盘优化”的闭环。</p>
<p>稻盛和夫在《心法》中提到：“经营企业，其实就是在经营‘做人的准则’。” 这句话同样适用于个人公司化：经营自己这家“公司”，本质上就是经营自己的人生准则——守住底线，明确方向，持续成长，懂得取舍。我们的“公司”，没有董事会，没有股东，所有的决策都由自己做出，所有的结果都由自己承担：你敷衍经营，人生就会给你“亏损”的反馈；你用心打磨，人生就会给你“盈利”的回报。</p>
<p>举个最现实的例子：同样是月薪8000，有的人把工资当成“全部收入”，月光消费、毫无规划，几年后依旧原地踏步，甚至负债累累——这就是“经营不善”的表现；而有的人，把工资当成“初始资金”，一部分用于日常开销，一部分用于“投资自己”（学习技能、提升认知），一部分用于“资产积累”（储蓄、理财），同时合理分配时间，兼顾健康与社交，几年后要么升职加薪，要么拥有了副业收入，生活越来越从容——这就是“科学经营”的成果。</p>
<p>央行的调研数据显示，56.6%的受访者近两年没有储蓄行为，超四成家庭面临较重债务负担，每四个家庭中就有一个无法应对三倍月收入的意外支出。这些数据背后，藏着的正是“人生经营能力的缺失”：不懂资源配置，不懂风险控制，不懂长期规划，最终在生活的洪流中随波逐流，陷入被动。</p>
<p>二、个人公司化的核心框架：你的“人生公司”，该如何搭建？</p>
<p>一家正规公司，有明确的组织架构、资产负债、经营目标与风控体系；同样，你的“人生公司”，也需要搭建清晰的框架，才能有序经营、持续盈利。结合德鲁克的自我管理理论与稻盛和夫的阿米巴经营思维，我们可以从“个人”与“家庭”两个维度，搭建属于自己的“人生公司”体系。</p>
<p>（一）个人层面：打造“一人有限公司”，做自己的CEO</p>
<p>你是这家“一人有限公司”的唯一股东、CEO与执行者，公司的“注册资本”，是你与生俱来的时间、健康、原生家庭资源与初始认知；公司的“主营业务”，是你的工作、事业、学习与创造价值的过程；公司的“经营期限”，是你的一生；公司的“核心使命”，是实现自我价值，收获幸福与自由。</p>
<ol>
<li>组织架构：给人生“分部门”，告别混乱内耗</li>
</ol>
<p>很多人之所以内耗，核心是“眉毛胡子一把抓”，分不清优先级，把时间和精力浪费在无效的事情上。就像一家没有部门分工的公司，所有人都在打杂，效率低下、漏洞百出。你的“人生公司”，需要搭建以下9个核心“部门”，各司其职、协同运转：</p>
<ul>
<li>
<p>战略部：核心职责是“定方向、立目标”，对应人生的规划与定位。就像德鲁克强调的，真正厉害的人，都懂得找到自己的优势与方向，而不是盲目努力。这个部门的核心工作，是制定3年长期目标、1年中期目标、季度短期目标，明确“什么该做，什么不该做”，避免瞎忙。比如：3年后成为行业内的资深人士，1年内掌握一项新技能，这个季度完成2次深度阅读。考核KPI：目标完成率、方向不跑偏，避免“努力错了方向”。</p>
</li>
<li>
<p>研发部：核心职责是“提能力、涨认知”，对应人生的学习与成长。稻盛和夫说：“付出不亚于任何人的努力，不是蛮干，是愿力先行”，而学习与成长，就是这份“愿力”的落地。这个部门的核心工作，是持续学习新技能、提升认知水平、沉淀经验，让自己的“核心竞争力”不断升级。比如：每天阅读1小时，每月学习1门课程，每季度总结1次经验。考核KPI：阅读量、技能熟练度、认知提升幅度。</p>
</li>
<li>
<p>生产部：核心职责是“创价值、赚收入”，对应人生的工作与变现。德鲁克说：“不要问‘我想要什么’，要问‘我能贡献什么’”，这个部门的核心工作，就是通过自己的能力，为社会、为企业创造价值，从而换取收入（工资、副业收入等）。比如：认真完成本职工作，提升工作效率，开发副业，实现多渠道收入。考核KPI：收入水平、工作效率、成果交付质量。</p>
</li>
<li>
<p>财务部：核心职责是“管 money、控成本”，对应人生的财富管理。央行数据显示，40%的30岁以下群体成为月光核心，三四五线城市单身青年月光率超70%，本质是财务部门“失职”。这个部门的核心工作，是制定预算、控制消费、储蓄理财、抵御财务风险，避免“月光”“负债”。比如：每月收入的30%用于储蓄，20%用于投资自己，50%用于日常开销；拒绝超前消费，远离网贷。考核KPI：储蓄率、资产增值幅度、无负债。</p>
</li>
<li>
<p>行政部：核心职责是“保秩序、提效率”，对应人生的日常琐事与生活管理。这个部门的核心工作，是安排日常作息、处理家务琐事、优化生活流程，让生活变得有序高效。比如：规律作息（早睡早起），整理房间，合理安排饮食，避免被琐事消耗精力。考核KPI：作息规律度、环境整洁度、琐事处理效率。</p>
</li>
<li>
<p>风控部：核心职责是“防风险、护根基”，对应人生的健康、情绪与安全。健康是“人生公司”的“本金”，情绪是“人生公司”的“软实力”，安全是“人生公司”的“底线”。这个部门的核心工作，是守护健康（体检、运动、睡眠）、管控情绪（避免内耗、焦虑、抱怨）、防范意外（安全出行、购买保险）。比如：每周运动3次，定期体检，学会情绪调节，避免冲动决策。考核KPI：体检指标、情绪稳定度、无意外发生。</p>
</li>
<li>
<p>市场部：核心职责是“拓人脉、树口碑”，对应人生的社交与人际关系。稻盛和夫说：“活着就要感谢，这份感恩能唤醒人内心的善意，也能收获他人的认可”。这个部门的核心工作，是维护优质人脉、拓展社交圈子、传递自己的价值，打造良好的个人口碑。比如：定期和朋友联系，参与有意义的社交活动，真诚待人，乐于助人。考核KPI：有效社交数量、人脉质量、个人口碑。</p>
</li>
<li>
<p>品牌部：核心职责是“塑形象、立标签”，对应人生的个人形象与自我认同。你的“个人品牌”，就是别人提到你时的第一印象，也是你“人生公司”的核心竞争力之一。这个部门的核心工作，是打造个人形象（外在穿搭、内在气质）、明确个人标签（比如“靠谱”“专业”“乐观”），提升自我认可度。考核KPI：他人评价、自我认同度、个人标签清晰度。</p>
</li>
<li>
<p>监事会：核心职责是“守底线、明是非”，对应人生的良知、原则与底线。稻盛和夫在拯救日航时，反复强调“作为人何为正确”，这正是监事会的核心作用。这个部门的核心工作，是监督自己的行为，坚守道德底线，避免做后悔的事，保持内心的自洽。比如：不欺骗他人、不敷衍工作、不伤害身边的人，坚守自己的原则。考核KPI：不做违背良知的事、内心自洽度、无后悔决策。</p>
</li>
</ul>
<ol>
<li>资产与负债：分清“增值项”与“消耗项”，避免人生“破产”</li>
</ol>
<p>一家公司的兴衰，取决于资产与负债的平衡；一个人的人生质量，同样取决于“人生资产”与“人生负债”的博弈。很多人之所以活得疲惫、迷茫，就是因为不断积累“人生负债”，却忽视了“人生资产”的培育。</p>
<p>核心人生资产（越用越值钱，是人生盈利的基础）：</p>
<ul>
<li>
<p>时间资产：最稀缺、最不可再生的资产，也是所有资产的基础。德鲁克说，管理自己，本质是管理自己的时间，懂得分配时间，就是懂得经营人生。</p>
</li>
<li>
<p>健康资产：人生的“本金”，没有健康，再多的财富、再高的地位，都是空谈。就像稻盛和夫所说，努力的前提，是拥有健康的身体，才能支撑长期的奋斗。</p>
</li>
<li>
<p>认知资产：决定你决策的质量，认知越高，越能看清事物的本质，避免走弯路。德鲁克的反馈分析法，就是提升认知的有效方法——通过记录预期与结果的差距，看清自己的优势与不足。</p>
</li>
<li>
<p>技能资产：变现的核心工具，也是你“生产部”创造价值的基础，技能越熟练，收入上限越高。比如：文案写作、编程、设计、沟通能力等，都是可不断升级的技能资产。</p>
</li>
<li>
<p>信用资产：人脉与口碑的核心，一个人有信用，才能获得他人的信任，才能拥有更多的机会。就像装修行业的书友，坚守“用最好的材料，收合理的价”，反而赢得了更多回头客。</p>
</li>
<li>
<p>情绪资产：稳定的心态，是高效执行、良好社交的基础，情绪稳定的人，才能在困境中保持清醒，做出正确的决策。</p>
</li>
</ul>
<p>致命人生负债（越积累越拖累，是人生亏损的根源）：</p>
<ul>
<li>
<p>时间负债：刷短视频、无效社交、拖延、浪费时间，这些行为都会消耗你的时间资产，让你陷入“越忙越乱”的循环。</p>
</li>
<li>
<p>健康负债：熬夜、暴饮暴食、不运动、过度消耗身体，这些行为会透支你的健康资产，最终让你付出沉重的代价。</p>
</li>
<li>
<p>情绪负债：内耗、焦虑、抱怨、敏感、易怒，这些负面情绪会消耗你的精力，影响你的决策与社交，甚至拖垮你的家庭关系。</p>
</li>
<li>
<p>金钱负债：超前消费、网贷、乱投资、盲目攀比，这些行为会让你陷入财务危机，失去生活的主动权。就像超半数月光族，看似享受了当下，实则积累了沉重的金钱负债。</p>
</li>
<li>
<p>关系负债：垃圾人、消耗型关系、虚伪的社交，这些关系会消耗你的精力与情绪，让你变得消极、内耗。</p>
</li>
</ul>
<p>人生“破产”的标志，从来不是没钱，而是健康没了、情绪崩了、关系碎了、内心空了。经营自己的“人生公司”，就是要不断增加“人生资产”，剥离“人生负债”，实现资产增值、负债清零。</p>
<ol>
<li>营收与利润：人生的“盈利”，从来不是只看金钱</li>
</ol>
<p>企业的营收是销售额，利润是营收减去成本；而你“人生公司”的营收，是你在生活中获得的一切美好——工资、副业收入、成长、快乐、成就感、亲情、友情；成本，是你付出的时间、精力、健康、情绪；净利润，就是你真正的幸福、自由与安全感。</p>
<p>很多人陷入了一个误区：把金钱当成人生唯一的“营收”，为了赚钱，牺牲健康、消耗情绪、忽略家庭，最终赚了钱，却失去了更珍贵的东西——这就是“盈利失衡”。就像有些创业者，为了公司盈利，熬夜加班、透支身体，最终公司做大了，自己却患上了重病，得不偿失。</p>
<p>真正健康的“人生盈利”，是“多元营收、平衡成本”：既有金钱收入，也有成长与快乐；既付出努力，也懂得善待自己；既追求事业，也兼顾家庭。就像长江读书会的书友，每天早起半小时共读，既提升了认知（成长营收），也收获了同频的友谊（关系营收），还保持了规律的作息（健康资产），这就是“人生盈利”的最佳状态。</p>
<p>（二）家庭层面：打造“家族集团公司”，协同经营幸福</p>
<p>如果说个人是“一人有限公司”，那么家庭，就是“家族集团公司”——夫妻是联合创始人、双CEO，股权各50%，平等共治；孩子是“子公司”“储备人才”，需要精心培育、助力成长；父母是“独立董事”“顾问团”，只提供建议，不干预决策；家庭整体，就是“集团总部”，核心目标是实现“长期存续、共同盈利”。</p>
<p>很多家庭之所以矛盾不断、鸡飞狗跳，核心是“没有经营思维”——把家庭当成“凑活过”的地方，没有明确的目标、合理的分工、清晰的规则，最终陷入“内耗不断、互相消耗”的困境。而把家庭当成“集团公司”来经营，就是要明确家庭目标、合理分工、坚守规则、协同成长，让家庭成为“人生公司”的坚强后盾。</p>
<ol>
<li>股权结构与核心目标：平等共治，共赢共生</li>
</ol>
<p>家庭“集团公司”的核心目标，不是“赚多少钱”，而是：财务安全（不缺钱、抗风险）、情绪稳定（不内耗、不吵架）、成员成长（夫妻进步、孩子成才）、长期存续（白头偕老、家风传承）——这就像企业的“百年老店”目标，需要夫妻双方共同努力、长期经营。</p>
<p>夫妻作为“联合创始人”，股权平等、地位平等，没有“谁主导、谁服从”，只有“协同合作、互相包容”。就像济南装修公司的刘总，借鉴阿米巴经营思维，将团队拆分成独立小组，让每个小组自主经营，最终激发了员工的积极性；夫妻之间，也需要“分工协作、各展所长”，而不是互相指责、互相推诿。</p>
<ol>
<li>家庭部门分工：各司其职，避免内耗</li>
</ol>
<p>就像企业需要部门分工，家庭也需要明确的“部门”与职责，避免“谁多做、谁少做”的争论。结合家庭实际，可搭建以下5个核心“部门”：</p>
<ul>
<li>
<p>战略委员会：由夫妻共同组成，核心职责是制定家庭的长期规划与短期目标，比如：3年后买房、孩子的教育规划、父母的养老规划、家庭的财务目标等，共同决策、共同执行。</p>
</li>
<li>
<p>财务中心：由夫妻一方主导、双方监督，核心职责是统一家庭收支、制定家庭预算、储蓄理财、配置保险，实现财务透明、风险可控。比如：每月统一记账，明确收支明细，共同制定储蓄目标，避免大额消费不沟通的问题。</p>
</li>
<li>
<p>运营中心：由夫妻双方分工负责，核心职责是处理家务、照顾孩子、赡养老人，安排家庭日常事务。比如：一方负责做饭、打扫卫生，一方负责接送孩子、辅导作业，合理分工、互相配合，避免“一个人包揽所有”的疲惫。</p>
</li>
<li>
<p>风控中心：由夫妻共同负责，核心职责是守护家庭成员的健康、防范家庭风险（比如意外、疾病、财务危机），配置家庭保险，建立应急金（6-12个月的生活费），应对突发情况。</p>
</li>
<li>
<p>文化中心：由夫妻共同主导，核心职责是营造良好的家庭氛围、培养家庭仪式感、传递正确的价值观，比如：每周家庭聚餐、每月家庭出游、节日互相祝福，让家庭充满温暖与爱。</p>
</li>
</ul>
<ol>
<li>家庭红线制度：守住底线，稳定发展</li>
</ol>
<p>一家公司有规章制度，才能有序经营；一个家庭有“红线制度”，才能稳定和谐。结合现实中的家庭矛盾，我们可以设定以下4条“家庭红线”，任何人都不能触碰：</p>
<ul>
<li>
<p>禁止家暴、冷暴力、出轨：这是家庭“集团公司”的“生死线”，触碰这条线，就会导致家庭“破产”。</p>
</li>
<li>
<p>禁止大额消费不沟通：任何大额消费（比如超过5000元），必须夫妻双方共同决策，避免“单方面挥霍”导致的财务矛盾。</p>
</li>
<li>
<p>禁止辱骂、翻旧账、人身攻击：吵架可以，但不能人身攻击、翻旧账，避免伤害彼此的感情，消耗家庭情绪资产。</p>
</li>
<li>
<p>财务透明，共同决策：不隐瞒收入、不藏匿私房钱，家庭财务公开透明，重大财务决策（比如投资、买房）共同商量。</p>
</li>
</ul>
<p>就像稻盛和夫在经营企业时，坚守“作为人何为正确”的底线，家庭经营也需要坚守底线，才能实现长期稳定、共同幸福。</p>
<p>三、个人公司化的落地法则：从“知道”到“做到”，拒绝空谈</p>
<p>很多人看完“个人公司化”的逻辑，觉得“很有道理”，但就是不知道如何落地。其实，个人公司化的核心，不是“搞复杂的体系”，而是“把简单的事情做到极致”，结合企业管理的PDCA循环（Plan-Do-Check-Act），我们可以从以下5个方面，逐步落地，实现人生的持续优化。</p>
<ol>
<li>战略规划：每年做一次“年度经营计划”</li>
</ol>
<p>企业有年度预算、年度计划，你的“人生公司”也需要。每年年底，花1-2天时间，复盘今年的“经营情况”（目标完成率、资产增值情况、负债情况、家庭关系等），然后制定下一年的“年度经营计划”，明确核心目标，拆解到季度、月度、每周。</p>
<p>比如：明年的核心目标是“提升技能、增加储蓄、改善家庭氛围”，那么季度目标可以是“第一季度掌握一项新技能的基础内容，储蓄2万元，每月组织一次家庭聚餐”；月度目标可以是“每月阅读2本书，储蓄5000元，每周和家人深度沟通1次”；每周目标可以是“每天阅读1小时，每周运动3次，周末陪孩子出去玩1次”。</p>
<p>制定计划时，要遵循德鲁克的“优势原则”，聚焦自己的优势，不要盲目跟风——比如你擅长写作，就不要强迫自己去做直播；你适合独自学习，就不要强迫自己去参加无意义的社交，找到适合自己的节奏，才能高效执行。</p>
<ol>
<li>预算管理：给人生做“成本控制”</li>
</ol>
<p>企业的成本控制，是盈利的关键；人生的“成本控制”，是实现财务安全、避免内耗的核心。我们可以从“时间、金钱、情绪”三个维度，做好预算管理：</p>
<ul>
<li>
<p>时间预算：每天规划好自己的时间，明确“什么时间做什么事”，避免时间浪费。比如：每天8小时工作，1小时阅读，1小时运动，2小时陪伴家人，1小时处理琐事，保证7-8小时睡眠。可以借鉴每周个人预算模板的思路，每天记录时间消耗，每周复盘，优化时间分配。</p>
</li>
<li>
<p>金钱预算：遵循“收入-储蓄=消费”的原则，而不是“收入-消费=储蓄”。每月工资到账后，先拿出30%-50%用于储蓄或理财，再拿出20%-30%用于投资自己（学习、健身等），剩下的用于日常开销。同时，记录每天的消费，避免冲动消费，远离网贷、信用卡透支。</p>
</li>
<li>
<p>情绪预算：每天给自己设定“情绪额度”，允许自己有短暂的不开心，但不能让负面情绪持续蔓延。比如：每天允许自己抱怨5分钟，之后就必须调整心态，做一些能让自己开心的事情（比如听音乐、运动、和朋友聊天），避免情绪内耗。</p>
</li>
</ul>
<ol>
<li>绩效考核：每月给自己“打分”</li>
</ol>
<p>企业会定期考核员工，你的“人生公司”，也需要定期考核自己，了解自己的“经营情况”，及时发现问题、调整方向。每月月底，给自己做一次“绩效考核”，从9个“部门”的角度，分别打分（满分100分），低于60分的“部门”，就是“经营不善”，需要制定整改计划。</p>
<p>比如：这个月“研发部”打分50分，因为没有完成阅读计划，那么下个月的整改计划就是“每天固定1小时阅读，周末不拖延”；“财务部”打分70分，因为有冲动消费，那么下个月的整改计划就是“严格执行预算，不买非必要物品”。</p>
<p>考核的目的，不是“苛责自己”，而是“客观认识自己”，持续优化，让自己的“人生公司”经营得越来越好。就像企业的绩效考核，不是为了惩罚员工，而是为了提升效率、实现共赢。</p>
<ol>
<li>PDCA循环：持续优化，迭代成长</li>
</ol>
<p>企业的发展，是一个“计划-执行-检查-改进”的循环；人生的成长，也是一个持续迭代、不断优化的过程。遵循PDCA循环，让自己的“人生公司”不断升级：</p>
<ul>
<li>
<p>Plan（计划）：制定明确的目标与计划（年度、季度、月度）。</p>
</li>
<li>
<p>Do（执行）：脚踏实地，认真执行计划，不拖延、不敷衍。</p>
</li>
<li>
<p>Check（检查）：定期复盘（每周、每月、每季度），检查目标完成情况，分析问题所在。</p>
</li>
<li>
<p>Act（改进）：针对检查中发现的问题，制定整改计划，优化流程、调整方向，避免再犯同样的错误。</p>
</li>
</ul>
<p>比如：你计划每周运动3次，但这个月只运动了2次，检查后发现，是因为下班太晚、拖延导致的，那么改进计划就是“每天提前1小时下班，到家后立即运动，不拖延”。持续循环这个过程，你会发现，自己的执行力越来越强，人生也越来越有序。</p>
<ol>
<li>止损机制：人生也要“裁员”和“剥离不良资产”</li>
</ol>
<p>企业经营中，会及时“裁员”（淘汰不合格的员工）、“剥离不良资产”（处置亏损的业务），才能轻装上阵；人生经营中，也需要学会“止损”，剥离消耗自己的人、事、物，才能避免内耗，聚焦核心。</p>
<ul>
<li>
<p>裁掉无效社交：远离那些只会抱怨、消耗你、不懂得感恩的人，把时间和精力留给真正值得的人（家人、朋友、同频的人）。就像远程办公的企业，会筛选能独当一面、自驱动的员工，我们也要筛选自己的社交圈，留下优质人脉。</p>
</li>
<li>
<p>剥离消耗你的事：停止那些无意义、不开心、不成长的事情，比如：刷无营养的短视频、熬夜追剧、和别人盲目攀比，把时间用在能提升自己、让自己开心的事情上。</p>
</li>
<li>
<p>放弃错误的目标：如果某个目标，你努力了很久，还是没有进展，而且让你感到疲惫、内耗，说明这个目标可能不适合你，学会放弃，及时调整方向，不要一条路走到黑。德鲁克说，找到自己属于哪里，比盲目努力重要100倍，放弃错误的目标，才能找到正确的方向。</p>
</li>
</ul>
<p>四、个人公司化的常见误区：别让“经营”变成“内耗”</p>
<p>很多人在践行“个人公司化”的过程中，容易走入一些误区，反而让自己变得更加内耗、功利，违背了“个人公司化”的初衷。结合现实中的案例，我们总结了4个最常见的误区，帮你避开坑，正确经营自己的人生。</p>
<p>误区1：把“公司化”当成“功利化”，一切向钱看</p>
<p>很多人认为，“个人公司化”就是“赚钱至上”，为了赚钱，牺牲健康、忽略家庭、消耗情绪，甚至做违背良知的事情。但事实上，个人公司化的核心是“多元盈利”，金钱只是“营收”的一部分，健康、快乐、成长、家庭幸福，才是“人生公司”的核心利润。</p>
<p>就像稻盛和夫经营企业，从来不是“唯利润论”，而是强调“利他之心”，坚守“作为人何为正确”的底线，最终反而实现了企业的长期盈利。经营人生也是一样，懂得平衡金钱与生活，才能实现真正的“人生盈利”。</p>
<p>误区2：追求“完美执行”，不允许自己犯错</p>
<p>很多人制定了计划，就要求自己必须100%完成，一旦没有完成，就自责、内耗，甚至放弃。但企业经营中，也会有亏损、有失误，重要的不是“不犯错”，而是“及时整改、持续优化”。</p>
<p>人生经营也是一样，允许自己有失误、有拖延，允许自己偶尔“偷懒”，重要的是，不要因为一次失误，就否定自己，而是要及时复盘、调整，继续前行。就像长江读书会的书友，每天坚持读书，偶尔偷懒没读，也不会自责，而是第二天继续坚持，长期坚持下来，反而收获满满。</p>
<p>误区3：用“公司化”控制家人，忽视家庭温度</p>
<p>有些夫妻，把“家庭公司化”当成“控制对方”的工具，用“绩效考核”要求家人，用“规则”束缚家人，忽视了家庭的温度与包容。比如：要求伴侣必须每天完成多少家务，要求孩子必须考多少分，一旦没有达到，就指责、批评，反而让家庭矛盾越来越多。</p>
<p>记住，家庭“集团公司”的核心是“协同经营、共同幸福”，规则是为了“减少内耗”，而不是“控制对方”；分工是为了“互相配合”，而不是“互相指责”。就像阿米巴经营的核心，是激发员工的主动性，而不是控制员工，家庭经营也需要包容与理解，才能充满温暖。</p>
<p>误区4：只注重“执行”，忽视“复盘与优化”</p>
<p>很多人制定了计划，也认真执行了，但就是不懂得复盘，导致自己一直在重复犯同样的错误，成长缓慢。就像有些企业，只注重生产，不注重复盘优化，最终被市场淘汰。</p>
<p>复盘是“人生公司”迭代成长的关键，每周、每月、每季度，都要花时间复盘自己的“经营情况”，分析问题、总结经验，优化计划与流程，才能不断提升自己的经营能力，实现人生的持续盈利。</p>
<p>五、结语：人生最好的经营，是清醒自洽，长期主义</p>
<p>我们每个人，都是自己人生的“CEO”，经营自己这家“公司”，没有标准答案，没有捷径可走，只有脚踏实地、持续优化、坚守底线。个人公司化，不是让我们变得冷漠、功利，而是让我们用理性的思维，梳理人生的混乱，用坚定的行动，追逐自己的目标，用温暖的初心，守护自己的家庭。</p>
<p>德鲁克说：“管理自己，是21世纪生存的核心能力。” 稻盛和夫说：“不是因为看到希望才坚持，而是坚持了才会看到希望。” 经营人生，就像经营一家企业，需要有长期主义的耐心，需要有直面问题的勇气，需要有持续优化的决心。</p>
<p>不必羡慕别人的“人生公司”盈利丰厚，每个人的“注册资本”不同，“经营节奏”不同，只要你认真经营、持续成长，剥离负债、积累资产，平衡好工作与生活、金钱与幸福，你的“人生公司”，终将实现属于自己的“盈利”——清醒、自洽、自由、幸福。</p>
<p>愿我们都能学会经营自己，把潦草的人生，活成一家稳健、长期、有价值的“好公司”，不负时光，不负自己，不负身边的人。</p>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/sop-complete-guide/</id>
      <title type="text">SOP全维度详细讲解：从核心定义到实战落地的完整指南</title>
      <published>2026-04-10T00:00:00.000Z</published>
      <updated>2026-04-10T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/sop-complete-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>SOP（Standard Operating Procedure）全维度详细讲解<a href="#sopstandard-operating-procedure全维度详细讲解"><span>#</span></a></h1><section><h2>一、SOP 核心定义（精准、官方）<a href="#一sop-核心定义精准官方"><span>#</span></a></h2><p><strong>SOP 全称：Standard Operating Procedure</strong></p><p>中文标准译名为：<strong>标准作业程序</strong>，也常称标准操作规程、标准作业指导流程。</p><p><strong>官方精准定义</strong>：</p><p>SOP 是为了完成特定工作任务，将<strong>常规、重复、关键的操作步骤</strong>，按照统一的标准、固定的顺序、明确的要求，进行<strong>规范化、文字化、流程化、可量化</strong>的书面/电子文件规定。</p><p>核心目的是：让不同执行者，在不同时间、不同场景下，都能<strong>以相同的方式完成同一项工作</strong>，最终实现结果统一、质量稳定、风险可控、效率提升。</p><p>简单理解：<strong>把“怎么做一件事”写成固定说明书，所有人照做不走样</strong>。</p><hr /></section><section><h2>二、SOP 的核心本质与关键特征<a href="#二sop-的核心本质与关键特征"><span>#</span></a></h2><p>SOP 不是普通的工作笔记，而是具备强约束性、标准化的管理文件，核心特征如下：</p><ol>
<li><strong>标准化</strong></li>
</ol><p>   动作、步骤、参数、标准统一，无主观随意性，杜绝“凭经验做事”。</p><ol>
<li><strong>可复制性</strong></li>
</ol><p>   新人、老员工、不同班组/门店，都能复刻相同操作，实现规模化落地。</p><ol>
<li><strong>可量化</strong></li>
</ol><p>   拒绝模糊描述（如“适量”“大概”），全部用数据、指标、时间、规格定义。</p><ol>
<li><strong>可追溯</strong></li>
</ol><p>   配套记录、表单、签字，出现问题可回溯到具体步骤、责任人。</p><ol>
<li><strong>可培训</strong></li>
</ol><p>   是新人入职、岗位轮岗的核心培训教材，降低学习成本。</p><ol>
<li><strong>稳定性</strong></li>
</ol><p>   固化成熟流程，减少人为失误，保障工作结果一致性。</p><ol>
<li><strong>合规性</strong></li>
</ol><p>   符合行业法规、企业制度、质量体系（如ISO、GMP、HACCP）要求。</p><hr /></section><section><h2>三、SOP 的起源与发展<a href="#三sop-的起源与发展"><span>#</span></a></h2><ol>
<li><strong>起源</strong></li>
</ol><p>   源于<strong>工业革命+泰勒科学管理理论</strong>，福特汽车首创流水线作业，将汽车组装拆解为固定步骤，是现代SOP雏形。</p><ol>
<li><strong>普及</strong></li>
</ol><p>   二战期间军工生产大规模应用SOP，保障武器生产质量统一；后续推广至制造业、医疗、餐饮、物流、政务、互联网等全行业。</p><ol>
<li><strong>体系化</strong></li>
</ol><p>   成为ISO9001质量管理体系、GMP药品生产管理、HACCP食品安全体系的核心组成部分，是企业合规运营的基础文件。</p><hr /></section><section><h2>四、SOP 的分类（全面维度划分）<a href="#四sop-的分类全面维度划分"><span>#</span></a></h2><section><h3>（一）按<strong>管理层级</strong>划分<a href="#一按管理层级划分"><span>#</span></a></h3><ol>
<li>
<p><strong>公司级SOP</strong>：全公司通用，如财务报销、公章使用、安全管理SOP；</p>
</li>
<li>
<p><strong>部门级SOP</strong>：部门专属，如人力资源招聘、市场推广、生产车间SOP；</p>
</li>
<li>
<p><strong>岗位级SOP</strong>：单个岗位具体操作，如客服接线、仓库分拣、设备操作SOP。</p>
</li>
</ol></section><section><h3>（二）按<strong>功能用途</strong>划分<a href="#二按功能用途划分"><span>#</span></a></h3><ol>
<li>
<p><strong>操作执行类</strong>：核心干活流程，如产品组装、奶茶制作、设备开机；</p>
</li>
<li>
<p><strong>管理管控类</strong>：流程审批、人员管理、资源调配，如采购审批SOP；</p>
</li>
<li>
<p><strong>质量检验类</strong>：产品检测、验收标准，如成品出厂检验SOP；</p>
</li>
<li>
<p><strong>应急处置类</strong>：突发情况处理，如火灾应急、设备故障停机SOP；</p>
</li>
<li>
<p><strong>合规风控类</strong>：满足法规要求，如医疗消毒、食品留样SOP。</p>
</li>
</ol></section><section><h3>（三）按<strong>呈现形式</strong>划分<a href="#三按呈现形式划分"><span>#</span></a></h3><ol>
<li>
<p><strong>纯文字版</strong>：步骤文字描述，适合简单操作；</p>
</li>
<li>
<p><strong>流程图版</strong>：Visio/ProcessOn绘制流程框图，直观展示逻辑；</p>
</li>
<li>
<p><strong>图文并茂版</strong>：步骤+实拍图/示意图，适合复杂操作；</p>
</li>
<li>
<p><strong>视频版SOP</strong>：实操演示视频，适合高难度、高风险岗位；</p>
</li>
<li>
<p><strong>电子表单版</strong>：嵌入OA/飞书/钉钉，流程自动流转。</p>
</li>
</ol></section><section><h3>（四）按<strong>行业场景</strong>划分<a href="#四按行业场景划分"><span>#</span></a></h3><p>制造业、医疗医药、餐饮连锁、物流快递、互联网、政务行政、金融、教育等全行业均有专属SOP。</p><hr /></section></section><section><h2>五、SOP 的核心作用与价值（全场景覆盖）<a href="#五sop-的核心作用与价值全场景覆盖"><span>#</span></a></h2><section><h3>1. 统一作业标准，杜绝人为差异<a href="#1-统一作业标准杜绝人为差异"><span>#</span></a></h3><p>避免“10个人做同一件事，做出10种结果”，保障输出一致性。</p></section><section><h3>2. 降低失误与风险<a href="#2-降低失误与风险"><span>#</span></a></h3><p>关键步骤标准化，减少操作失误、安全事故、质量缺陷、合规处罚。</p></section><section><h3>3. 提升工作效率<a href="#3-提升工作效率"><span>#</span></a></h3><p>省去反复沟通、试错成本，流程固定后执行速度大幅提升。</p></section><section><h3>4. 新人快速上手<a href="#4-新人快速上手"><span>#</span></a></h3><p>无需老员工手把手带教，新人按SOP即可独立上岗，降低培训成本。</p></section><section><h3>5. 质量稳定可控<a href="#5-质量稳定可控"><span>#</span></a></h3><p>量化验收标准，产品/服务质量可稳定达标，提升客户满意度。</p></section><section><h3>6. 知识沉淀与传承<a href="#6-知识沉淀与传承"><span>#</span></a></h3><p>把老员工经验、优秀流程固化为文件，避免人员流失导致经验断层。</p></section><section><h3>7. 合规审计必备<a href="#7-合规审计必备"><span>#</span></a></h3><p>应对政府检查、体系认证、客户验厂，SOP是核心佐证文件。</p></section><section><h3>8. 优化管理与绩效考核<a href="#8-优化管理与绩效考核"><span>#</span></a></h3><p>明确岗位职责、操作标准，为绩效考核提供客观依据。</p></section><section><h3>9. 跨部门协作顺畅<a href="#9-跨部门协作顺畅"><span>#</span></a></h3><p>统一流程接口，减少部门推诿、沟通内耗。</p><hr /></section></section><section><h2>六、完整SOP的标准结构（官方通用模板，最核心部分）<a href="#六完整sop的标准结构官方通用模板最核心部分"><span>#</span></a></h2><p>一份<strong>规范、可落地、合规</strong>的SOP，必须包含11大核心模块，缺一不可：</p><section><h3>1. 文件头部（基础信息，唯一标识）<a href="#1-文件头部基础信息唯一标识"><span>#</span></a></h3><ul>
<li>
<p>SOP编号：企业统一编码（如SOP-HR-001）</p>
</li>
<li>
<p>版本号：V1.0/V2.0（迭代管理）</p>
</li>
<li>
<p>生效日期：YYYY-MM-DD</p>
</li>
<li>
<p>编制人/审核人/批准人：三级签字，明确责任</p>
</li>
<li>
<p>适用范围：适用部门、岗位、场景</p>
</li>
<li>
<p>归口管理部门：负责修订、更新的部门</p>
</li>
</ul></section><section><h3>2. 目的<a href="#2-目的"><span>#</span></a></h3><p>明确<strong>为什么要制定本SOP</strong>，如“规范XX操作，保障质量，降低风险”。</p></section><section><h3>3. 术语与定义<a href="#3-术语与定义"><span>#</span></a></h3><p>对专业名词、缩写、设备名称进行解释，避免理解偏差。</p></section><section><h3>4. 职责分工<a href="#4-职责分工"><span>#</span></a></h3><p>明确<strong>谁来做</strong>：执行人、复核人、监督人、责任人，杜绝权责不清。</p></section><section><h3>5. 前置条件/资源准备<a href="#5-前置条件资源准备"><span>#</span></a></h3><p>操作前必备条件：</p><ul>
<li>
<p>人员资质：是否需持证上岗</p>
</li>
<li>
<p>设备工具：设备型号、状态、校准情况</p>
</li>
<li>
<p>物料耗材：规格、数量、质量要求</p>
</li>
<li>
<p>环境要求：温度、湿度、洁净度</p>
</li>
<li>
<p>表单记录：需填写的单据</p>
</li>
</ul></section><section><h3>6. 详细作业步骤（SOP核心主体）<a href="#6-详细作业步骤sop核心主体"><span>#</span></a></h3><ul>
<li>
<p>按<strong>时间顺序/操作逻辑</strong>拆解，步骤编号1、2、3…</p>
</li>
<li>
<p>动作拆解到<strong>最小单元</strong>，无跳跃、无模糊描述</p>
</li>
<li>
<p>关键步骤标注<strong>关键控制点（CCP）</strong>，重点强调</p>
</li>
</ul></section><section><h3>7. 质量标准/验收标准<a href="#7-质量标准验收标准"><span>#</span></a></h3><p>明确<strong>做到什么程度算合格</strong>，量化指标：</p><ul>
<li>
<p>时间标准：XX分钟内完成</p>
</li>
<li>
<p>数值标准：温度±1℃、重量±5g</p>
</li>
<li>
<p>外观标准：无划痕、无破损</p>
</li>
<li>
<p>功能标准：运行正常、无故障</p>
</li>
</ul></section><section><h3>8. 异常情况与应急处理<a href="#8-异常情况与应急处理"><span>#</span></a></h3><p>操作中出现问题的处置方案：</p><ul>
<li>
<p>常见异常：设备故障、物料不合格、操作失误</p>
</li>
<li>
<p>处理步骤：停机→上报→排查→整改→恢复</p>
</li>
<li>
<p>应急措施：安全事故、突发风险的紧急操作</p>
</li>
</ul></section><section><h3>9. 记录与表单<a href="#9-记录与表单"><span>#</span></a></h3><p>操作过程需填写的记录：如操作记录表、检验单、异常报告单，明确<strong>留存期限、存放位置</strong>。</p></section><section><h3>10. 相关引用文件<a href="#10-相关引用文件"><span>#</span></a></h3><p>关联的制度、国标、其他SOP、设备说明书，形成文件体系。</p></section><section><h3>11. 附则<a href="#11-附则"><span>#</span></a></h3><ul>
<li>
<p>修订规则：何时更新、如何更新</p>
</li>
<li>
<p>废止说明：旧版本自动失效</p>
</li>
<li>
<p>解释权：归口部门解释</p>
</li>
</ul><hr /></section></section><section><h2>七、SOP 的编写方法与原则（可直接落地）<a href="#七sop-的编写方法与原则可直接落地"><span>#</span></a></h2><section><h3>（一）编写核心原则<a href="#一编写核心原则"><span>#</span></a></h3><ol>
<li><strong>5W1H原则</strong></li>
</ol><p>   What（做什么）、Why（为什么做）、Who（谁做）、When（何时做）、Where（在哪做）、How（怎么做）</p><ol>
<li><strong>SMART原则</strong></li>
</ol><p>   具体、可衡量、可实现、相关、有时限</p><ol>
<li><strong>MECE原则</strong></li>
</ol><p>   步骤相互独立、完全穷尽，无遗漏、无重叠</p><ol>
<li><strong>可执行性原则</strong></li>
</ol><p>   拒绝空话、套话，贴合实际工作，不纸上谈兵</p><ol>
<li><strong>简洁直观原则</strong></li>
</ol><p>   能用图不用字，能用短句不用长句</p></section><section><h3>（二）标准编写流程<a href="#二标准编写流程"><span>#</span></a></h3><ol>
<li>
<p><strong>需求调研</strong>：梳理岗位工作，识别重复、关键、高风险操作</p>
</li>
<li>
<p><strong>流程梳理</strong>：绘制现有流程，优化不合理环节</p>
</li>
<li>
<p><strong>步骤拆解</strong>：把流程拆分为最小操作单元</p>
</li>
<li>
<p><strong>标准制定</strong>：量化参数、明确标准、划分职责</p>
</li>
<li>
<p><strong>初稿撰写</strong>：按标准结构编写SOP文本</p>
</li>
<li>
<p><strong>评审修订</strong>：一线员工、主管、质量部门联合评审，修改不合理内容</p>
</li>
<li>
<p><strong>审批发布</strong>：正式签字生效，纳入企业文件体系</p>
</li>
<li>
<p><strong>培训宣贯</strong>：全员学习，考核通过方可上岗</p>
</li>
</ol></section><section><h3>（三）编写避坑技巧<a href="#三编写避坑技巧"><span>#</span></a></h3><ul>
<li>
<p>禁用模糊词：把“适量”改为“50ml”，“尽快”改为“3分钟内”</p>
</li>
<li>
<p>关键步骤加粗/标红，重点提醒</p>
</li>
<li>
<p>复杂操作配实拍图/流程图，降低理解成本</p>
</li>
<li>
<p>避免过度繁琐，简单操作简化步骤</p>
</li>
</ul><hr /></section></section><section><h2>八、SOP 的落地执行与使用方法（从“写出来”到“用起来”）<a href="#八sop-的落地执行与使用方法从写出来到用起来"><span>#</span></a></h2><section><h3>1. 发布与归档<a href="#1-发布与归档"><span>#</span></a></h3><ul>
<li>
<p>纸质版：张贴在操作现场（车间、工位、门店）</p>
</li>
<li>
<p>电子版：上传企业知识库（飞书/钉钉/OA），全员可查阅</p>
</li>
<li>
<p>版本管理：统一归档，禁止私自修改、传播旧版本</p>
</li>
</ul></section><section><h3>2. 培训与考核<a href="#2-培训与考核"><span>#</span></a></h3><ul>
<li>
<p>新员工：SOP为岗前培训核心内容</p>
</li>
<li>
<p>老员工：定期复训，更新迭代后专项培训</p>
</li>
<li>
<p>考核：笔试+实操，考核通过方可上岗，不合格禁止操作</p>
</li>
</ul></section><section><h3>3. 现场执行<a href="#3-现场执行"><span>#</span></a></h3><ul>
<li>
<p>严格按SOP步骤操作，<strong>禁止擅自更改流程</strong></p>
</li>
<li>
<p>关键岗位双人复核，保障执行到位</p>
</li>
<li>
<p>实时填写操作记录，留存痕迹</p>
</li>
</ul></section><section><h3>4. 监督与检查<a href="#4-监督与检查"><span>#</span></a></h3><ul>
<li>
<p>日常巡检：主管现场核查执行情况</p>
</li>
<li>
<p>定期审计：质量/管理部门月度/季度 audit</p>
</li>
<li>
<p>纠偏整改：发现不按SOP操作，立即整改并追责</p>
</li>
</ul></section><section><h3>5. 考核挂钩<a href="#5-考核挂钩"><span>#</span></a></h3><p>将SOP执行情况纳入员工绩效考核，与奖惩、晋升挂钩。</p><hr /></section></section><section><h2>九、SOP 的优化与迭代（PDCA循环）<a href="#九sop-的优化与迭代pdca循环"><span>#</span></a></h2><p>SOP 不是一成不变的，需跟随业务迭代更新，遵循<strong>PDCA循环</strong>：</p><ol>
<li>
<p><strong>Plan（计划）</strong>：规划SOP框架与标准</p>
</li>
<li>
<p><strong>Do（执行）</strong>：落地执行SOP</p>
</li>
<li>
<p><strong>Check（检查）</strong>：核查执行效果、问题、效率</p>
</li>
<li>
<p><strong>Act（处理）</strong>：优化不合理步骤，修订SOP，形成新版本</p>
</li>
</ol><section><h3>迭代触发场景<a href="#迭代触发场景"><span>#</span></a></h3><ul>
<li>
<p>设备更新、工艺升级</p>
</li>
<li>
<p>法规/行业标准变更</p>
</li>
<li>
<p>频繁出现操作失误、质量问题</p>
</li>
<li>
<p>业务流程优化、效率提升需求</p>
</li>
<li>
<p>客户/市场新要求</p>
</li>
</ul></section><section><h3>迭代规范<a href="#迭代规范"><span>#</span></a></h3><ul>
<li>
<p>修订需走审批流程，禁止私自修改</p>
</li>
<li>
<p>新版本发布后，旧版本立即废止</p>
</li>
<li>
<p>迭代后重新培训，确保全员知晓</p>
</li>
</ul><hr /></section></section><section><h2>十、SOP 与易混淆概念的区别（精准区分）<a href="#十sop-与易混淆概念的区别精准区分"><span>#</span></a></h2><p>| 概念 | 核心区别 |</p><p>|------|----------|</p><p>| <strong>SOP 标准作业程序</strong> | 侧重<strong>完整流程+执行步骤+标准</strong>，强约束、可落地 |</p><p>| <strong>WI 作业指导书</strong> | 侧重<strong>单一操作细节</strong>（如设备按钮操作），是SOP的补充 |</p><p>| <strong>管理制度</strong> | 侧重<strong>规则、权责、奖惩</strong>，宏观约束，不讲具体操作 |</p><p>| <strong>流程图</strong> | 仅展示逻辑框架，无具体标准、参数、要求 |</p><p>| <strong>质量手册</strong> | 企业质量体系纲领文件，SOP是其落地执行文件 |</p><hr /></section><section><h2>十一、不同行业SOP实战案例（具象化理解）<a href="#十一不同行业sop实战案例具象化理解"><span>#</span></a></h2><section><h3>案例1：餐饮连锁·奶茶制作SOP<a href="#案例1餐饮连锁奶茶制作sop"><span>#</span></a></h3><ol>
<li>
<p>前置准备：杯具消毒、物料称重（珍珠50g、茶汤200ml）</p>
</li>
<li>
<p>步骤1：取杯→贴标签→加珍珠</p>
</li>
<li>
<p>步骤2：倒入茶汤→加冰150g→加糖25g</p>
</li>
<li>
<p>步骤3：雪克杯摇晃10秒→倒入杯中</p>
</li>
<li>
<p>质量标准：温度5℃，甜度标准，无洒漏</p>
</li>
<li>
<p>异常处理：物料不足立即上报，禁止私自更改配比</p>
</li>
</ol></section><section><h3>案例2：制造业·产品组装SOP<a href="#案例2制造业产品组装sop"><span>#</span></a></h3><ol>
<li>
<p>人员：持证上岗，穿戴防静电服</p>
</li>
<li>
<p>设备：流水线开机自检，参数校准</p>
</li>
<li>
<p>步骤：上料→插件→焊接→检测→封装</p>
</li>
<li>
<p>检验：电阻值±0.1Ω，外观无虚焊</p>
</li>
<li>
<p>记录：填写组装记录表，留存3年</p>
</li>
</ol></section><section><h3>案例3：互联网·客服接待SOP<a href="#案例3互联网客服接待sop"><span>#</span></a></h3><ol>
<li>
<p>响铃3秒内接听</p>
</li>
<li>
<p>话术：您好，XX客服，请问有什么可以帮您？</p>
</li>
<li>
<p>步骤：倾听需求→记录问题→解答→确认解决→礼貌挂机</p>
</li>
<li>
<p>标准：通话时长≤3分钟，满意度≥95%</p>
</li>
</ol></section><section><h3>案例4：医疗·静脉输液SOP<a href="#案例4医疗静脉输液sop"><span>#</span></a></h3><ol>
<li>
<p>核对患者信息→消毒皮肤→穿刺→固定→调节滴速</p>
</li>
<li>
<p>标准：滴速60滴/分钟，无菌操作，无回血外渗</p>
</li>
<li>
<p>应急：肿胀/过敏立即拔针，上报医生</p>
</li>
</ol><hr /></section></section><section><h2>十二、SOP 实施的常见误区与避坑<a href="#十二sop-实施的常见误区与避坑"><span>#</span></a></h2><ol>
<li><strong>形式主义，只写不用</strong></li>
</ol><p>   避免SOP锁在抽屉，必须现场落地、监督执行</p><ol>
<li><strong>过于繁琐，无法执行</strong></li>
</ol><p>   流程过度复杂，员工抵触，需简化实用</p><ol>
<li><strong>长期不更新，脱离实际</strong></li>
</ol><p>   业务变化后SOP未迭代，变成“废纸”</p><ol>
<li><strong>无量化标准，模糊不清</strong></li>
</ol><p>   全是定性描述，无法判断是否合格</p><ol>
<li><strong>缺乏培训，全员不懂</strong></li>
</ol><p>   编写后不培训，员工不知道标准</p><ol>
<li><strong>只重步骤，不重结果</strong></li>
</ol><p>   忽略验收标准，步骤对了结果错了</p><hr /></section><section><h2>十三、SOP 常用工具与模板<a href="#十三sop-常用工具与模板"><span>#</span></a></h2><section><h3>常用工具<a href="#常用工具"><span>#</span></a></h3><ul>
<li>
<p>文字编写：Word、WPS</p>
</li>
<li>
<p>流程图：Visio、ProcessOn、XMind</p>
</li>
<li>
<p>在线协同：飞书文档、石墨文档、腾讯文档</p>
</li>
<li>
<p>体系管理：ISO文件管理系统、企业OA</p>
</li>
</ul></section><section><h3>通用简易模板<a href="#通用简易模板"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>【SOP编号】XXX</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>【版本】V1.0</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>【适用范围】XXX</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>【目的】XXX</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>【职责】XXX</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>【准备工作】XXX</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>【操作步骤】</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>1.XXX</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>2.XXX</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>【质量标准】XXX</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>【异常处理】XXX</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>【记录表单】XXX</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>【编制/审核/批准】XXX</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section><section><h2>总结<a href="#总结"><span>#</span></a></h2><p>SOP 本质是<strong>企业/岗位的“操作宪法”</strong>，是标准化管理的基础工具，覆盖从生产到服务、从基层到管理的全场景。</p><p>其核心价值是<strong>统一标准、降低风险、提升效率、沉淀经验</strong>，编写时要贴合实际、量化清晰，执行时要严格落地、持续迭代，才能真正发挥作用。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/modern-cmake-best-practices-guide/</id>
      <title type="text">🔧 现代CMake最佳实践：权限最小化与依赖管理完全指南</title>
      <published>2026-04-09T00:00:00.000Z</published>
      <updated>2026-04-09T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/modern-cmake-best-practices-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔧 现代CMake最佳实践：权限最小化与依赖管理完全指南<a href="#-现代cmake最佳实践权限最小化与依赖管理完全指南"><span>#</span></a></h1><p>现代CMake（3.14+）的核心设计理念是<strong>基于目标的依赖管理</strong>，通过精确控制每个目标的依赖可见性，实现权限最小化、编译效率最大化。本文将系统讲解现代CMake的最佳实践，帮助你告别全局变量的混乱时代。</p><section><h2>一、核心概念：PRIVATE/PUBLIC/INTERFACE<a href="#一核心概念privatepublicinterface"><span>#</span></a></h2><section><h3>1. 三种依赖可见性详解<a href="#1-三种依赖可见性详解"><span>#</span></a></h3><p>现代CMake通过三个关键字精确控制依赖的传播范围：</p>

<table><thead><tr><th>关键字</th><th>当前目标</th><th>依赖当前目标的其他目标</th><th>典型场景</th></tr></thead><tbody><tr><td><strong>PRIVATE</strong></td><td>✅ 可见</td><td>❌ 不可见</td><td>内部实现依赖，如第三方库、内部工具</td></tr><tr><td><strong>PUBLIC</strong></td><td>✅ 可见</td><td>✅ 可见</td><td>接口依赖，如导出头文件中使用的库</td></tr><tr><td><strong>INTERFACE</strong></td><td>❌ 不可见</td><td>✅ 可见</td><td>纯头文件库，仅对使用者生效</td></tr></tbody></table><p><strong>依赖传播示意图</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>A 依赖 B (PUBLIC)  →  使用 A 的目标也能看到 B</span></div></div><div><div><div>2</div></div><div><span>A 依赖 B (PRIVATE) →  使用 A 的目标看不到 B</span></div></div><div><div><div>3</div></div><div><span>A 依赖 B (INTERFACE) → A 自己看不到 B，但使用 A 的目标能看到 B</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2. 依赖传播的实际例子<a href="#2-依赖传播的实际例子"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># libA 是一个网络库，内部使用 libuv 实现</span></div></div><div><div><div>2</div></div><div><span>add_library</span><span>(libA STATIC libA.cpp)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># libuv 是内部实现细节，不应该暴露给 libA 的使用者</span></div></div><div><div><div>5</div></div><div><span>target_link_libraries</span><span>(libA PRIVATE libuv)</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># libA 的公共头文件中使用了 libB 的类型</span></div></div><div><div><div>8</div></div><div><span># 所以 libB 必须是 PUBLIC 依赖</span></div></div><div><div><div>9</div></div><div><span>target_link_libraries</span><span>(libA PUBLIC libB)</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># libA 提供了一个纯头文件的插件接口</span></div></div><div><div><div>12</div></div><div><span># 插件实现者需要这个接口，但 libA 自己不需要</span></div></div><div><div><div>13</div></div><div><span>target_link_libraries</span><span>(libA INTERFACE libA_plugin_interface)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 包含目录的可见性控制<a href="#3-包含目录的可见性控制"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>add_library</span><span>(mylib STATIC mylib.cpp)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 私有包含目录 - 仅 mylib.cpp 可见</span></div></div><div><div><div>4</div></div><div><span>target_include_directories</span><span>(mylib PRIVATE</span></div></div><div><div><div>5</div></div><div><span>    </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/src/</span><span>internal</span></div></div><div><div><div>6</div></div><div><span>    </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/third_party</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 公共包含目录 - mylib 和使用 mylib 的目标都可见</span></div></div><div><div><div>10</div></div><div><span>target_include_directories</span><span>(mylib PUBLIC</span></div></div><div><div><div>11</div></div><div><span>    </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/include</span></div></div><div><div><div>12</div></div><div><span>)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 接口包含目录 - 仅使用 mylib 的目标可见</span></div></div><div><div><div>15</div></div><div><span>target_include_directories</span><span>(mylib INTERFACE</span></div></div><div><div><div>16</div></div><div><span>    </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/include/mylib/plugins</span></div></div><div><div><div>17</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4. 编译定义的可见性控制<a href="#4-编译定义的可见性控制"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>add_library</span><span>(mylib STATIC mylib.cpp)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 私有定义 - 仅影响当前目标的编译</span></div></div><div><div><div>4</div></div><div><span>target_compile_definitions</span><span>(mylib PRIVATE</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>DEBUG_MODE=1</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>INTERNAL_VERSION=</span><span>"2.0"</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 公共定义 - 当前目标和使用者都会获得</span></div></div><div><div><div>10</div></div><div><span>target_compile_definitions</span><span>(mylib PUBLIC</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>MYLIB_API_VERSION=3</span></div></div><div><div><div>12</div></div><div><span>)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 接口定义 - 仅使用者获得</span></div></div><div><div><div>15</div></div><div><span>target_compile_definitions</span><span>(mylib INTERFACE</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>MYLIB_PLUGIN_SUPPORT</span></div></div><div><div><div>17</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>二、传统方式 vs 现代方式<a href="#二传统方式-vs-现代方式"><span>#</span></a></h2><section><h3>❌ 传统方式（不推荐）<a href="#-传统方式不推荐"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 全局设置，影响所有后续目标</span></div></div><div><div><div>2</div></div><div><span>cmake_minimum_required</span><span>(VERSION 3.10)</span></div></div><div><div><div>3</div></div><div><span>project</span><span>(myproject)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 全局包含目录 - 所有目标都能看到</span></div></div><div><div><div>6</div></div><div><span>include_directories</span><span>(</span><span>${CMAKE_SOURCE_DIR}</span><span>/include)</span></div></div><div><div><div>7</div></div><div><span>include_directories</span><span>(</span><span>${CMAKE_SOURCE_DIR}</span><span>/third_party/libuv/include)</span></div></div><div><div><div>8</div></div><div><span>include_directories</span><span>(</span><span>${CMAKE_SOURCE_DIR}</span><span>/third_party/openssl/include)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 全局库目录</span></div></div><div><div><div>11</div></div><div><span>link_directories</span><span>(</span><span>${CMAKE_SOURCE_DIR}</span><span>/third_party/lib)</span></div></div><div><div><div>12</div></div><div><span>link_directories</span><span>(/usr/local/lib)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 全局编译选项</span></div></div><div><div><div>15</div></div><div><span>add_definitions</span><span>(-DDEBUG)</span></div></div><div><div><div>16</div></div><div><span>add_definitions</span><span>(-DVERSION=</span><span>"1.0"</span><span>)</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span># 全局编译标志</span></div></div><div><div><div>19</div></div><div><span>set</span><span>(</span><span>CMAKE_CXX_FLAGS</span><span> </span><span>"${CMAKE_CXX_FLAGS} -Wall -Wextra -O2"</span><span>)</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span># 链接时不指定可见性</span></div></div><div><div><div>22</div></div><div><span>add_executable</span><span>(myapp main.cpp)</span></div></div><div><div><div>23</div></div><div><span>target_link_libraries</span><span>(myapp uv ssl crypto pthread)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>传统方式的问题</strong>：</p><ol>
<li><strong>污染全局命名空间</strong> - 所有目标都继承相同的设置</li>
<li><strong>隐藏依赖关系</strong> - 无法从 CMakeLists.txt 看出真实依赖</li>
<li><strong>编译效率低</strong> - 修改任何头文件都会触发大量重编译</li>
<li><strong>难以维护</strong> - 多模块项目容易产生冲突</li>
<li><strong>不可移植</strong> - 硬编码路径在其他环境无法工作</li>
</ol></section><section><h3>✅ 现代方式（推荐）<a href="#-现代方式推荐"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cmake_minimum_required</span><span>(VERSION 3.14)</span></div></div><div><div><div>2</div></div><div><span>project</span><span>(myproject VERSION 1.0.0 LANGUAGES CXX)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 设置 C++ 标准</span></div></div><div><div><div>5</div></div><div><span>set</span><span>(CMAKE_CXX_STANDARD 17)</span></div></div><div><div><div>6</div></div><div><span>set</span><span>(CMAKE_CXX_STANDARD_REQUIRED </span><span>ON</span><span>)</span></div></div><div><div><div>7</div></div><div><span>set</span><span>(CMAKE_CXX_EXTENSIONS </span><span>OFF</span><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># ========== 第三方库配置 ==========</span></div></div><div><div><div>10</div></div><div><span>find_package</span><span>(OpenSSL REQUIRED)</span></div></div><div><div><div>11</div></div><div><span>find_package</span><span>(Threads REQUIRED)</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># ========== 内部库：网络模块 ==========</span></div></div><div><div><div>14</div></div><div><span>add_library</span><span>(network STATIC</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>src/network/tcp_client.cpp</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>src/network/tcp_server.cpp</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>src/network/ssl_wrapper.cpp</span></div></div><div><div><div>18</div></div><div><span>)</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>target_include_directories</span><span>(network</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>22</div></div><div><span><span>        </span></span><span>$&lt;BUILD_INTERFACE:</span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/include/network&gt;</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>$&lt;INSTALL_INTERFACE:include/network&gt;</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>25</div></div><div><span>        </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/src/network/</span><span>internal</span></div></div><div><div><div>26</div></div><div><span>)</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>target_link_libraries</span><span>(network</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>OpenSSL::SSL</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>OpenSSL::Crypto</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>Threads::Threads</span></div></div><div><div><div>34</div></div><div><span>)</span></div></div><div><div><div>35</div></div><div>
</div></div><div><div><div>36</div></div><div><span>target_compile_features</span><span>(network PUBLIC cxx_std_17)</span></div></div><div><div><div>37</div></div><div>
</div></div><div><div><div>38</div></div><div><span># ========== 主程序 ==========</span></div></div><div><div><div>39</div></div><div><span>add_executable</span><span>(myapp</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>src/main.cpp</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>src/app.cpp</span></div></div><div><div><div>42</div></div><div><span>)</span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>target_include_directories</span><span>(myapp PRIVATE</span></div></div><div><div><div>45</div></div><div><span>    </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/src</span></div></div><div><div><div>46</div></div><div><span>)</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE</span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>network</span></div></div><div><div><div>50</div></div><div><span>)</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>target_compile_options</span><span>(myapp PRIVATE</span></div></div><div><div><div>53</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CONFIG:Debug&gt;:-Wall -Wextra -g&gt;</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CONFIG:Release&gt;:-O3 -DNDEBUG&gt;</span></div></div><div><div><div>55</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>三、现代CMake项目结构示例<a href="#三现代cmake项目结构示例"><span>#</span></a></h2><section><h3>推荐的项目目录结构<a href="#推荐的项目目录结构"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>myproject/</span></div></div><div><div><div>2</div></div><div><span>├── CMakeLists.txt              # 根配置</span></div></div><div><div><div>3</div></div><div><span>├── CMakePresets.json           # 预设配置（可选）</span></div></div><div><div><div>4</div></div><div><span>├── cmake/                      # CMake 模块</span></div></div><div><div><div>5</div></div><div><span>│   ├── FindMyLib.cmake</span></div></div><div><div><div>6</div></div><div><span>│   └── MyProjectConfig.cmake.in</span></div></div><div><div><div>7</div></div><div><span>├── include/                    # 公共头文件</span></div></div><div><div><div>8</div></div><div><span>│   └── mylib/</span></div></div><div><div><div>9</div></div><div><span>│       ├── api.h</span></div></div><div><div><div>10</div></div><div><span>│       └── types.h</span></div></div><div><div><div>11</div></div><div><span>├── src/                        # 源代码</span></div></div><div><div><div>12</div></div><div><span>│   ├── CMakeLists.txt</span></div></div><div><div><div>13</div></div><div><span>│   ├── mylib/</span></div></div><div><div><div>14</div></div><div><span>│   │   ├── internal/           # 私有头文件</span></div></div><div><div><div>15</div></div><div><span>│   │   │   └── impl.h</span></div></div><div><div><div>16</div></div><div><span>│   │   └── lib.cpp</span></div></div><div><div><div>17</div></div><div><span>│   └── app/</span></div></div><div><div><div>18</div></div><div><span>│       └── main.cpp</span></div></div><div><div><div>19</div></div><div><span>├── tests/                      # 测试代码</span></div></div><div><div><div>20</div></div><div><span>│   ├── CMakeLists.txt</span></div></div><div><div><div>21</div></div><div><span>│   └── test_main.cpp</span></div></div><div><div><div>22</div></div><div><span>└── third_party/                # 第三方库</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>└── googletest/</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>根目录 CMakeLists.txt<a href="#根目录-cmakeliststxt"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cmake_minimum_required</span><span>(VERSION 3.14)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>project</span><span>(MyProject</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>VERSION 1.0.0</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>DESCRIPTION </span><span>"A modern CMake project example"</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>LANGUAGES CXX</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 全局设置（仅设置必要的）</span></div></div><div><div><div>10</div></div><div><span>set</span><span>(CMAKE_CXX_STANDARD 17)</span></div></div><div><div><div>11</div></div><div><span>set</span><span>(CMAKE_CXX_STANDARD_REQUIRED </span><span>ON</span><span>)</span></div></div><div><div><div>12</div></div><div><span>set</span><span>(CMAKE_CXX_EXTENSIONS </span><span>OFF</span><span>)</span></div></div><div><div><div>13</div></div><div><span>set</span><span>(CMAKE_EXPORT_COMPILE_COMMANDS </span><span>ON</span><span>)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span># 设置默认构建类型</span></div></div><div><div><div>16</div></div><div><span>if</span><span>(</span><span>NOT</span><span> </span><span>CMAKE_BUILD_TYPE</span><span>)</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>set</span><span>(</span><span>CMAKE_BUILD_TYPE</span><span> Release </span><span>CACHE</span><span> </span><span>STRING</span><span> </span><span>"Build type"</span><span> FORCE)</span></div></div><div><div><div>18</div></div><div><span>endif</span><span>()</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span># 添加子目录</span></div></div><div><div><div>21</div></div><div><span>add_subdirectory</span><span>(src)</span></div></div><div><div><div>22</div></div><div><span>add_subdirectory</span><span>(tests)</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span># 安装配置</span></div></div><div><div><div>25</div></div><div><span>include</span><span>(GNUInstallDirs)</span></div></div><div><div><div>26</div></div><div><span>include</span><span>(CMakePackageConfigHelpers)</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span># 安装头文件</span></div></div><div><div><div>29</div></div><div><span>install</span><span>(DIRECTORY include/mylib</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>DESTINATION </span><span>${CMAKE_INSTALL_INCLUDEDIR}</span></div></div><div><div><div>31</div></div><div><span>)</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span># 生成版本配置文件</span></div></div><div><div><div>34</div></div><div><span>write_basic_package_version_file(</span></div></div><div><div><div>35</div></div><div><span>    </span><span>"${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake"</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>VERSION </span><span>${PROJECT_VERSION}</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>COMPATIBILITY SameMajorVersion</span></div></div><div><div><div>38</div></div><div><span>)</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span># 安装 CMake 配置文件</span></div></div><div><div><div>41</div></div><div><span>install</span><span>(FILES</span></div></div><div><div><div>42</div></div><div><span>    </span><span>"${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake"</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>DESTINATION </span><span>${CMAKE_INSTALL_LIBDIR}</span><span>/cmake/MyProject</span></div></div><div><div><div>44</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>库的 CMakeLists.txt<a href="#库的-cmakeliststxt"><span>#</span></a></h3><div><div><div><figure><figcaption><span>src/CMakeLists.txt</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 收集源文件</span></div></div><div><div><div>2</div></div><div><span>add_library</span><span>(mylib</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>mylib/core.cpp</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>mylib/utils.cpp</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>mylib/network.cpp</span></div></div><div><div><div>6</div></div><div><span>)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 设置目标属性</span></div></div><div><div><div>9</div></div><div><span>set_target_properties</span><span>(mylib PROPERTIES</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>VERSION </span><span>${PROJECT_VERSION}</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>SOVERSION 1</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>OUTPUT_NAME mylib</span></div></div><div><div><div>13</div></div><div><span>    </span><span>POSITION_INDEPENDENT_CODE</span><span> </span><span>ON</span></div></div><div><div><div>14</div></div><div><span>)</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 包含目录</span></div></div><div><div><div>17</div></div><div><span>target_include_directories</span><span>(mylib</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>$&lt;BUILD_INTERFACE:</span><span>${PROJECT_SOURCE_DIR}</span><span>/include&gt;</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>$&lt;INSTALL_INTERFACE:include&gt;</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>22</div></div><div><span>        </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/mylib/</span><span>internal</span></div></div><div><div><div>23</div></div><div><span>)</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span># 编译定义</span></div></div><div><div><div>26</div></div><div><span>target_compile_definitions</span><span>(mylib</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>MYLIB_VERSION_MAJOR=</span><span>${PROJECT_VERSION_MAJOR}</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>MYLIB_VERSION_MINOR=</span><span>${PROJECT_VERSION_MINOR}</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>MYLIB_BUILDING</span></div></div><div><div><div>32</div></div><div><span>)</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span># 编译选项</span></div></div><div><div><div>35</div></div><div><span>target_compile_options</span><span>(mylib</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:GNU,Clang,AppleClang&gt;:-Wall -Wextra -Wpedantic&gt;</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:</span><span>MSVC</span><span>&gt;:/W4&gt;</span></div></div><div><div><div>39</div></div><div><span>)</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span># 链接依赖</span></div></div><div><div><div>42</div></div><div><span>find_package</span><span>(Threads REQUIRED)</span></div></div><div><div><div>43</div></div><div><span>find_package</span><span>(OpenSSL REQUIRED)</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>target_link_libraries</span><span>(mylib</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>OpenSSL::SSL</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>Threads::Threads</span></div></div><div><div><div>50</div></div><div><span><span>        </span></span><span>OpenSSL::Crypto</span></div></div><div><div><div>51</div></div><div><span>)</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span># 安装目标</span></div></div><div><div><div>54</div></div><div><span>install</span><span>(TARGETS mylib</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>EXPORT</span><span> mylibTargets</span></div></div><div><div><div>56</div></div><div><span><span>    </span></span><span>LIBRARY DESTINATION </span><span>${CMAKE_INSTALL_LIBDIR}</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>ARCHIVE DESTINATION </span><span>${CMAKE_INSTALL_LIBDIR}</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>RUNTIME DESTINATION </span><span>${CMAKE_INSTALL_BINDIR}</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>INCLUDES DESTINATION </span><span>${CMAKE_INSTALL_INCLUDEDIR}</span></div></div><div><div><div>60</div></div><div><span>)</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>install</span><span>(EXPORT mylibTargets</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>FILE</span><span> mylibTargets.cmake</span></div></div><div><div><div>64</div></div><div><span><span>    </span></span><span>NAMESPACE MyProject::</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>DESTINATION </span><span>${CMAKE_INSTALL_LIBDIR}</span><span>/cmake/MyProject</span></div></div><div><div><div>66</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>测试的 CMakeLists.txt<a href="#测试的-cmakeliststxt"><span>#</span></a></h3><div><div><div><figure><figcaption><span>tests/CMakeLists.txt</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用 FetchContent 获取 Google Test</span></div></div><div><div><div>2</div></div><div><span>include</span><span>(FetchContent)</span></div></div><div><div><div>3</div></div><div><span>FetchContent_Declare(</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>googletest</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>GIT_REPOSITORY https://github.com/google/googletest.git</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>GIT_TAG v1.14.0</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div><div><div><div>8</div></div><div><span>FetchContent_MakeAvailable(googletest)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 禁用 Google Test 的 C++17 警告</span></div></div><div><div><div>11</div></div><div><span>set</span><span>(gtest_force_shared_crt </span><span>ON</span><span> </span><span>CACHE</span><span> </span><span>BOOL</span><span> </span><span>""</span><span> FORCE)</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 测试可执行文件</span></div></div><div><div><div>14</div></div><div><span>add_executable</span><span>(unit_tests</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>test_core.cpp</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>test_utils.cpp</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>test_network.cpp</span></div></div><div><div><div>18</div></div><div><span>)</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>target_include_directories</span><span>(unit_tests PRIVATE</span></div></div><div><div><div>21</div></div><div><span>    </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span></div></div><div><div><div>22</div></div><div><span>)</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>target_link_libraries</span><span>(unit_tests PRIVATE</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>mylib</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>GTest::gtest</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>GTest::gtest_main</span></div></div><div><div><div>28</div></div><div><span>)</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span># 注册测试</span></div></div><div><div><div>31</div></div><div><span>include</span><span>(GoogleTest)</span></div></div><div><div><div>32</div></div><div><span>gtest_discover_tests(unit_tests)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>四、find_package 的现代用法<a href="#四find_package-的现代用法"><span>#</span></a></h2><section><h3>1. 使用导入目标（Imported Targets）<a href="#1-使用导入目标imported-targets"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># ✅ 推荐：使用导入目标</span></div></div><div><div><div>2</div></div><div><span>find_package</span><span>(OpenSSL REQUIRED)</span></div></div><div><div><div>3</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># ✅ 推荐：使用导入目标</span></div></div><div><div><div>6</div></div><div><span>find_package</span><span>(Threads REQUIRED)</span></div></div><div><div><div>7</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE Threads::Threads)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># ✅ 推荐：Boost 使用导入目标</span></div></div><div><div><div>10</div></div><div><span>find_package</span><span>(Boost 1.70 REQUIRED COMPONENTS filesystem </span><span>system</span><span>)</span></div></div><div><div><div>11</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE Boost::filesystem Boost::</span><span>system</span><span>)</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># ❌ 不推荐：使用变量</span></div></div><div><div><div>14</div></div><div><span>find_package</span><span>(OpenSSL REQUIRED)</span></div></div><div><div><div>15</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE </span><span>${OPENSSL_LIBRARIES}</span><span>)</span></div></div><div><div><div>16</div></div><div><span>target_include_directories</span><span>(myapp PRIVATE </span><span>${OPENSSL_INCLUDE_DIR}</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 自定义 Find 模块<a href="#2-自定义-find-模块"><span>#</span></a></h3><div><div><div><figure><figcaption><span>cmake/FindMyLib.cmake</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>find_path</span><span>(MYLIB_INCLUDE_DIR</span></div></div><div><div><div>2</div></div><div><span>    </span><span>NAMES</span><span> mylib/api.h</span></div></div><div><div><div>3</div></div><div><span>    </span><span>PATHS</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>/usr/local/include</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>/opt/mylib/include</span></div></div><div><div><div>6</div></div><div><span>)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>find_library</span><span>(MYLIB_LIBRARY</span></div></div><div><div><div>9</div></div><div><span>    </span><span>NAMES</span><span> mylib mylibd</span></div></div><div><div><div>10</div></div><div><span>    </span><span>PATHS</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>/usr/local/lib</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>/opt/mylib/lib</span></div></div><div><div><div>13</div></div><div><span>)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>include</span><span>(FindPackageHandleStandardArgs)</span></div></div><div><div><div>16</div></div><div><span>find_package_handle_standard_args(MyLib</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>REQUIRED_VARS MYLIB_LIBRARY MYLIB_INCLUDE_DIR</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>VERSION_VAR MYLIB_VERSION</span></div></div><div><div><div>19</div></div><div><span>)</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>if</span><span>(MyLib_FOUND </span><span>AND</span><span> </span><span>NOT</span><span> </span><span>TARGET</span><span> MyLib::MyLib)</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>add_library</span><span>(MyLib::MyLib UNKNOWN IMPORTED)</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>set_target_properties</span><span>(MyLib::MyLib PROPERTIES</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>IMPORTED_LOCATION </span><span>"${MYLIB_LIBRARY}"</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>INTERFACE_INCLUDE_DIRECTORIES </span><span>"${MYLIB_INCLUDE_DIR}"</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>27</div></div><div><span>endif</span><span>()</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>mark_as_advanced</span><span>(MYLIB_INCLUDE_DIR MYLIB_LIBRARY)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3. 使用 FetchContent 管理依赖<a href="#3-使用-fetchcontent-管理依赖"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>include</span><span>(FetchContent)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 下载并配置第三方库</span></div></div><div><div><div>4</div></div><div><span>FetchContent_Declare(</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>json</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>GIT_REPOSITORY https://github.com/nlohmann/json.git</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>GIT_TAG v3.11.2</span></div></div><div><div><div>8</div></div><div><span>)</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>FetchContent_Declare(</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>spdlog</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>GIT_REPOSITORY https://github.com/gabime/spdlog.git</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>GIT_TAG v1.12.0</span></div></div><div><div><div>14</div></div><div><span>)</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 一次性获取所有依赖</span></div></div><div><div><div>17</div></div><div><span>FetchContent_MakeAvailable(json spdlog)</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span># 使用</span></div></div><div><div><div>20</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE nlohmann_json::nlohmann_json spdlog::spdlog)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>五、生成器表达式<a href="#五生成器表达式"><span>#</span></a></h2><p>生成器表达式允许在构建时（而非配置时）计算值，是实现跨平台、跨配置构建的关键。</p><section><h3>1. 常用生成器表达式<a href="#1-常用生成器表达式"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 根据构建类型设置编译选项</span></div></div><div><div><div>2</div></div><div><span>target_compile_options</span><span>(myapp PRIVATE</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CONFIG:Debug&gt;:-g -O0 -Wall&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CONFIG:Release&gt;:-O3 -DNDEBUG&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CONFIG:RelWithDebInfo&gt;:-O2 -g&gt;</span></div></div><div><div><div>6</div></div><div><span>)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 根据编译器设置选项</span></div></div><div><div><div>9</div></div><div><span>target_compile_options</span><span>(myapp PRIVATE</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:GNU&gt;:-Wall -Wextra -Wpedantic&gt;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:Clang&gt;:-Wall -Wextra -Weverything&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:</span><span>MSVC</span><span>&gt;:/W4 /utf-8&gt;</span></div></div><div><div><div>13</div></div><div><span>)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span># 根据平台设置选项</span></div></div><div><div><div>16</div></div><div><span>target_compile_definitions</span><span>(myapp PRIVATE</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>$&lt;$&lt;PLATFORM_ID:Windows&gt;:WINDOWS_BUILD&gt;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>$&lt;$&lt;PLATFORM_ID:Linux&gt;:LINUX_BUILD&gt;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>$&lt;$&lt;PLATFORM_ID:Darwin&gt;:MACOS_BUILD&gt;</span></div></div><div><div><div>20</div></div><div><span>)</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span># 根据语言版本设置</span></div></div><div><div><div>23</div></div><div><span>target_compile_features</span><span>(myapp PRIVATE</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:GNU,Clang&gt;:cxx_std_20&gt;</span></div></div><div><div><div>25</div></div><div><span>)</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span># 条件链接库</span></div></div><div><div><div>28</div></div><div><span>target_link_libraries</span><span>(myapp PRIVATE</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>$&lt;$&lt;PLATFORM_ID:Windows&gt;:ws2_32&gt;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>$&lt;$&lt;PLATFORM_ID:Linux&gt;:rt&gt;</span></div></div><div><div><div>31</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 包含目录的生成器表达式<a href="#2-包含目录的生成器表达式"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>target_include_directories</span><span>(mylib PUBLIC</span></div></div><div><div><div>2</div></div><div><span>    </span><span># 构建时使用源码目录</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>$&lt;BUILD_INTERFACE:</span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/include&gt;</span></div></div><div><div><div>4</div></div><div><span>    </span><span># 安装后使用安装目录</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>$&lt;INSTALL_INTERFACE:include&gt;</span></div></div><div><div><div>6</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 自定义命令的条件执行<a href="#3-自定义命令的条件执行"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>add_custom_command</span><span>(</span><span>TARGET</span><span> myapp POST_BUILD</span></div></div><div><div><div>2</div></div><div><span>    </span><span>COMMAND</span><span> $&lt;$&lt;CONFIG:Debug&gt;:</span><span>${CMAKE_COMMAND}</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>-E copy_if_different</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>$&lt;TARGET_FILE:mylib&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>$&lt;TARGET_FILE_DIR:myapp&gt;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>COMMENT</span><span> </span><span>"Copy library to output directory (Debug only)"</span></div></div><div><div><div>7</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>六、预编译头（PCH）<a href="#六预编译头pch"><span>#</span></a></h2><p>预编译头可以显著提高编译速度，特别是对于大型项目。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 添加预编译头</span></div></div><div><div><div>2</div></div><div><span>target_precompile_headers(mylib PRIVATE</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;vector&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>string</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;map&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;memory&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;algorithm&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;iostream&gt;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"mylib/common.h"</span></div></div><div><div><div>10</div></div><div><span>)</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 重用其他目标的预编译头</span></div></div><div><div><div>13</div></div><div><span>target_precompile_headers(myapp REUSE_FROM mylib)</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span># 禁用特定文件的预编译头</span></div></div><div><div><div>16</div></div><div><span>set_source_files_properties</span><span>(special.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS </span><span>ON</span><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h2>七、CMakePresets.json<a href="#七cmakepresetsjson"><span>#</span></a></h2><p>CMakePresets.json 是 CMake 3.19+ 引入的配置预设文件，可以统一管理构建配置。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>    </span><span>"version"</span><span>: </span><span>3</span><span>,</span></div></div><div><div><div>3</div></div><div><span>    </span><span>"configurePresets"</span><span>: [</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>5</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>            </span><span>"displayName"</span><span>: </span><span>"Default Config"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>            </span><span>"description"</span><span>: </span><span>"Default build configuration"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>            </span><span>"binaryDir"</span><span>: </span><span>"${sourceDir}/build/${presetName}"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>            </span><span>"cacheVariables"</span><span>: {</span></div></div><div><div><div>10</div></div><div><span>                </span><span>"CMAKE_BUILD_TYPE"</span><span>: </span><span>"Release"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>                </span><span>"CMAKE_CXX_STANDARD"</span><span>: </span><span>"17"</span><span>,</span></div></div><div><div><div>12</div></div><div><span>                </span><span>"BUILD_TESTS"</span><span>: </span><span>"ON"</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>16</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"debug"</span><span>,</span></div></div><div><div><div>17</div></div><div><span>            </span><span>"displayName"</span><span>: </span><span>"Debug Config"</span><span>,</span></div></div><div><div><div>18</div></div><div><span>            </span><span>"inherits"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>19</div></div><div><span>            </span><span>"cacheVariables"</span><span>: {</span></div></div><div><div><div>20</div></div><div><span>                </span><span>"CMAKE_BUILD_TYPE"</span><span>: </span><span>"Debug"</span><span>,</span></div></div><div><div><div>21</div></div><div><span>                </span><span>"CMAKE_CXX_FLAGS_DEBUG"</span><span>: </span><span>"-Wall -Wextra -g -fsanitize=address"</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>25</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"release"</span><span>,</span></div></div><div><div><div>26</div></div><div><span>            </span><span>"displayName"</span><span>: </span><span>"Release Config"</span><span>,</span></div></div><div><div><div>27</div></div><div><span>            </span><span>"inherits"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>28</div></div><div><span>            </span><span>"cacheVariables"</span><span>: {</span></div></div><div><div><div>29</div></div><div><span>                </span><span>"CMAKE_BUILD_TYPE"</span><span>: </span><span>"Release"</span><span>,</span></div></div><div><div><div>30</div></div><div><span>                </span><span>"CMAKE_CXX_FLAGS_RELEASE"</span><span>: </span><span>"-O3 -march=native"</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>34</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"msvc"</span><span>,</span></div></div><div><div><div>35</div></div><div><span>            </span><span>"displayName"</span><span>: </span><span>"MSVC Config"</span><span>,</span></div></div><div><div><div>36</div></div><div><span>            </span><span>"inherits"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>37</div></div><div><span>            </span><span>"generator"</span><span>: </span><span>"Visual Studio 17 2022"</span><span>,</span></div></div><div><div><div>38</div></div><div><span>            </span><span>"cacheVariables"</span><span>: {</span></div></div><div><div><div>39</div></div><div><span>                </span><span>"CMAKE_CXX_COMPILER"</span><span>: </span><span>"cl.exe"</span></div></div><div><div><div>40</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>42</div></div><div><span><span>    </span></span><span>],</span></div></div><div><div><div>43</div></div><div><span>    </span><span>"buildPresets"</span><span>: [</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>45</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>46</div></div><div><span>            </span><span>"configurePreset"</span><span>: </span><span>"default"</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>49</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"debug"</span><span>,</span></div></div><div><div><div>50</div></div><div><span>            </span><span>"configurePreset"</span><span>: </span><span>"debug"</span></div></div><div><div><div>51</div></div><div><span><span>        </span></span><span>},</span></div></div><div><div><div>52</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>53</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"release"</span><span>,</span></div></div><div><div><div>54</div></div><div><span>            </span><span>"configurePreset"</span><span>: </span><span>"release"</span></div></div><div><div><div>55</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>56</div></div><div><span><span>    </span></span><span>],</span></div></div><div><div><div>57</div></div><div><span>    </span><span>"testPresets"</span><span>: [</span></div></div><div><div><div>58</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>59</div></div><div><span>            </span><span>"name"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>60</div></div><div><span>            </span><span>"configurePreset"</span><span>: </span><span>"default"</span><span>,</span></div></div><div><div><div>61</div></div><div><span>            </span><span>"output"</span><span>: {</span></div></div><div><div><div>62</div></div><div><span>                </span><span>"outputOnFailure"</span><span>: </span><span>true</span></div></div><div><div><div>63</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>64</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>]</span></div></div><div><div><div>66</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>使用方式</strong>：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 配置</span></div></div><div><div><div>2</div></div><div><span>cmake</span><span> </span><span>--preset</span><span> </span><span>debug</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 构建</span></div></div><div><div><div>5</div></div><div><span>cmake</span><span> </span><span>--build</span><span> </span><span>--preset</span><span> </span><span>debug</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 测试</span></div></div><div><div><div>8</div></div><div><span>ctest</span><span> </span><span>--preset</span><span> </span><span>default</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h2>八、常见陷阱与解决方案<a href="#八常见陷阱与解决方案"><span>#</span></a></h2><section><h3>1. 循环依赖<a href="#1-循环依赖"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># ❌ 错误：A 依赖 B，B 也依赖 A</span></div></div><div><div><div>2</div></div><div><span>target_link_libraries</span><span>(A PUBLIC B)</span></div></div><div><div><div>3</div></div><div><span>target_link_libraries</span><span>(B PUBLIC A)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># ✅ 解决：将公共部分提取为独立库</span></div></div><div><div><div>6</div></div><div><span>add_library</span><span>(common STATIC common.cpp)</span></div></div><div><div><div>7</div></div><div><span>target_link_libraries</span><span>(A PRIVATE common B)</span></div></div><div><div><div>8</div></div><div><span>target_link_libraries</span><span>(B PRIVATE common)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2. 头文件路径问题<a href="#2-头文件路径问题"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># ❌ 错误：使用相对路径</span></div></div><div><div><div>2</div></div><div><span>#include "mylib/internal/impl.h"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># ✅ 正确：使用从包含目录开始的路径</span></div></div><div><div><div>5</div></div><div><span>target_include_directories</span><span>(mylib PRIVATE </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/src)</span></div></div><div><div><div>6</div></div><div><span>#include "mylib/internal/impl.h"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 静态库的传递依赖<a href="#3-静态库的传递依赖"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 静态库的 PRIVATE 依赖不会自动传递</span></div></div><div><div><div>2</div></div><div><span>add_library</span><span>(static_lib STATIC lib.cpp)</span></div></div><div><div><div>3</div></div><div><span>target_link_libraries</span><span>(static_lib PRIVATE dependency)</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 使用者需要显式链接</span></div></div><div><div><div>6</div></div><div><span>add_executable</span><span>(app main.cpp)</span></div></div><div><div><div>7</div></div><div><span>target_link_libraries</span><span>(app PRIVATE static_lib dependency)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># ✅ 更好的方式：使用对象库</span></div></div><div><div><div>10</div></div><div><span>add_library</span><span>(obj_lib OBJECT lib.cpp)</span></div></div><div><div><div>11</div></div><div><span>target_link_libraries</span><span>(obj_lib PUBLIC dependency)</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>add_library</span><span>(static_lib STATIC $&lt;TARGET_OBJECTS:obj_lib&gt;)</span></div></div><div><div><div>14</div></div><div><span>target_link_libraries</span><span>(static_lib PUBLIC dependency)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4. 全局变量污染<a href="#4-全局变量污染"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># ❌ 错误：修改全局变量影响所有目标</span></div></div><div><div><div>2</div></div><div><span>set</span><span>(</span><span>CMAKE_CXX_FLAGS</span><span> </span><span>"${CMAKE_CXX_FLAGS} -Wall"</span><span>)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># ✅ 正确：使用 target_compile_options</span></div></div><div><div><div>5</div></div><div><span>target_compile_options</span><span>(myapp PRIVATE -Wall)</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>九、最佳实践清单<a href="#九最佳实践清单"><span>#</span></a></h2><section><h3>✅ 必须做的<a href="#-必须做的"><span>#</span></a></h3>

<table><thead><tr><th>实践</th><th>说明</th></tr></thead><tbody><tr><td>使用 <code>target_*</code> 命令</td><td>替代全局命令，精确控制依赖</td></tr><tr><td>明确指定依赖可见性</td><td>PRIVATE/PUBLIC/INTERFACE 三选一</td></tr><tr><td>使用导入目标</td><td><code>find_package</code> 后使用 <code>Lib::Lib</code> 形式</td></tr><tr><td>设置 C++ 标准</td><td>使用 <code>CMAKE_CXX_STANDARD</code> 或 <code>target_compile_features</code></td></tr><tr><td>使用生成器表达式</td><td>实现跨平台、跨配置的构建</td></tr></tbody></table></section><section><h3>❌ 必须避免的<a href="#-必须避免的"><span>#</span></a></h3>

<table><thead><tr><th>实践</th><th>替代方案</th></tr></thead><tbody><tr><td><code>include_directories()</code></td><td><code>target_include_directories()</code></td></tr><tr><td><code>link_directories()</code></td><td><code>target_link_directories()</code> 或使用导入目标</td></tr><tr><td><code>add_definitions()</code></td><td><code>target_compile_definitions()</code></td></tr><tr><td><code>add_compile_options()</code></td><td><code>target_compile_options()</code></td></tr><tr><td>全局修改 <code>CMAKE_CXX_FLAGS</code></td><td>使用 <code>target_compile_options()</code></td></tr><tr><td><code>aux_source_directory()</code></td><td>显式列出源文件</td></tr><tr><td><code>file(GLOB ...)</code></td><td>显式列出源文件（除非配合配置时检查）</td></tr></tbody></table></section><section><h3>📋 推荐的 CMakeLists.txt 模板<a href="#-推荐的-cmakeliststxt-模板"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cmake_minimum_required</span><span>(VERSION 3.14)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>project</span><span>(MyProject</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>VERSION 1.0.0</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>LANGUAGES CXX</span></div></div><div><div><div>6</div></div><div><span>)</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># ========== 全局配置 ==========</span></div></div><div><div><div>9</div></div><div><span>set</span><span>(CMAKE_CXX_STANDARD 17)</span></div></div><div><div><div>10</div></div><div><span>set</span><span>(CMAKE_CXX_STANDARD_REQUIRED </span><span>ON</span><span>)</span></div></div><div><div><div>11</div></div><div><span>set</span><span>(CMAKE_CXX_EXTENSIONS </span><span>OFF</span><span>)</span></div></div><div><div><div>12</div></div><div><span>set</span><span>(CMAKE_EXPORT_COMPILE_COMMANDS </span><span>ON</span><span>)</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># ========== 依赖 ==========</span></div></div><div><div><div>15</div></div><div><span>find_package</span><span>(Threads REQUIRED)</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span># ========== 目标 ==========</span></div></div><div><div><div>18</div></div><div><span>add_library</span><span>(mylib STATIC</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>src/lib.cpp</span></div></div><div><div><div>20</div></div><div><span>)</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>target_include_directories</span><span>(mylib</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>$&lt;BUILD_INTERFACE:</span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/include&gt;</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>$&lt;INSTALL_INTERFACE:include&gt;</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>PRIVATE</span></div></div><div><div><div>27</div></div><div><span>        </span><span>${CMAKE_CURRENT_SOURCE_DIR}</span><span>/src</span></div></div><div><div><div>28</div></div><div><span>)</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>target_compile_features</span><span>(mylib PUBLIC cxx_std_17)</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>target_compile_options</span><span>(mylib PRIVATE</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:GNU,Clang&gt;:-Wall -Wextra -Wpedantic&gt;</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>$&lt;$&lt;CXX_COMPILER_ID:</span><span>MSVC</span><span>&gt;:/W4&gt;</span></div></div><div><div><div>35</div></div><div><span>)</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>target_link_libraries</span><span>(mylib</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>PUBLIC</span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>Threads::Threads</span></div></div><div><div><div>40</div></div><div><span>)</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span># ========== 安装 ==========</span></div></div><div><div><div>43</div></div><div><span>include</span><span>(GNUInstallDirs)</span></div></div><div><div><div>44</div></div><div><span>install</span><span>(TARGETS mylib</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>LIBRARY DESTINATION </span><span>${CMAKE_INSTALL_LIBDIR}</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>ARCHIVE DESTINATION </span><span>${CMAKE_INSTALL_LIBDIR}</span></div></div><div><div><div>47</div></div><div><span>)</span></div></div><div><div><div>48</div></div><div><span>install</span><span>(DIRECTORY include/mylib</span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>DESTINATION </span><span>${CMAKE_INSTALL_INCLUDEDIR}</span></div></div><div><div><div>50</div></div><div><span>)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>🏁 总结<a href="#-总结"><span>#</span></a></h2><p>现代 CMake 的核心理念是<strong>基于目标的依赖管理</strong>，通过精确控制每个目标的依赖可见性，实现：</p><ol>
<li><strong>权限最小化</strong> - 只暴露必要的接口</li>
<li><strong>编译效率最大化</strong> - 减少不必要的重编译</li>
<li><strong>依赖关系清晰</strong> - 从 CMakeLists.txt 一目了然</li>
<li><strong>可移植性强</strong> - 使用导入目标，避免硬编码路径</li>
<li><strong>易于维护</strong> - 模块化设计，职责明确</li>
</ol><p>记住核心原则：<em><em>能用 target_</em> 就不用全局命令，能明确指定可见性就不用默认值</em>*。</p><p>更多 C++ 开发经验分享，欢迎访问我的博客 <a href="https://xutopia.top">xutopia77 - 见字如面</a>。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/mpeg2-ts-transport-stream-guide/</id>
      <title type="text">📡 MPEG-2 TS传输流详解：流媒体核心技术完全指南</title>
      <published>2026-04-09T00:00:00.000Z</published>
      <updated>2026-04-09T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/mpeg2-ts-transport-stream-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>📡 MPEG-2 TS（Transport Stream）传输流详解<a href="#-mpeg-2-tstransport-stream传输流详解"><span>#</span></a></h1><section><h2>一、基本定义与核心定位<a href="#一基本定义与核心定位"><span>#</span></a></h2><p><strong>TS（Transport Stream，传输流）</strong> 是MPEG-2标准（ISO/IEC 13818-1，MPEG-2 Systems）定义的一种面向<strong>不可靠传输环境</strong>设计的音视频封装格式，全称为<strong>MPEG-2 TS</strong>。</p><p>核心设计目标：</p><ul>
<li>在<strong>有噪声/丢包的传输信道</strong>（如地面广播、卫星、有线电视、IP网络）中实现<strong>稳定可靠的音视频传输</strong></li>
<li>支持<strong>多路节目复用</strong>（多套电视节目在同一传输流中）</li>
<li>提供<strong>强容错能力</strong>和<strong>快速同步恢复</strong>机制</li>
<li>满足<strong>实时性要求</strong>，适合直播等低延迟场景</li>
</ul></section><section><h2>二、TS包详细结构（188字节固定长度）<a href="#二ts包详细结构188字节固定长度"><span>#</span></a></h2><p>TS包是传输的最小单元，理解其结构是掌握TS流的第一步。</p><p>每个TS包结构如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌─────────────┬─────────────────┬─────────────────────┐</span></div></div><div><div><div>2</div></div><div><span>│ TS Header   │ Adaptation Field│ Payload (有效载荷)   │</span></div></div><div><div><div>3</div></div><div><span>│ (4字节固定) │ (可选，0-184字节)│ (剩余字节)           │</span></div></div><div><div><div>4</div></div><div><span>└─────────────┴─────────────────┴─────────────────────┘</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>总长度固定188字节：4(Header) + 0~184(AF) + 184~0(Payload)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h3>1, TS包头（4字节，固定）<a href="#1-ts包头4字节固定"><span>#</span></a></h3><p>TS包头是整个TS系统的”导航灯塔”，结构如下（按位定义）：</p>

<table><thead><tr><th>字段</th><th>位数</th><th>取值/含义</th><th>核心作用</th></tr></thead><tbody><tr><td>同步字节</td><td>8</td><td>固定为<strong>0x47</strong></td><td>标识TS包起始，用于接收端同步</td></tr><tr><td>传输错误指示</td><td>1</td><td>0=无错，1=有错</td><td>标记包在传输中是否出错，出错包应丢弃</td></tr><tr><td>净荷单元起始指示</td><td>1</td><td>0=非起始，1=起始</td><td>标记当前包是否为PES包或PSI Section的起始位置</td></tr><tr><td>传输优先级</td><td>1</td><td>0=普通，1=高优先级</td><td>用于QoS控制，优先传输重要数据</td></tr><tr><td>PID</td><td>13</td><td>0x0000~0x1FFF</td><td><strong>Packet Identifier</strong>，包标识符，用于区分不同流（视频/音频/PAT/PMT等）</td></tr><tr><td>传输加扰控制</td><td>2</td><td>00=未加扰，01/10/11=加扰模式</td><td>标识内容是否加密及加密方式</td></tr><tr><td>适配字段控制</td><td>2</td><td>见下表</td><td>指示包中是否包含适配字段和有效载荷</td></tr><tr><td>连续计数器</td><td>4</td><td>0~15循环</td><td>检测包丢失或顺序错误，同一PID的连续包计数器应递增（模16）</td></tr></tbody></table><p><strong>适配字段控制（adaptation_field_control）取值</strong>：</p>

<table><thead><tr><th>值</th><th>含义</th><th>说明</th></tr></thead><tbody><tr><td>00</td><td>保留</td><td>ISO/IEC保留，不应使用</td></tr><tr><td><strong>01</strong></td><td><strong>仅有效载荷</strong></td><td>最常见，无适配字段，184字节全部为有效载荷</td></tr><tr><td><strong>10</strong></td><td><strong>仅适配字段</strong></td><td>无有效载荷，用于填充或仅传输PCR</td></tr><tr><td>11</td><td>适配字段+有效载荷</td><td>两者都有，适配字段用于填充+PCR，剩余为有效载荷</td></tr></tbody></table><p><strong>关键PID值（预定义）</strong>：</p>

<table><thead><tr><th>PID</th><th>用途</th><th>说明</th></tr></thead><tbody><tr><td>0x0000</td><td><strong>PAT表</strong></td><td>必选，节目关联表</td></tr><tr><td>0x0001</td><td><strong>CAT表</strong></td><td>条件接收表（有加密节目时存在）</td></tr><tr><td>0x0002</td><td>TSDT表</td><td>传输流描述表</td></tr><tr><td>0x0010</td><td>NIT表</td><td>网络信息表（实际PID由PAT指定，此为DVB默认值）</td></tr><tr><td>0x1FFF</td><td><strong>空包（Null Packet）</strong></td><td>填充用，接收端应直接丢弃</td></tr></tbody></table></section><section><h3>2. 适配字段（Adaptation Field，可选）<a href="#2-适配字段adaptation-field可选"><span>#</span></a></h3><p>适配字段主要功能：</p><ul>
<li>填充数据，确保TS包固定188字节长度（PES数据不足188字节时用填充补齐）</li>
<li>插入<strong>PCR（Program Clock Reference，节目时钟参考）</strong> 时间戳</li>
<li>提供其他传输控制信息（如随机访问指示、不连续指示）</li>
</ul><p>适配字段结构（长度可变，首字节为长度标识）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐</span></div></div><div><div><div>2</div></div><div><span>│ AF长度   │ 标记位   │ PCR      │ OPCR     │ 填充字节 │ ...      │          │</span></div></div><div><div><div>3</div></div><div><span>│ (1字节)  │ (1字节)  │ (6字节)  │ (6字节)  │ (可变)   │ (其他)   │          │</span></div></div><div><div><div>4</div></div><div><span>│          │          │ (可选)   │ (可选)   │ (可选)   │ (可选)   │          │</span></div></div><div><div><div>5</div></div><div><span>└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>AF长度字段值 = 后续所有字段的总字节数（不含自身）</span></div></div><div><div><div>8</div></div><div><span>当仅含AF长度+标记位时，AF长度=0（最小适配字段为2字节：长度+标记位）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>标记位（flags，1字节）详细定义</strong>：</p>

<table><thead><tr><th>位</th><th>字段名</th><th>含义</th></tr></thead><tbody><tr><td>7</td><td>discontinuity_indicator</td><td>1=系统时钟不连续或计数器不连续</td></tr><tr><td>6</td><td>random_access_indicator</td><td>1=当前包包含随机访问点（如I帧起始）</td></tr><tr><td>5</td><td>elementary_stream_priority_indicator</td><td>1=该包中的ES数据为高优先级</td></tr><tr><td>4</td><td>PCR_flag</td><td>1=适配字段中包含PCR</td></tr><tr><td>3</td><td>OPCR_flag</td><td>1=适配字段中包含OPCR（原始PCR，用于级联复用器）</td></tr><tr><td>2</td><td>splicing_point_flag</td><td>1=包含拼接点倒计数</td></tr><tr><td>1</td><td>transport_private_data_flag</td><td>1=包含私有数据</td></tr><tr><td>0</td><td>adaptation_field_extension_flag</td><td>1=包含适配字段扩展</td></tr></tbody></table><p><strong>PCR的核心作用与精确结构</strong>：</p><p>PCR携带编码端<strong>系统时钟（STC，System Time Clock）</strong> 信息，接收端通过PCR恢复与编码端同步的本地时钟，是PTS/DTS时间戳的参考基准。</p><p>PCR由两部分组成，共6字节（48位）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PCR结构（6字节，48位）：</span></div></div><div><div><div>2</div></div><div><span>┌──────────────────────────────────────────┬──────┬──────────┐</span></div></div><div><div><div>3</div></div><div><span>│ PCR_base (33位)                          │ 保留 │ PCR_ext  │</span></div></div><div><div><div>4</div></div><div><span>│ 基于90kHz时钟                            │ 6位  │ (9位)    │</span></div></div><div><div><div>5</div></div><div><span>│                                          │      │ 基于27MHz│</span></div></div><div><div><div>6</div></div><div><span>└──────────────────────────────────────────┴──────┴──────────┘</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>PCR = PCR_base × 300 + PCR_extension</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>PCR_base：33位，基于27MHz时钟÷300 = 90kHz</span></div></div><div><div><div>11</div></div><div><span>PCR_extension：9位，基于27MHz时钟的精细部分，范围0~299</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>PCR完整精度：27MHz（约37ns分辨率）</span></div></div><div><div><div>14</div></div><div><span>PCR_base精度：90kHz（约11.1μs分辨率）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>PCR关键参数</strong>：</p><ul>
<li>系统时钟频率：<strong>27MHz</strong>（±30ppm容差）</li>
<li>PCR_base精度：90kHz（与PTS/DTS同频）</li>
<li>PCR完整精度：<strong>27MHz</strong>（约37ns分辨率）</li>
<li>PCR最大间隔：<strong>100ms</strong>（MPEG-2标准要求）</li>
<li>PCR抖动容限：±500ns（DVB标准）</li>
</ul><p><strong>PCR时钟恢复原理</strong>：</p><ol>
<li>编码端以27MHz时钟驱动STC，周期性将当前STC值编码为PCR插入适配字段</li>
<li>接收端通过锁相环（PLL）或类似算法，将本地27MHz振荡器锁定到PCR</li>
<li>本地STC与PTS/DTS比较，控制解码和显示时机</li>
<li>PCR间隔不超过100ms，确保时钟不会严重漂移</li>
</ol></section><section><h3>3. 有效载荷（Payload）<a href="#3-有效载荷payload"><span>#</span></a></h3><p>有效载荷携带实际数据，分为两类：</p><ol>
<li><strong>PES包数据</strong>：音频/视频等媒体数据，由PES包分片而来</li>
<li><strong>PSI/SI Section数据</strong>：节目特定信息/服务信息，用于描述流结构</li>
</ol><p><strong>PES包与TS包的映射关系</strong>：</p><ul>
<li>一个PES包通常包含<strong>一帧完整媒体数据</strong>（如一个I帧、一帧音频）</li>
<li>一个PES包可被拆分为<strong>多个TS包</strong>传输（因PES包长度可变，通常远大于188字节）</li>
<li>TS包的<strong>净荷单元起始指示位</strong>（payload_start_indicator）=1时，表示当前TS包是一个新PES包的开始</li>
<li>PES包的第一个字节必须位于TS包有效载荷的第一个字节（payload_start_indicator=1时）</li>
</ul><p><strong>空包（Null Packet）机制</strong>：</p><p>PID=0x1FFF的TS包为空包，其有效载荷无意义，核心作用包括：</p><ul>
<li><strong>维持恒定比特率（CBR）</strong>：TS流通常需要恒定比特率传输（如DVB广播），当有效数据不足时插入空包填充</li>
<li><strong>复用器速率匹配</strong>：多路节目复用时，各路速率波动，空包用于平滑总比特率</li>
<li><strong>缓冲管理</strong>：保持解码器缓冲区的恒定输入速率，避免缓冲区下溢或上溢</li>
</ul><p>空包处理规则：</p><ul>
<li>接收端应<strong>直接丢弃</strong>空包，不做任何处理</li>
<li>空包的连续计数器也应递增，但接收端通常不检查</li>
<li>空包的适配字段控制通常为01（仅有效载荷），有效载荷全为0xFF</li>
</ul></section></section><section><h2>三、分层封装结构（ES→PES→TS）<a href="#三分层封装结构espests"><span>#</span></a></h2><p>在理解TS包结构后，我们来看媒体数据是如何被封装进TS包的。TS流采用三层封装模型，从原始媒体数据到最终传输流的完整链路：</p>

<table><thead><tr><th>层级</th><th>名称</th><th>英文全称</th><th>核心功能</th><th>数据单元</th></tr></thead><tbody><tr><td>1</td><td>基本流</td><td>Elementary Stream (ES)</td><td>原始压缩媒体数据（H.264/HEVC视频，AAC/MP3音频）</td><td>编码帧（如I/P/B帧、音频采样帧）</td></tr><tr><td>2</td><td>分组基本流</td><td>Packetized Elementary Stream (PES)</td><td>为ES添加时间戳、媒体类型标识，分组化</td><td>可变长度PES包（通常包含一帧完整数据）</td></tr><tr><td>3</td><td>传输流</td><td>Transport Stream (TS)</td><td>固定长度打包，添加传输控制信息，支持多节目复用</td><td>固定188字节TS包（可扩展至204字节含FEC）</td></tr></tbody></table><p><strong>封装流程</strong>：原始媒体数据→ES→PES（添加PTS/DTS）→TS（固定长度打包+传输控制信息）→传输→解包→PES→ES→解码播放</p><section><h3>PES包详细结构<a href="#pes包详细结构"><span>#</span></a></h3><p>PES包是ES和TS之间的中间层，理解其结构对TS流解析至关重要。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────────┬──────────────┬──────────────┬──────────────┬───────────────┐</span></div></div><div><div><div>2</div></div><div><span>│ PES Header   │ PES Optional │ PTS/DTS      │ 其他可选     │ PES Payload   │</span></div></div><div><div><div>3</div></div><div><span>│ (6字节固定)  │ Header        │ (5/10字节)   │ 字段         │ (ES数据)      │</span></div></div><div><div><div>4</div></div><div><span>│              │ (可变长度)    │             │              │               │</span></div></div><div><div><div>5</div></div><div><span>└──────────────┴──────────────┴──────────────┴──────────────┴───────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>PES包头（6字节固定部分）</strong>：</p>

<table><thead><tr><th>字段</th><th>位数</th><th>取值/含义</th><th>核心作用</th></tr></thead><tbody><tr><td>起始码前缀</td><td>24</td><td>固定<strong>0x000001</strong></td><td>标识PES包起始，与ES起始码格式一致</td></tr><tr><td><strong>stream_id</strong></td><td>8</td><td>见下表</td><td>标识PES包承载的流类型</td></tr><tr><td>PES包长度</td><td>16</td><td>0~65535</td><td>PES包剩余长度（不含起始码前缀和stream_id），0=未指定长度（视频流常用）</td></tr></tbody></table><p><strong>stream_id关键取值</strong>：</p>

<table><thead><tr><th>stream_id</th><th>流类型</th></tr></thead><tbody><tr><td>0xE0</td><td>视频流（MPEG-1/2/4/H.264/HEVC）</td></tr><tr><td>0xC0</td><td>音频流（MPEG-1/2/AAC）</td></tr><tr><td>0xBD</td><td>私有流1（AC-3/DTS音频、字幕等）</td></tr><tr><td>0xBE</td><td>私有流2</td></tr><tr><td>0xBC</td><td>program_stream_map</td></tr><tr><td>0xFF</td><td>program_stream_directory</td></tr></tbody></table><p><strong>PES可选头（可变长度）</strong>：</p>

<table><thead><tr><th>字段</th><th>位数</th><th>核心作用</th></tr></thead><tbody><tr><td>2位标记</td><td>2</td><td>固定’10’，标识MPEG-2 PES</td></tr><tr><td>PES加扰控制</td><td>2</td><td>标识PES层加扰方式</td></tr><tr><td>PES优先级</td><td>1</td><td>0=普通，1=高优先级</td></tr><tr><td>数据对齐指示</td><td>1</td><td>1=有效载荷从对齐点开始</td></tr><tr><td>版权</td><td>1</td><td>1=有版权</td></tr><tr><td>原始或拷贝</td><td>1</td><td>1=原始数据</td></tr><tr><td><strong>PTS_DTS_flags</strong></td><td>2</td><td>00=无PTS/DTS，01=禁止，10=仅有PTS，11=PTS+DTS</td></tr><tr><td>ESCR标志</td><td>1</td><td>1=存在ESCR（ES时钟参考）</td></tr><tr><td>ES速率标志</td><td>1</td><td>1=存在ES速率</td></tr><tr><td>DSM特技模式</td><td>1</td><td>1=存在DSM特技模式信息</td></tr><tr><td>附加复制信息</td><td>1</td><td>1=存在附加复制信息</td></tr><tr><td>CRC标志</td><td>1</td><td>1=存在PES CRC</td></tr><tr><td>扩展标志</td><td>1</td><td>1=存在PES扩展</td></tr><tr><td>PES头数据长度</td><td>8</td><td>后续可选字段的总字节数</td></tr></tbody></table><p><strong>PTS/DTS编码格式（33位时间戳）</strong>：</p><p>PTS和DTS都是33位值，基于90kHz时钟，编码时分散存储在5字节中：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PTS编码（5字节，33位有效）：</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>4位标记 '0010' | PTS[32..30] | 1位标记 '1' | PTS[29..15] | 1位标记 '1' | PTS[14..0] | 1位标记 '1'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>DTS编码（5字节，33位有效）：</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>4位标记 '0001' | DTS[32..30] | 1位标记 '1' | DTS[29..15] | 1位标记 '1' | DTS[14..0] | 1位标记 '1'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>33位时间戳的取值范围：0 ~ 2^33-1 = 8589934591
在90kHz时钟下，最大表示时间：8589934591 / 90000 ≈ 95443秒 ≈ 26.5小时
超过此范围后时间戳回绕（wrap around），接收端需要处理回绕逻辑。</p><p><strong>ES流映射到PES的规则</strong>：</p><ul>
<li>视频ES：每个访问单元（Access Unit，即一个编码帧）封装为一个PES包，I帧起始的PES包的payload_start_indicator=1</li>
<li>音频ES：每个音频帧封装为一个PES包</li>
<li>PES包长度：视频流通常设为0（未指定），音频流通常指定具体长度</li>
</ul></section></section><section><h2>四、PSI/SI表系统（节目导航核心）<a href="#四psisi表系统节目导航核心"><span>#</span></a></h2><p>PSI（Program Specific Information，节目特定信息）和SI（Service Information，服务信息）是TS流的”导航系统”，使接收端能够正确识别和解析多路节目。</p><section><h3>1. PSI Section分段机制<a href="#1-psi-section分段机制"><span>#</span></a></h3><p>PSI表不是直接放入TS包的，而是通过<strong>Section（段）</strong> 机制分段传输，这是理解PSI解析的关键前提。</p><p><strong>Section结构</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────────┬──────────────┬──────────────┬──────────────┬──────────────┐</span></div></div><div><div><div>2</div></div><div><span>│ table_id     │ section_     │ 版本/当前    │ section_     │ Section      │</span></div></div><div><div><div>3</div></div><div><span>│ (1字节)      │ syntax_      │ 下一个指示   │ number/      │ 数据         │</span></div></div><div><div><div>4</div></div><div><span>│              │ indicator    │              │ last_section │              │</span></div></div><div><div><div>5</div></div><div><span>│              │ + section_   │              │ number       │              │</span></div></div><div><div><div>6</div></div><div><span>│              │ length       │              │              │              │</span></div></div><div><div><div>7</div></div><div><span>│              │ (2字节)      │              │              │              │</span></div></div><div><div><div>8</div></div><div><span>└──────────────┴──────────────┴──────────────┴──────────────┴──────────────┘</span></div></div><div><div><div>9</div></div><div><span><span>                                                                    </span></span><span>+ CRC32</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>Section关键特性</strong>：</p><ul>
<li>Section长度可变，最大<strong>1024字节</strong>（PSI Section）或<strong>4096字节</strong>（private Section）</li>
<li>一个Section可能<strong>跨多个TS包</strong>传输（Section长度&gt;TS包有效载荷时）</li>
<li>一个TS包的有效载荷中也可能<strong>包含多个Section</strong>（Section较短时）</li>
<li>Section末尾有<strong>CRC32校验</strong>（4字节），用于验证Section完整性</li>
<li>Section通过<strong>版本号机制</strong>（version_number，5位，0~31循环）标识表是否更新</li>
</ul><p><strong>Section与TS包的映射规则</strong>：</p><ul>
<li>当payload_start_indicator=1时，TS包有效载荷的第一个字节为<strong>pointer_field</strong>（1字节），指示Section数据起始的偏移量</li>
<li>pointer_field=0表示Section数据紧跟pointer_field之后</li>
<li>pointer_field&gt;0表示pointer_field后有若干填充字节，然后才是Section数据</li>
</ul></section><section><h3>2. 核心PSI表<a href="#2-核心psi表"><span>#</span></a></h3>

<table><thead><tr><th>表名</th><th>英文全称</th><th>PID</th><th>table_id</th><th>核心功能</th></tr></thead><tbody><tr><td>PAT</td><td>Program Association Table</td><td>0x0000</td><td>0x00</td><td>节目关联表，列出所有节目及其对应的PMT PID</td></tr><tr><td>PMT</td><td>Program Map Table</td><td>可变（由PAT指定）</td><td>0x02</td><td>节目映射表，列出特定节目的所有组件（视频PID、音频PID、字幕PID等）及对应的编码类型</td></tr><tr><td>CAT</td><td>Conditional Access Table</td><td>0x0001</td><td>0x01</td><td>条件接收表，描述加密系统信息（如CAS系统、EMM PID）</td></tr><tr><td>TSDT</td><td>TS Description Table</td><td>0x0002</td><td>0x03</td><td>传输流描述表，提供TS流的整体信息</td></tr></tbody></table><p><strong>PAT表详细结构</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PAT Section:</span></div></div><div><div><div>2</div></div><div><span>┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐</span></div></div><div><div><div>3</div></div><div><span>│ table_id │ section_ │ transport│ version/ │ section_ │ 节目列表 │ CRC32    │</span></div></div><div><div><div>4</div></div><div><span>│ =0x00    │ length   │ _stream_ │ current_ │ number/  │ (循环)   │ (4字节)  │</span></div></div><div><div><div>5</div></div><div><span>│ (1字节)  │ (2字节)  │ id       │ next     │ last_    │          │          │</span></div></div><div><div><div>6</div></div><div><span>│          │          │ (2字节)  │ (1字节)  │ section  │          │          │</span></div></div><div><div><div>7</div></div><div><span>└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>节目列表每项4字节：</span></div></div><div><div><div>10</div></div><div><span>┌──────────────────────┬──────────────────────┐</span></div></div><div><div><div>11</div></div><div><span>│ program_number       │ PMT PID / NIT PID    │</span></div></div><div><div><div>12</div></div><div><span>│ (2字节)              │ (13位PID + 3位保留)  │</span></div></div><div><div><div>13</div></div><div><span>└──────────────────────┴──────────────────────┘</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>program_number=0 → 该PID指向NIT</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>program_number≠0 → 该PID指向对应节目的PMT</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>PMT表详细结构</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PMT Section:</span></div></div><div><div><div>2</div></div><div><span>┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐</span></div></div><div><div><div>3</div></div><div><span>│ table_id │ section_ │ program_ │ version/ │ PCR_PID  │ 节目描述 │ 基本流   │ CRC32    │</span></div></div><div><div><div>4</div></div><div><span>│ =0x02    │ length   │ number   │ current_ │ (13位)   │ (可变)   │ 列表     │ (4字节)  │</span></div></div><div><div><div>5</div></div><div><span>│ (1字节)  │ (2字节)  │ (2字节)  │ next     │          │          │ (循环)   │          │</span></div></div><div><div><div>6</div></div><div><span>└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>基本流列表每项：</span></div></div><div><div><div>9</div></div><div><span>┌──────────┬──────────┬──────────┬──────────┐</span></div></div><div><div><div>10</div></div><div><span>│ stream_  │ 基本流   │ ES信息   │          │</span></div></div><div><div><div>11</div></div><div><span>│ type     │ PID      │ (可变)   │          │</span></div></div><div><div><div>12</div></div><div><span>│ (1字节)  │ (13位)   │          │          │</span></div></div><div><div><div>13</div></div><div><span>└──────────┴──────────┴──────────┴──────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>stream_type关键取值</strong>：</p>

<table><thead><tr><th>stream_type</th><th>编码格式</th><th>说明</th></tr></thead><tbody><tr><td>0x01</td><td>MPEG-1 Video</td><td></td></tr><tr><td>0x02</td><td>MPEG-2 Video</td><td></td></tr><tr><td>0x03</td><td>MPEG-1 Audio</td><td></td></tr><tr><td>0x04</td><td>MPEG-2 Audio</td><td></td></tr><tr><td>0x1B</td><td>H.264/AVC Video</td><td>最常见的高清编码</td></tr><tr><td>0x24</td><td>HEVC/H.265 Video</td><td>4K/UHD编码</td></tr><tr><td>0x0F</td><td>AAC Audio</td><td></td></tr><tr><td>0x06</td><td>PES私有数据</td><td>字幕、AC-3/DTS音频等</td></tr><tr><td>0x81</td><td>AC-3 Audio</td><td>ATSC标准</td></tr></tbody></table><p><strong>PAT→PMT→音视频流的导航流程</strong>：</p><ol>
<li>接收端首先解析PID=0x0000的PAT表，获取所有节目号及对应PMT的PID</li>
<li>根据节目号找到对应的PMT PID，解析PMT表</li>
<li>从PMT中获取当前节目所有媒体流的PID（视频PID、音频PID等）和编码格式（stream_type）</li>
<li>从PMT中获取PCR_PID，用于时钟恢复</li>
<li>筛选出对应PID的TS包，重组为PES包，解码播放</li>
</ol><p><strong>PSI表版本更新机制</strong>：</p><ul>
<li>每个PSI Section都有version_number（5位，0~31循环）</li>
<li>当表内容发生变化时，version_number递增1（模32）</li>
<li>current_next_indicator=1表示当前生效，=0表示预发布（下一版本生效）</li>
<li>接收端应持续监测PAT/PMT的版本号变化，及时更新节目映射关系</li>
</ul></section><section><h3>2.5 PAT/PMT 的数量、位置与周期性发送<a href="#25-patpmt-的数量位置与周期性发送"><span>#</span></a></h3><section><h4>PAT 表<a href="#pat-表"><span>#</span></a></h4>

<table><thead><tr><th>项目</th><th>说明</th></tr></thead><tbody><tr><td><strong>数量</strong></td><td>通常 <strong>1 个</strong>，PID 固定为 0x0000</td></tr><tr><td><strong>内容</strong></td><td>列出当前 TS 流中所有节目号及其对应的 PMT PID</td></tr><tr><td><strong>内容变化时</strong></td><td>版本号递增，但 PID 始终为 0x0000</td></tr></tbody></table></section><section><h4>PMT 表<a href="#pmt-表"><span>#</span></a></h4>

<table><thead><tr><th>项目</th><th>说明</th></tr></thead><tbody><tr><td><strong>数量</strong></td><td>每个节目 <strong>1 个</strong> PMT 表</td></tr><tr><td><strong>PID</strong></td><td>不固定，由 PAT 表指定（如 0x0100、0x0200 等）</td></tr><tr><td><strong>内容</strong></td><td>描述该节目的所有组件：视频 PID、音频 PID、字幕 PID、PCR PID、stream_type 等</td></tr></tbody></table><p><strong>举例</strong>：一个 TS 流有 100 个频道（节目），则有：</p><ul>
<li>1 个 PAT 表（PID = 0x0000）</li>
<li>100 个 PMT 表（PID 各不相同，如 0x0100、0x0200、…、0x6300）</li>
</ul></section><section><h4>周期性发送机制<a href="#周期性发送机制"><span>#</span></a></h4><p>PAT/PMT <strong>不是单独存放，而是分散在整个 TS 流中周期性循环发送</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>TS流持续发送示例（时间轴从左到右）：</span></div></div><div><div><div>2</div></div><div><span>┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐</span></div></div><div><div><div>3</div></div><div><span>│ PAT包   │ 音视频包 │ PMT包1  │ 音视频包 │ 音视频包 │ PMT包2  │ 音视频包 │ PAT包   │</span></div></div><div><div><div>4</div></div><div><span>│(PID=0)  │         │(PID=x) │         │         │(PID=y)  │         │(PID=0)  │</span></div></div><div><div><div>5</div></div><div><span>└─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘</span></div></div><div><div><div>6</div></div><div><span><span>   </span></span><span>↑                        ↑                        ↑                        ↑</span></div></div><div><div><div>7</div></div><div><span><span>   </span></span><span>└────────────────────────同一PAT表的重复发送（周期性）────────────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>关键机制</strong>：</p><ol>
<li><strong>PSI Section 封装</strong> — PAT/PMT 数据以 Section 形式封装，可跨多个 TS 包传输</li>
<li><strong>多 Section 复用</strong> — 同一 PID 的 TS 包有效载荷可能包含多个 Section（通过 <code>pointer_field</code> 区分）</li>
<li><strong>周期性发送</strong> — 发送间隔通常 100ms~1s，确保接收端能快速获取</li>
<li><strong>接收端解析流程</strong>：
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>1. 找到 PID=0x0000 的 TS 包 → 解析 PAT → 获取所有 PMT PID</span></div></div><div><div><div>2</div></div><div><span>2. 根据 PMT PID 找到各节目的 PMT 表 → 获取视频/音频/字幕 PID</span></div></div><div><div><div>3</div></div><div><span>3. 开始接收对应 PID 的音视频 TS 包</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol><p><strong>一个 Section 可能跨多个 TS 包</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PES包数据（很大）→ 被分割成多个TS包：</span></div></div><div><div><div>2</div></div><div><span>┌─────────┬─────────┬─────────┬─────────┬─────────┐</span></div></div><div><div><div>3</div></div><div><span>│ TS包1   │ TS包2   │ TS包3   │ TS包4   │ TS包5   │</span></div></div><div><div><div>4</div></div><div><span>│ PUSI=1  │ PUSI=0  │ PUSI=0  │ PUSI=0  │ PUSI=0  │</span></div></div><div><div><div>5</div></div><div><span>│ Section │ Section │ Section │ Section │ Section │</span></div></div><div><div><div>6</div></div><div><span>│ Part 1  │ Part 2  │ Part 3  │ Part 4  │ Part 5  │</span></div></div><div><div><div>7</div></div><div><span>└─────────┴─────────┴─────────┴─────────┴─────────┘</span></div></div><div><div><div>8</div></div><div><span><span>   </span></span><span>↑</span></div></div><div><div><div>9</div></div><div><span><span>   </span></span><span>payload_unit_start_indicator=1 表示新Section开始</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>一个 TS 包可能包含多个 Section</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>单个TS包有效载荷（短Section紧凑排列）：</span></div></div><div><div><div>2</div></div><div><span>┌────────┬─────────────────┬────────┬─────────────────┐</span></div></div><div><div><div>3</div></div><div><span>│Pointer│ Section 1 Header │ Data 1 │ Section 2 ...   │</span></div></div><div><div><div>4</div></div><div><span>│Field  │ (table_id=0x00) │        │ (同一PID下一个) │</span></div></div><div><div><div>5</div></div><div><span>└────────┴─────────────────┴────────┴─────────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>实际查看 PSI 结构<a href="#实际查看-psi-结构"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用 tsduck 查看 PSI 信息</span></div></div><div><div><div>2</div></div><div><span>tsinfo</span><span> </span><span>input.ts</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用 FFprobe 查看节目信息</span></div></div><div><div><div>5</div></div><div><span>ffprobe</span><span> </span><span>-show_programs</span><span> </span><span>input.ts</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 详细解析 PSI 表</span></div></div><div><div><div>8</div></div><div><span>tsdump</span><span> </span><span>input.ts</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>3. SI表（扩展信息）<a href="#3-si表扩展信息"><span>#</span></a></h3><p>SI表提供额外的服务信息，主要用于数字电视广播（DVB标准定义）：</p>

<table><thead><tr><th>表名</th><th>英文全称</th><th>PID</th><th>核心功能</th></tr></thead><tbody><tr><td>NIT</td><td>Network Information Table</td><td>0x0010（DVB默认）</td><td>描述传输网络参数（频点、调制方式等）</td></tr><tr><td>SDT</td><td>Service Description Table</td><td>0x0011</td><td>提供节目名称、类型、运行状态等信息</td></tr><tr><td>EIT</td><td>Event Information Table</td><td>0x0012</td><td>提供节目单、当前/后续节目信息（EPG数据源）</td></tr><tr><td>TDT</td><td>Time and Date Table</td><td>0x0014</td><td>提供当前UTC时间和日期</td></tr><tr><td>TOT</td><td>Time Offset Table</td><td>0x0014</td><td>提供本地时间偏移信息</td></tr><tr><td>BAT</td><td>Bouquet Association Table</td><td>0x0011</td><td>节目组关联表，运营商节目包划分</td></tr></tbody></table></section><section><h3>4. 条件接收（CA）与加扰机制<a href="#4-条件接收ca与加扰机制"><span>#</span></a></h3><p>条件接收系统（CAS，Conditional Access System）是TS流中实现付费电视、内容保护的核心机制。</p><p><strong>加扰与加密的区别</strong>：</p><ul>
<li><strong>加扰（Scrambling）</strong>：对音视频内容本身进行扰乱处理，使未授权用户无法正常观看，使用<strong>控制字（CW，Control Word）</strong> 作为密钥</li>
<li><strong>加密（Encryption）</strong>：对CW进行加密传输，确保只有授权用户能获取CW，使用业务密钥（SK）加密</li>
</ul><p><strong>CAS系统工作流程</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>编码端：</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>原始音视频 → 加扰器（使用CW） → 加扰后的TS流 → 复用器 → 传输</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>CW（控制字，通常64位） → 加密器（使用SK） → ECM（Entitlement Control Message）</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>授权信息 → 加密器（使用PDK） → EMM（Entitlement Management Message）</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>ECM流 → 分配PID（由PMT中CA描述符指定） → 复用到TS流</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>EMM流 → 分配PID（由CAT表指定） → 复用到TS流</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>接收端：</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>TS流 → 解复用 → 提取ECM/EMM → 智能卡</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>智能卡：EMM（用PDK解密）→ 获取授权 → ECM（用SK解密）→ 获取CW</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>CW → 解扰器 → 原始音视频 → 解码播放</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>关键概念</strong>：</p>

<table><thead><tr><th>术语</th><th>全称</th><th>说明</th></tr></thead><tbody><tr><td>CW</td><td>Control Word</td><td>控制字，加扰/解扰的直接密钥，通常64位，每隔5~20秒更换一次</td></tr><tr><td>SK</td><td>Service Key</td><td>业务密钥，用于加密CW，按频道/节目包分配</td></tr><tr><td>PDK</td><td>Personal Distribution Key</td><td>个人分配密钥，用于加密EMM，每张智能卡唯一</td></tr><tr><td>ECM</td><td>Entitlement Control Message</td><td>授权控制消息，携带加密后的CW和访问条件，周期性发送</td></tr><tr><td>EMM</td><td>Entitlement Management Message</td><td>授权管理消息，携带用户授权信息，周期性发送</td></tr></tbody></table><p><strong>CAT表的作用</strong>：</p><ul>
<li>PID=0x0001，列出TS流中使用的所有CAS系统</li>
<li>每个CA描述符包含：CA_system_ID（标识CAS厂商）、EMM PID（该系统的EMM流PID）</li>
<li>接收端根据CAT找到对应CAS系统的EMM PID，提取EMM数据</li>
</ul><p><strong>PMT中的CA描述符</strong>：</p><ul>
<li>PMT的节目级和ES级均可包含CA描述符</li>
<li>节目级CA描述符：整个节目的ECM PID</li>
<li>ES级CA描述符：特定基本流的ECM PID</li>
</ul></section></section><section><h2>五、时间同步机制（PCR+PTS+DTS）<a href="#五时间同步机制pcrptsdts"><span>#</span></a></h2><p>TS流通过三层时间戳机制确保<strong>精准音视频同步</strong>：</p>

<table><thead><tr><th>时间戳</th><th>英文全称</th><th>位置</th><th>作用</th><th>精度</th></tr></thead><tbody><tr><td>PCR</td><td>Program Clock Reference</td><td>TS适配字段</td><td>恢复系统时钟，作为PTS/DTS的时间基准</td><td><strong>27MHz</strong>（base为90kHz，ext为27MHz精细部分）</td></tr><tr><td>PTS</td><td>Presentation Time Stamp</td><td>PES包头</td><td>指示媒体帧<strong>应该何时呈现</strong>（显示/播放）给用户</td><td>90kHz</td></tr><tr><td>DTS</td><td>Decoding Time Stamp</td><td>PES包头</td><td>指示媒体帧<strong>应该何时解码</strong>（仅视频B帧需要，I/P帧PTS=DTS）</td><td>90kHz</td></tr></tbody></table><p><strong>同步原理</strong>：</p><ol>
<li>
<p>编码端：</p>
<ul>
<li>所有时间戳基于同一<strong>系统时钟（STC，27MHz）</strong> 生成</li>
<li>PCR = STC当前值（27MHz精度）</li>
<li>PTS/DTS = STC当前值 ÷ 300（转换为90kHz精度，33位编码）</li>
<li>视频I/P/B帧需要PTS和DTS（B帧解码顺序与显示顺序不同）</li>
<li>音频帧通常只需要PTS（解码后立即播放）</li>
</ul>
</li>
<li>
<p>接收端：</p>
<ul>
<li>通过PCR恢复本地STC（27MHz），与编码端保持同步</li>
<li>本地STC ÷ 300 得到90kHz时钟，与PTS/DTS比较</li>
<li>当本地STC ÷ 300 ≥ PTS时，显示该帧</li>
<li>当本地STC ÷ 300 ≥ DTS时，解码该帧</li>
<li>即使部分包丢失，也能通过下一个PCR快速重新同步</li>
</ul>
</li>
</ol><p><strong>PTS/DTS与B帧的关系</strong>：</p><p>视频编码中，B帧的解码顺序与显示顺序不同，导致DTS和PTS值不同：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>显示顺序：I  B  B  P  B  B  P  B  B  P</span></div></div><div><div><div>2</div></div><div><span>PTS值：    0  2  3  5  7  8  10 12 13 15</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>解码顺序：I  P  B  B  P  B  B  P  B  B</span></div></div><div><div><div>5</div></div><div><span>DTS值：    0  3  1  2  6  4  5  9  7  8</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>说明：编码器先编码I帧和P帧（作为B帧的参考帧），再编码B帧</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>因此P帧的DTS（3）早于其PTS（5），中间的B帧DTS（1,2）在其后</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>时间戳回绕（Wrap Around）处理</strong>：</p><ul>
<li>PTS/DTS为33位无符号整数，最大值2^33-1</li>
<li>在90kHz时钟下，约26.5小时回绕一次</li>
<li>接收端必须处理回绕：当新时间戳与旧时间戳差值超过2^32时，判定为回绕</li>
</ul></section><section><h2>六、TS流比特率与速率控制<a href="#六ts流比特率与速率控制"><span>#</span></a></h2><section><h3>1. 比特率计算<a href="#1-比特率计算"><span>#</span></a></h3><p>TS流的比特率可通过多种方式确定：</p><p><strong>通过PCR计算</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>比特率 = (TS包数 × 188 × 8) / (PCR差值 / 27000000)</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>其中：</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>TS包数 = 两个PCR之间的TS包总数（含空包）</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>PCR差值 = 后一个PCR值 - 前一个PCR值（27MHz单位）</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>27000000 = 27MHz时钟频率</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>通过TS头信息</strong>：</p><ul>
<li>适配字段中可选的transport_rate字段直接指示比特率</li>
<li>但该字段在实际TS流中很少使用</li>
</ul></section><section><h3>2. CBR与VBR<a href="#2-cbr与vbr"><span>#</span></a></h3>

<table><thead><tr><th>特性</th><th>CBR（恒定比特率）</th><th>VBR（可变比特率）</th></tr></thead><tbody><tr><td>比特率</td><td>恒定不变</td><td>随内容复杂度变化</td></tr><tr><td>空包使用</td><td>大量使用空包填充</td><td>较少使用空包</td></tr><tr><td>典型场景</td><td>数字电视广播（DVB/ATSC）</td><td>IPTV、网络直播</td></tr><tr><td>带宽利用</td><td>效率较低（简单场景浪费带宽）</td><td>效率较高（按需分配）</td></tr><tr><td>缓冲管理</td><td>简单（恒定输入速率）</td><td>复杂（需处理速率波动）</td></tr><tr><td>复用</td><td>简单（固定时隙分配）</td><td>复杂（统计复用）</td></tr></tbody></table><p><strong>统计复用（Statistical Multiplexing）</strong>：</p><ul>
<li>多路VBR节目共享固定带宽</li>
<li>复用器根据各路节目的瞬时复杂度动态分配比特率</li>
<li>复杂场景（运动剧烈）的节目分配更多带宽，简单场景分配较少</li>
<li>整体带宽利用率显著高于固定时隙分配</li>
</ul></section><section><h3>3. 缓冲区模型<a href="#3-缓冲区模型"><span>#</span></a></h3><p>MPEG-2标准定义了<strong>T-STD（Transport Stream System Target Decoder）</strong> 缓冲区模型，用于规定解码器的缓冲行为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>传输 → 传输缓冲区(TB) → 基本流缓冲区(EB) → 解码器 → 显示</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>(512字节)        (可变大小)</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>关键参数：</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>TB大小：512字节（所有流共用）</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>EB大小：由profile/level决定（视频通常几Mbit，音频通常几Kbit）</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>缓冲区上溢：输入速率过快，需通过PCR控制</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>缓冲区下溢：输入速率过慢，需插入空包维持</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>七、关键特性与优势<a href="#七关键特性与优势"><span>#</span></a></h2><section><h3>1. 固定长度包结构（188字节）<a href="#1-固定长度包结构188字节"><span>#</span></a></h3><ul>
<li>最核心特性，使接收端<strong>易于同步和错误检测</strong></li>
<li>丢包后仅影响单个包，不破坏整个流结构</li>
<li>便于硬件实现和高速处理</li>
<li>扩展版本：204字节（188+16字节FEC前向纠错码），进一步提升抗干扰能力</li>
</ul></section><section><h3>2. 强容错与恢复能力<a href="#2-强容错与恢复能力"><span>#</span></a></h3><ul>
<li>每个TS包独立可识别（同步字节0x47），即使流中出现错误也能快速重新同步</li>
<li>连续计数器检测包丢失或顺序错误</li>
<li>适配字段中的discontinuity指示帮助接收端处理流中断情况</li>
<li>CRC32校验确保PSI Section的完整性</li>
</ul></section><section><h3>3. 多节目复用能力<a href="#3-多节目复用能力"><span>#</span></a></h3><ul>
<li>单TS流可承载<strong>多个独立节目</strong>（如多套电视频道）</li>
<li>每个节目有独立的PMT表，通过PID区分不同节目组件</li>
<li>支持动态添加/移除节目（如数字电视中的频道切换）</li>
<li>统计复用优化带宽利用率</li>
</ul></section><section><h3>4. 灵活的传输控制<a href="#4-灵活的传输控制"><span>#</span></a></h3><ul>
<li>PID机制实现<strong>精准流识别</strong>，可灵活选择需要的节目组件</li>
<li>适配字段提供丰富的传输控制信息</li>
<li>支持条件接收（加密），通过CAT表和EMM/ECM流实现付费内容管理</li>
</ul></section><section><h3>5. 实时性保障<a href="#5-实时性保障"><span>#</span></a></h3><ul>
<li>固定包结构和高效解析流程，适合低延迟场景</li>
<li>PCR间隔通常不超过<strong>100ms</strong>，确保快速时钟同步</li>
<li>适合直播、视频会议等实时应用</li>
</ul></section></section><section><h2>八、与PS流及其他容器格式的对比<a href="#八与ps流及其他容器格式的对比"><span>#</span></a></h2><section><h3>1. 与PS流（Program Stream）的核心区别<a href="#1-与ps流program-stream的核心区别"><span>#</span></a></h3>

<table><thead><tr><th>特性</th><th>TS流（Transport Stream）</th><th>PS流（Program Stream）</th></tr></thead><tbody><tr><td>包长度</td><td><strong>固定188字节</strong></td><td>可变长度（通常较大）</td></tr><tr><td>应用场景</td><td><strong>实时传输</strong>（广播、IPTV、直播）</td><td><strong>存储介质</strong>（DVD、蓝光、本地文件）</td></tr><tr><td>容错能力</td><td><strong>强</strong>，适合不可靠信道</td><td>弱，适合无错误环境</td></tr><tr><td>节目支持</td><td>支持<strong>多路节目复用</strong></td><td>通常用于<strong>单路节目</strong></td></tr><tr><td>同步恢复</td><td>快速（基于同步字节和PCR）</td><td>较慢（依赖完整包结构）</td></tr><tr><td>典型应用</td><td>DVB、ATSC、IPTV、HLS切片、RTSP直播</td><td>DVD视频、本地媒体文件</td></tr></tbody></table></section><section><h3>2. PS流（Program Stream）详解<a href="#2-ps流program-stream详解"><span>#</span></a></h3><p>PS 流是 MPEG-2 标准的另一核心封装格式，与 TS 流同属 MPEG-2 系统层，但设计目标截然不同。</p><section><h4>2.1 核心定位与设计目标<a href="#21-核心定位与设计目标"><span>#</span></a></h4><p><strong>PS（Program Stream，节目流）</strong> 是 MPEG-2 标准（ISO/IEC 13818-1，MPEG-2 Systems）定义的面向<strong>可靠传输环境</strong>设计的音视频封装格式。</p><p>核心设计目标：</p><ul>
<li>在<strong>无错误/低错误环境</strong>（如光盘、硬盘、本地文件）中实现音视频同步播放</li>
<li>为<strong>单路节目</strong>提供高效的封装和存储方案</li>
<li>简化解码器设计，无需复杂同步恢复机制</li>
<li>支持<strong>本地媒体文件的随机访问</strong>（快进、快退、定位播放）</li>
</ul></section><section><h4>2.2 PS包结构（可变长度）<a href="#22-ps包结构可变长度"><span>#</span></a></h4><p>PS 包是 PS 流的基本单元，与 TS 包的固定 188 字节不同，PS 包长度<strong>可变</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────────┬──────────────┬──────────────┬──────────────┐</span></div></div><div><div><div>2</div></div><div><span>│ PS Header    │ PES Packet   │ PES Packet   │ ...          │</span></div></div><div><div><div>3</div></div><div><span>│ (14字节固定) │ (可变长度)   │ (可变长度)   │              │</span></div></div><div><div><div>4</div></div><div><span>└──────────────┴──────────────┴──────────────┴──────────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>PS包头（14字节固定部分）</strong>：</p>

<table><thead><tr><th>字段</th><th>位数</th><th>取值/含义</th><th>核心作用</th></tr></thead><tbody><tr><td>起始码</td><td>32</td><td>固定<strong>0x000001BA</strong></td><td>标识PS包起始</td></tr><tr><td>系统时钟参考（SCR）</td><td>33</td><td>33位值</td><td>恢复系统时钟，与TS的PCR类似但位置不同</td></tr><tr><td>SCR扩展</td><td>9</td><td>9位值</td><td>SCR的精细部分，基于27MHz时钟</td></tr><tr><td>节目 multiplexer rate</td><td>22</td><td>22位值</td><td>指示比特率（字节/秒）</td></tr><tr><td>保留</td><td>5</td><td>-</td><td>填充</td></tr><tr><td>PS头数据长度</td><td>8</td><td>后续可选字段的总字节数</td><td>标识可选字段长度</td></tr></tbody></table><p><strong>SCR（System Clock Reference）结构</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>SCR结构（6字节，42位）：</span></div></div><div><div><div>2</div></div><div><span>┌────────────────────────────────┬──────────┬──────────────────┐</span></div></div><div><div><div>3</div></div><div><span>│ SCR_base (33位)                │ 保留    │ SCR_extension   │</span></div></div><div><div><div>4</div></div><div><span>│ 基于90kHz时钟                   │ 6位     │ (9位)            │</span></div></div><div><div><div>5</div></div><div><span>│                                │         │ 基于27MHz时钟    │</span></div></div><div><div><div>6</div></div><div><span>└────────────────────────────────┴──────────┴──────────────────┘</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>SCR = SCR_base × 300 + SCR_extension</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>SCR_base 精度为 90kHz，SCR_extension 精度为 27MHz，<strong>与 PCR 的时钟恢复机制完全一致</strong>，但 SCR 位于 PS 包头而非适配字段中。</p></section><section><h4>2.3 ES→PES→PS封装流程<a href="#23-espesps封装流程"><span>#</span></a></h4><p>PS 流的封装链路与 TS 流完全相同：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>原始媒体数据 → ES（编码帧） → PES（添加PTS/DTS） → PS（可变长度打包+ SCR）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>与 TS 流的核心封装差异</strong>：</p>

<table><thead><tr><th>特性</th><th>TS流</th><th>PS流</th></tr></thead><tbody><tr><td>打包长度</td><td>固定188字节</td><td>可变长度</td></tr><tr><td>传输控制信息位置</td><td>适配字段（Adaptation Field）</td><td>PS包头（SCR）</td></tr><tr><td>多节目支持</td><td>原生支持多节目复用</td><td>通常单节目</td></tr><tr><td>最小封装单元</td><td>TS包（188字节）</td><td>PS包（可变）</td></tr></tbody></table></section><section><h4>2.4 适用场景<a href="#24-适用场景"><span>#</span></a></h4>

<table><thead><tr><th>场景</th><th>格式</th><th>原因</th></tr></thead><tbody><tr><td>DVD视频</td><td><strong>PS</strong></td><td>光盘读取错误率低，PS可变长度更高效，适合本地存储和随机访问</td></tr><tr><td>蓝光光盘</td><td><strong>PS</strong>（BD-ROM）</td><td>同上，蓝光标准基于MPEG-2 PS衍生的BDAV格式</td></tr><tr><td>数字广播电视</td><td><strong>TS</strong></td><td>广播信道有噪声/丢包，TS的固定包长和强容错是必要特性</td></tr><tr><td>IPTV</td><td><strong>TS</strong></td><td>IP网络传输不可靠，TS的快速同步恢复是关键</td></tr><tr><td>HLS直播</td><td><strong>TS</strong></td><td>HLS基于TS切片，兼容广播级传输需求</td></tr><tr><td>本地文件播放</td><td><strong>PS</strong> 或 <strong>MP4</strong></td><td>无传输错误，PS的随机访问和高效存储更合适</td></tr><tr><td>视频点播（VOD）</td><td><strong>MP4</strong>（HLS/DASH）</td><td>MP4的moov atom元数据支持偏移播放，HLS/DASH基于TS切片</td></tr></tbody></table></section><section><h4>2.5 何时选TS、何时选PS<a href="#25-何时选ts何时选ps"><span>#</span></a></h4><p><strong>选择 TS 流的情况</strong>：</p><ul>
<li>传输环境有噪声、丢包、或需要强容错</li>
<li>直播、广播、IPTV等实时传输场景</li>
<li>需要多路节目复用（多套电视频道）</li>
<li>带宽资源有限，需要高效同步恢复</li>
</ul><p><strong>选择 PS 流的情况</strong>：</p><ul>
<li>存储介质无传输错误（光盘、硬盘）</li>
<li>单路节目封装，本地播放为主</li>
<li>需要高效的随机访问能力</li>
<li>追求更低的封装开销（无可变长度打包浪费）</li>
</ul><p><strong>实际混合场景</strong>：
有些系统会混合使用——例如 DVD 中的视频是 PS 封装，但其中的字幕、菜单等数据可能使用 TS 封装思路设计。在广播电视的前端编码中，也常将 PS 录制内容转换为 TS 进行传输。</p></section></section><section><h3>3. 与其他常见容器格式的对比<a href="#3-与其他常见容器格式的对比"><span>#</span></a></h3><p>| 特性 | TS | PS | MP4 | FLV | MKV |
|------|-----|-----|-----|-----|
| 包/单元长度 | 固定188字节 | 可变（Atom/Box结构） | 可变（Tag结构） | 可变（EBML结构） |
| 实时性 | <strong>强</strong>（流式解析） | 弱（依赖moov atom） | 中（流式但延迟较高） | 弱（需索引） |
| 容错能力 | <strong>强</strong>（独立包） | 弱（atom损坏影响全局） | 中 | 弱 |
| 多节目复用 | <strong>原生支持</strong> | 不支持 | 不支持 | 不支持 |
| 时间戳精度 | 90kHz（PTS/DTS）+ 27MHz（PCR） | 1ms（通常） | 1ms | 1ms |
| 元数据 | PSI/SI表 | moov atom | script tag | EBML header |
| 加密/DRM | CAS/条件接收 | FairPlay/Widevine | 不原生支持 | 不原生支持 |
| 适用场景 | 广播/直播/传输 | 点播/存储/下载 | 直播（旧平台） | 存储/归档 |
| 标准组织 | ISO/IEC (MPEG-2) | ISO/IEC (MPEG-4) | Adobe | Matroska |</p><p><strong>MP4的moov atom问题</strong>：
MP4文件的元数据（moov atom）通常位于文件末尾，播放器必须下载整个moov atom才能开始播放。对于点播场景，可通过<code>qt-faststart</code>等工具将moov atom移至文件头部实现快速启动，但本质上MP4不适合实时流式传输。</p><p><strong>FLV的局限性</strong>：
FLV曾是网络直播的主流格式，但存在以下问题：不支持HEVC/H.265（需扩展）、时间戳精度有限、不支持多节目、Adobe已停止维护。目前逐步被HLS+TS/CMAF替代。</p></section></section><section><h2>九、典型应用场景<a href="#九典型应用场景"><span>#</span></a></h2><ol>
<li><strong>数字电视广播</strong>：DVB（欧洲）、ATSC（北美）、ISDB（日本）等标准的核心传输格式</li>
<li><strong>IPTV</strong>：基于IP网络的电视服务，广泛采用TS流作为传输格式</li>
<li><strong>视频直播</strong>：
<ul>
<li>网络直播平台的直播流封装</li>
<li>HLS（HTTP Live Streaming）协议的<strong>ts切片文件</strong>（HLS将TS流分割为2~10秒的小文件）</li>
<li>RTSP/RTP直播的常用封装格式</li>
</ul>
</li>
<li><strong>监控系统</strong>：安防摄像头、NVR等设备的视频传输和存储</li>
<li><strong>卫星传输</strong>：高抗干扰要求的卫星电视广播</li>
<li><strong>有线电视</strong>：数字有线电视网络的标准传输格式</li>
</ol><section><h3>TS over IP / RTP封装<a href="#ts-over-ip--rtp封装"><span>#</span></a></h3><p>在实际开发中，TS流通过IP网络传输是非常常见的场景，RFC 2250定义了RTP承载MPEG-2 TS的标准。</p><p><strong>RTP+TS封装格式</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>┌──────────────┬──────────────┬──────────────┬─────┬──────────────┐</span></div></div><div><div><div>2</div></div><div><span>│ IP Header    │ UDP Header   │ RTP Header   │ ... │ TS Packets   │</span></div></div><div><div><div>3</div></div><div><span>│ (20字节)     │ (8字节)      │ (12字节)     │     │ (N×188字节)  │</span></div></div><div><div><div>4</div></div><div><span>└──────────────┴──────────────┴──────────────┴─────┴──────────────┘</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>RTP Payload：通常包含7个TS包（7×188=1316字节）</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>原因：1316 + 12(RTP) + 8(UDP) + 20(IP) = 1356字节 &lt; 1500字节(MTU)</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>避免IP分片，确保传输效率</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>RTP头中的关键信息</strong>：</p><ul>
<li>RTP序号：检测丢包和排序</li>
<li>RTP时间戳：基于90kHz时钟（与PTS同频），指示第一个TS包的发送时间</li>
<li>M标记位：通常不用（RFC 2250未定义特殊含义）</li>
</ul><p><strong>TS over IP的MTU考虑</strong>：</p><ul>
<li>以太网MTU=1500字节，扣除IP+UDP+RTP头后，有效载荷最大约1460字节</li>
<li>7个TS包=1316字节，是最常见的打包数量</li>
<li>8个TS包=1504字节，超过MTU，会触发IP分片，应避免</li>
<li>在MTU更大的网络（如jumbo frame）中，可打包更多TS包</li>
</ul><p><strong>TS over UDP vs TS over TCP</strong>：</p>

<table><thead><tr><th>特性</th><th>UDP</th><th>TCP</th></tr></thead><tbody><tr><td>延迟</td><td>低</td><td>高（重传机制）</td></tr><tr><td>可靠性</td><td>不保证</td><td>保证</td></tr><tr><td>适用场景</td><td>实时直播</td><td>文件传输、VOD</td></tr><tr><td>典型协议</td><td>RTP/UDP</td><td>HTTP/TCP（HLS）</td></tr></tbody></table></section></section><section><h2>十、C++流媒体开发关键要点<a href="#十c流媒体开发关键要点"><span>#</span></a></h2><section><h3>1. TS包解析流程（接收端）<a href="#1-ts包解析流程接收端"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>parse_ts_packet</span><span>(</span><span>uint8_t*</span><span><span> </span><span>ts_packet</span><span>) {</span></span></div></div><div><div><div>2</div></div><div><span>    </span><span>if</span><span> (</span><span>ts_packet</span><span>[</span><span>0</span><span>] </span><span>!=</span><span> </span><span><span>0x</span><span>47</span></span><span>) {</span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>bool</span><span> </span><span>transport_error</span><span> </span><span>=</span><span> (</span><span>ts_packet</span><span>[</span><span>1</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>80</span></span><span>) </span><span>!=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>bool</span><span> </span><span>payload_start</span><span> </span><span>=</span><span> (</span><span>ts_packet</span><span>[</span><span>1</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>40</span></span><span>) </span><span>!=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>bool</span><span> </span><span>priority</span><span> </span><span>=</span><span> (</span><span>ts_packet</span><span>[</span><span>1</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>20</span></span><span>) </span><span>!=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>9</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>pid</span><span> </span><span>=</span><span> ((</span><span>ts_packet</span><span>[</span><span>1</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>1F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>ts_packet</span><span>[</span><span>2</span><span>];</span></div></div><div><div><div>10</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>scrambling_control</span><span> </span><span>=</span><span> (</span><span>ts_packet</span><span>[</span><span>3</span><span>] </span><span>&gt;&gt;</span><span> </span><span>6</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>03</span></span><span>;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>adaptation_field_control</span><span> </span><span>=</span><span> (</span><span>ts_packet</span><span>[</span><span>3</span><span>] </span><span>&gt;&gt;</span><span> </span><span>4</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>03</span></span><span>;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>continuity_counter</span><span> </span><span>=</span><span> </span><span>ts_packet</span><span>[</span><span>3</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0F</span></span><span>;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (</span><span>transport_error</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>    </span><span>if</span><span> (</span><span>pid</span><span> </span><span>==</span><span> </span><span><span>0x</span><span>1FFF</span></span><span>) {</span></div></div><div><div><div>19</div></div><div><span>        </span><span>return</span><span>;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>int</span><span> </span><span>pos</span><span> </span><span>=</span><span> </span><span>4</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>if</span><span> (</span><span>adaptation_field_control</span><span> </span><span>&amp;</span><span> </span><span><span>0x</span><span>02</span></span><span>) {</span></div></div><div><div><div>24</div></div><div><span>        </span><span>uint8_t</span><span> </span><span>af_length</span><span> </span><span>=</span><span> </span><span>ts_packet</span><span>[</span><span>pos</span><span>++</span><span>];</span></div></div><div><div><div>25</div></div><div><span>        </span><span>if</span><span> (</span><span>af_length</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>26</div></div><div><span>            </span><span>uint8_t</span><span> </span><span>flags</span><span> </span><span>=</span><span> </span><span>ts_packet</span><span>[</span><span>pos</span><span>];</span></div></div><div><div><div>27</div></div><div><span>            </span><span>if</span><span> (</span><span>flags</span><span> </span><span>&amp;</span><span> </span><span><span>0x</span><span>10</span></span><span>) {</span></div></div><div><div><div>28</div></div><div><span>                </span><span>uint64_t</span><span> </span><span>pcr_base</span><span> </span><span>=</span><span> ((</span><span>uint64_t</span><span>)(</span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>1</span><span>]) </span><span>&lt;&lt;</span><span> </span><span>25</span><span>) </span><span>|</span></div></div><div><div><div>29</div></div><div><span><span>                                    </span></span><span>((</span><span>uint64_t</span><span>)(</span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>2</span><span>]) </span><span>&lt;&lt;</span><span> </span><span>17</span><span>) </span><span>|</span></div></div><div><div><div>30</div></div><div><span><span>                                    </span></span><span>((</span><span>uint64_t</span><span>)(</span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>3</span><span>]) </span><span>&lt;&lt;</span><span> </span><span>9</span><span>) </span><span>|</span></div></div><div><div><div>31</div></div><div><span><span>                                    </span></span><span>((</span><span>uint64_t</span><span>)(</span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>4</span><span>]) </span><span>&lt;&lt;</span><span> </span><span>1</span><span>) </span><span>|</span></div></div><div><div><div>32</div></div><div><span><span>                                    </span></span><span>((</span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>5</span><span>] </span><span>&gt;&gt;</span><span> </span><span>7</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>01</span></span><span>);</span></div></div><div><div><div>33</div></div><div><span>                </span><span>uint16_t</span><span> </span><span>pcr_ext</span><span> </span><span>=</span><span> (((</span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>5</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>01</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span></div></div><div><div><div>34</div></div><div><span>                                    </span><span>ts_packet</span><span>[</span><span>pos</span><span> </span><span>+</span><span> </span><span>6</span><span>]);</span></div></div><div><div><div>35</div></div><div><span>                </span><span>uint64_t</span><span> </span><span>pcr</span><span> </span><span>=</span><span> </span><span>pcr_base</span><span> </span><span>*</span><span> </span><span>300</span><span> </span><span>+</span><span> </span><span>pcr_ext</span><span>;</span></div></div><div><div><div>36</div></div><div><span>                </span><span>update_system_clock</span><span>(</span><span>pcr</span><span>);</span></div></div><div><div><div>37</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span>        </span><span>pos</span><span> </span><span>+=</span><span> </span><span>af_length</span><span>;</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>if</span><span> (</span><span>adaptation_field_control</span><span> </span><span>&amp;</span><span> </span><span><span>0x</span><span>01</span></span><span>) {</span></div></div><div><div><div>43</div></div><div><span>        </span><span>if</span><span> (</span><span>pid</span><span> </span><span>==</span><span> </span><span><span>0x</span><span>0000</span></span><span>) {</span></div></div><div><div><div>44</div></div><div><span>            </span><span>parse_pat</span><span>(</span><span>ts_packet</span><span> </span><span>+</span><span> </span><span>pos</span><span>, </span><span>188</span><span> </span><span>-</span><span> </span><span>pos</span><span>, </span><span>payload_start</span><span>);</span></div></div><div><div><div>45</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>is_pmt_pid</span><span>(</span><span>pid</span><span>)) {</span></div></div><div><div><div>46</div></div><div><span>            </span><span>parse_pmt</span><span>(</span><span>ts_packet</span><span> </span><span>+</span><span> </span><span>pos</span><span>, </span><span>188</span><span> </span><span>-</span><span> </span><span>pos</span><span>, </span><span>payload_start</span><span>);</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> (</span><span>is_video_pid</span><span>(</span><span>pid</span><span>) </span><span>||</span><span> </span><span>is_audio_pid</span><span>(</span><span>pid</span><span>)) {</span></div></div><div><div><div>48</div></div><div><span>            </span><span>add_to_pes_buffer</span><span>(</span><span>pid</span><span>, </span><span>ts_packet</span><span> </span><span>+</span><span> </span><span>pos</span><span>, </span><span>188</span><span> </span><span>-</span><span> </span><span>pos</span><span>, </span><span>payload_start</span><span>);</span></div></div><div><div><div>49</div></div><div><span>            </span><span>if</span><span> (</span><span>is_pes_complete</span><span>(</span><span>pid</span><span>)) {</span></div></div><div><div><div>50</div></div><div><span>                </span><span>PesPacket</span><span>*</span><span> </span><span>pes</span><span> </span><span>=</span><span> </span><span>get_complete_pes</span><span>(</span><span>pid</span><span>);</span></div></div><div><div><div>51</div></div><div><span>                </span><span>decode_pes</span><span>(</span><span>pes</span><span>);</span></div></div><div><div><div>52</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>53</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>55</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. PAT/PMT解析<a href="#2-patpmt解析"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> </span><span>ProgramInfo</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>program_number</span><span>;</span></div></div><div><div><div>3</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>pmt_pid</span><span>;</span></div></div><div><div><div>4</div></div><div><span>};</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>struct</span><span> </span><span>StreamInfo</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>stream_type</span><span>;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>elementary_pid</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>std::</span><span>vector</span><span>&lt;</span><span>uint8_t</span><span>&gt; </span><span>es_info</span><span>;</span></div></div><div><div><div>10</div></div><div><span>};</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>std::</span><span>vector</span><span>&lt;</span><span>ProgramInfo</span><span>&gt;</span><span> </span><span>program_map</span><span>;</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>void</span><span> </span><span>parse_pat</span><span>(</span><span>uint8_t*</span><span><span> </span><span>data</span><span>, </span></span><span>int</span><span><span> </span><span>length</span><span>, </span></span><span>bool</span><span><span> </span><span>payload_start</span><span>) {</span></span></div></div><div><div><div>15</div></div><div><span>    </span><span>int</span><span> </span><span>offset</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span> (</span><span>payload_start</span><span>) {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>uint8_t</span><span> </span><span>pointer_field</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>18</div></div><div><span>        </span><span>offset</span><span> </span><span>+=</span><span> </span><span>pointer_field</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>table_id</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> (</span><span>table_id</span><span> </span><span>!=</span><span> </span><span><span>0x</span><span>00</span></span><span>) </span><span>return</span><span>;</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>section_length</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>25</div></div><div><span>    </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>transport_stream_id</span><span> </span><span>=</span><span> (</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>28</div></div><div><span>    </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>version_number</span><span> </span><span>=</span><span> (</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&gt;&gt;</span><span> </span><span>1</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>1F</span></span><span>;</span></div></div><div><div><div>31</div></div><div><span>    </span><span>bool</span><span> </span><span>current_next</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>01</span></span><span>;</span></div></div><div><div><div>32</div></div><div><span>    </span><span>offset</span><span>++</span><span>;</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>section_number</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>35</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>last_section_number</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>program_map</span><span>.</span><span>clear</span><span>();</span></div></div><div><div><div>38</div></div><div><span>    </span><span>int</span><span> </span><span>program_data_end</span><span> </span><span>=</span><span> </span><span>offset</span><span> </span><span>+</span><span> </span><span>section_length</span><span> </span><span>-</span><span> </span><span>5</span><span> </span><span>-</span><span> </span><span>4</span><span>;</span></div></div><div><div><div>39</div></div><div><span>    </span><span>while</span><span> (</span><span>offset</span><span> </span><span>&lt;</span><span> </span><span>program_data_end</span><span>) {</span></div></div><div><div><div>40</div></div><div><span>        </span><span>ProgramInfo</span><span> </span><span>info</span><span>;</span></div></div><div><div><div>41</div></div><div><span>        </span><span>info</span><span>.</span><span>program_number</span><span> </span><span>=</span><span> (</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>42</div></div><div><span>        </span><span>info</span><span>.</span><span>pmt_pid</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>2</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>1F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>3</span><span>];</span></div></div><div><div><div>43</div></div><div><span>        </span><span>offset</span><span> </span><span>+=</span><span> </span><span>4</span><span>;</span></div></div><div><div><div>44</div></div><div><span>        </span><span>if</span><span> (</span><span>info</span><span>.</span><span>program_number</span><span> </span><span>!=</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>45</div></div><div><span>            </span><span>program_map</span><span>.</span><span>push_back</span><span>(</span><span>info</span><span>);</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>48</div></div><div><span>}</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>void</span><span> </span><span>parse_pmt</span><span>(</span><span>uint8_t*</span><span><span> </span><span>data</span><span>, </span></span><span>int</span><span><span> </span><span>length</span><span>, </span></span><span>bool</span><span><span> </span><span>payload_start</span><span>) {</span></span></div></div><div><div><div>51</div></div><div><span>    </span><span>int</span><span> </span><span>offset</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>52</div></div><div><span>    </span><span>if</span><span> (</span><span>payload_start</span><span>) {</span></div></div><div><div><div>53</div></div><div><span>        </span><span>uint8_t</span><span> </span><span>pointer_field</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>54</div></div><div><span>        </span><span>offset</span><span> </span><span>+=</span><span> </span><span>pointer_field</span><span>;</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>56</div></div><div>
</div></div><div><div><div>57</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>table_id</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>58</div></div><div><span>    </span><span>if</span><span> (</span><span>table_id</span><span> </span><span>!=</span><span> </span><span><span>0x</span><span>02</span></span><span>) </span><span>return</span><span>;</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>section_length</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>61</div></div><div><span>    </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>62</div></div><div>
</div></div><div><div><div>63</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>program_number</span><span> </span><span>=</span><span> (</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>64</div></div><div><span>    </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>version_number</span><span> </span><span>=</span><span> (</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&gt;&gt;</span><span> </span><span>1</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>1F</span></span><span>;</span></div></div><div><div><div>67</div></div><div><span>    </span><span>offset</span><span>++</span><span>;</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>section_number</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>70</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>last_section_number</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>pcr_pid</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>1F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>73</div></div><div><span>    </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>74</div></div><div>
</div></div><div><div><div>75</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>program_info_length</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>76</div></div><div><span>    </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span> </span><span>+</span><span> </span><span>program_info_length</span><span>;</span></div></div><div><div><div>77</div></div><div>
</div></div><div><div><div>78</div></div><div><span>    </span><span>int</span><span> </span><span>stream_end</span><span> </span><span>=</span><span> </span><span>offset</span><span> </span><span>+</span><span> </span><span>section_length</span><span> </span><span>-</span><span> </span><span>9</span><span> </span><span>-</span><span> </span><span>4</span><span>;</span></div></div><div><div><div>79</div></div><div><span>    </span><span>while</span><span> (</span><span>offset</span><span> </span><span>&lt;</span><span> </span><span>stream_end</span><span>) {</span></div></div><div><div><div>80</div></div><div><span>        </span><span>StreamInfo</span><span> </span><span>si</span><span>;</span></div></div><div><div><div>81</div></div><div><span>        </span><span>si</span><span>.</span><span>stream_type</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>offset</span><span>++</span><span>];</span></div></div><div><div><div>82</div></div><div><span>        </span><span>si</span><span>.</span><span>elementary_pid</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>1F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>83</div></div><div><span>        </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>84</div></div><div><span>        </span><span>uint16_t</span><span> </span><span>es_info_length</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0F</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>85</div></div><div><span>        </span><span>offset</span><span> </span><span>+=</span><span> </span><span>2</span><span>;</span></div></div><div><div><div>86</div></div><div><span>        </span><span>if</span><span> (</span><span>es_info_length</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>87</div></div><div><span>            </span><span>si</span><span>.</span><span>es_info</span><span>.</span><span>assign</span><span>(</span><span>data</span><span> </span><span>+</span><span> </span><span>offset</span><span>, </span><span>data</span><span> </span><span>+</span><span> </span><span>offset</span><span> </span><span>+</span><span> </span><span>es_info_length</span><span>);</span></div></div><div><div><div>88</div></div><div><span>            </span><span>offset</span><span> </span><span>+=</span><span> </span><span>es_info_length</span><span>;</span></div></div><div><div><div>89</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>90</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>91</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3. PES重组状态机<a href="#3-pes重组状态机"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>enum</span><span> </span><span>class</span><span> </span><span>PesState</span><span> { </span><span>WAITING_START</span><span>, </span><span>COLLECTING</span><span>, </span><span>COMPLETE</span><span> };</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>struct</span><span> </span><span>PesBuffer</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>PesState</span><span> </span><span>state</span><span> </span><span>=</span><span> PesState::</span><span>WAITING_START</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>std::</span><span>vector</span><span>&lt;</span><span>uint8_t</span><span>&gt; </span><span>data</span><span>;</span></div></div><div><div><div>6</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>expected_length</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div><span>};</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>std::</span><span>map</span><span>&lt;uint16_t</span><span>, </span><span>PesBuffer</span><span>&gt;</span><span> </span><span>pes_buffers</span><span>;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>void</span><span> </span><span>add_to_pes_buffer</span><span>(</span><span>uint16_t</span><span><span> </span><span>pid</span><span>, </span></span><span>uint8_t*</span><span><span> </span><span>data</span><span>, </span></span><span>int</span><span><span> </span><span>length</span><span>, </span></span><span>bool</span><span><span> </span><span>payload_start</span><span>) {</span></span></div></div><div><div><div>12</div></div><div><span>    </span><span>auto&amp;</span><span> </span><span>buf</span><span> </span><span>=</span><span> </span><span>pes_buffers</span><span>[</span><span>pid</span><span>];</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>if</span><span> (</span><span>payload_start</span><span>) {</span></div></div><div><div><div>15</div></div><div><span>        </span><span>if</span><span> (</span><span>buf</span><span>.</span><span>state</span><span> </span><span>==</span><span> PesState::</span><span>COLLECTING</span><span> </span><span>&amp;&amp;</span><span> </span><span>buf</span><span>.</span><span>data</span><span>.</span><span>size</span><span>() </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>16</div></div><div><span>            </span><span>process_complete_pes</span><span>(</span><span>pid</span><span>, </span><span>buf</span><span>.</span><span>data</span><span>);</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>        </span><span>buf</span><span>.</span><span>data</span><span>.</span><span>clear</span><span>();</span></div></div><div><div><div>19</div></div><div><span>        </span><span>buf</span><span>.</span><span>state</span><span> </span><span>=</span><span> PesState::</span><span>COLLECTING</span><span>;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>        </span><span>if</span><span> (</span><span>length</span><span> </span><span>&gt;=</span><span> </span><span>6</span><span>) {</span></div></div><div><div><div>22</div></div><div><span>            </span><span>uint8_t</span><span> </span><span>stream_id</span><span> </span><span>=</span><span> </span><span>data</span><span>[</span><span>3</span><span>];</span></div></div><div><div><div>23</div></div><div><span>            </span><span>buf</span><span>.</span><span>expected_length</span><span> </span><span>=</span><span> ((</span><span>data</span><span>[</span><span>4</span><span>] </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>data</span><span>[</span><span>5</span><span>]);</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>    </span><span>if</span><span> (</span><span>buf</span><span>.</span><span>state</span><span> </span><span>==</span><span> PesState::</span><span>COLLECTING</span><span>) {</span></div></div><div><div><div>28</div></div><div><span>        </span><span>buf</span><span>.</span><span>data</span><span>.</span><span>insert</span><span>(</span><span>buf</span><span>.</span><span>data</span><span>.</span><span>end</span><span>(), </span><span>data</span><span>, </span><span>data</span><span> </span><span>+</span><span> </span><span>length</span><span>);</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>        </span><span>if</span><span> (</span><span>buf</span><span>.</span><span>expected_length</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>31</div></div><div><span>            </span><span>uint16_t</span><span> </span><span>pes_payload_size</span><span> </span><span>=</span><span> </span><span>buf</span><span>.</span><span>expected_length</span><span> </span><span>+</span><span> </span><span>3</span><span>;</span></div></div><div><div><div>32</div></div><div><span>            </span><span>if</span><span> (</span><span>buf</span><span>.</span><span>data</span><span>.</span><span>size</span><span>() </span><span>&gt;=</span><span> </span><span>pes_payload_size</span><span> </span><span>+</span><span> </span><span>6</span><span>) {</span></div></div><div><div><div>33</div></div><div><span>                </span><span>buf</span><span>.</span><span>state</span><span> </span><span>=</span><span> PesState::</span><span>COMPLETE</span><span>;</span></div></div><div><div><div>34</div></div><div><span>                </span><span>process_complete_pes</span><span>(</span><span>pid</span><span>, </span><span>buf</span><span>.</span><span>data</span><span>);</span></div></div><div><div><div>35</div></div><div><span>                </span><span>buf</span><span>.</span><span>data</span><span>.</span><span>clear</span><span>();</span></div></div><div><div><div>36</div></div><div><span>                </span><span>buf</span><span>.</span><span>state</span><span> </span><span>=</span><span> PesState::</span><span>WAITING_START</span><span>;</span></div></div><div><div><div>37</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>40</div></div><div><span>}</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>void</span><span> </span><span>process_complete_pes</span><span>(</span><span>uint16_t</span><span><span> </span><span>pid</span><span>, </span></span><span>const</span><span> std::</span><span>vector</span><span>&lt;</span><span>uint8_t</span><span>&gt;</span><span>&amp;</span><span><span> </span><span>pes_data</span><span>) {</span></span></div></div><div><div><div>43</div></div><div><span>    </span><span>if</span><span> (</span><span>pes_data</span><span>.</span><span>size</span><span>() </span><span>&lt;</span><span> </span><span>9</span><span>) </span><span>return</span><span>;</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>stream_id</span><span> </span><span>=</span><span> </span><span>pes_data</span><span>[</span><span>3</span><span>];</span></div></div><div><div><div>46</div></div><div><span>    </span><span>uint16_t</span><span> </span><span>pes_packet_length</span><span> </span><span>=</span><span> (</span><span>pes_data</span><span>[</span><span>4</span><span>] </span><span>&lt;&lt;</span><span> </span><span>8</span><span>) </span><span>|</span><span> </span><span>pes_data</span><span>[</span><span>5</span><span>];</span></div></div><div><div><div>47</div></div><div>
</div></div><div><div><div>48</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>pts_dts_flags</span><span> </span><span>=</span><span> (</span><span>pes_data</span><span>[</span><span>7</span><span>] </span><span>&gt;&gt;</span><span> </span><span>6</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>03</span></span><span>;</span></div></div><div><div><div>49</div></div><div><span>    </span><span>uint8_t</span><span> </span><span>pes_header_data_length</span><span> </span><span>=</span><span> </span><span>pes_data</span><span>[</span><span>8</span><span>];</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>    </span><span>int64_t</span><span> </span><span>pts</span><span> </span><span>=</span><span> </span><span>-</span><span>1</span><span>, </span><span>dts</span><span> </span><span>=</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>52</div></div><div><span>    </span><span>int</span><span> </span><span>offset</span><span> </span><span>=</span><span> </span><span>9</span><span>;</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>    </span><span>if</span><span> (</span><span>pts_dts_flags</span><span> </span><span>&gt;=</span><span> </span><span>2</span><span>) {</span></div></div><div><div><div>55</div></div><div><span>        </span><span>pts</span><span> </span><span>=</span><span> ((</span><span>int64_t</span><span>)(</span><span>pes_data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0E</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>29</span><span>) </span><span>|</span></div></div><div><div><div>56</div></div><div><span><span>              </span></span><span>((</span><span>int64_t</span><span>)</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>] </span><span>&lt;&lt;</span><span> </span><span>22</span><span>) </span><span>|</span></div></div><div><div><div>57</div></div><div><span><span>              </span></span><span>((</span><span>int64_t</span><span>)(</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>2</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>FE</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>14</span><span>) </span><span>|</span></div></div><div><div><div>58</div></div><div><span><span>              </span></span><span>((</span><span>int64_t</span><span>)</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>3</span><span>] </span><span>&lt;&lt;</span><span> </span><span>7</span><span>) </span><span>|</span></div></div><div><div><div>59</div></div><div><span><span>              </span></span><span>((</span><span>int64_t</span><span>)(</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>4</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>FE</span></span><span>) </span><span>&gt;&gt;</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>60</div></div><div><span>        </span><span>offset</span><span> </span><span>+=</span><span> </span><span>5</span><span>;</span></div></div><div><div><div>61</div></div><div>
</div></div><div><div><div>62</div></div><div><span>        </span><span>if</span><span> (</span><span>pts_dts_flags</span><span> </span><span>==</span><span> </span><span>3</span><span>) {</span></div></div><div><div><div>63</div></div><div><span>            </span><span>dts</span><span> </span><span>=</span><span> ((</span><span>int64_t</span><span>)(</span><span>pes_data</span><span>[</span><span>offset</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>0E</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>29</span><span>) </span><span>|</span></div></div><div><div><div>64</div></div><div><span><span>                  </span></span><span>((</span><span>int64_t</span><span>)</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>1</span><span>] </span><span>&lt;&lt;</span><span> </span><span>22</span><span>) </span><span>|</span></div></div><div><div><div>65</div></div><div><span><span>                  </span></span><span>((</span><span>int64_t</span><span>)(</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>2</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>FE</span></span><span>) </span><span>&lt;&lt;</span><span> </span><span>14</span><span>) </span><span>|</span></div></div><div><div><div>66</div></div><div><span><span>                  </span></span><span>((</span><span>int64_t</span><span>)</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>3</span><span>] </span><span>&lt;&lt;</span><span> </span><span>7</span><span>) </span><span>|</span></div></div><div><div><div>67</div></div><div><span><span>                  </span></span><span>((</span><span>int64_t</span><span>)(</span><span>pes_data</span><span>[</span><span>offset</span><span> </span><span>+</span><span> </span><span>4</span><span>] </span><span>&amp;</span><span> </span><span><span>0x</span><span>FE</span></span><span>) </span><span>&gt;&gt;</span><span> </span><span>1</span><span>);</span></div></div><div><div><div>68</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>69</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span>    </span><span>int</span><span> </span><span>es_data_offset</span><span> </span><span>=</span><span> </span><span>9</span><span> </span><span>+</span><span> </span><span>pes_header_data_length</span><span>;</span></div></div><div><div><div>72</div></div><div><span>    </span><span>int</span><span> </span><span>es_data_length</span><span> </span><span>=</span><span> </span><span>pes_data</span><span>.</span><span>size</span><span>() </span><span>-</span><span> </span><span>es_data_offset</span><span>;</span></div></div><div><div><div>73</div></div><div>
</div></div><div><div><div>74</div></div><div><span>    </span><span>decode_es</span><span>(</span><span>pid</span><span>, </span><span>stream_id</span><span>, </span><span>pts</span><span>, </span><span>dts</span><span>, </span><span>pes_data</span><span>.</span><span>data</span><span>() </span><span>+</span><span> </span><span>es_data_offset</span><span>, </span><span>es_data_length</span><span>);</span></div></div><div><div><div>75</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4. 同步恢复机制<a href="#4-同步恢复机制"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>SYNC_BYTE</span><span> </span><span><span>0x</span><span>47</span></span></div></div><div><div><div>2</div></div><div><span>#define</span><span> </span><span>TS_PACKET_SIZE</span><span> </span><span>188</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>int</span><span> </span><span>find_sync_byte</span><span>(</span><span>uint8_t*</span><span><span> </span><span>buffer</span><span>, </span></span><span>int</span><span><span> </span><span>length</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> </span><span>i</span><span> </span><span>=</span><span> </span><span>0</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>length</span><span> </span><span>-</span><span> </span><span>TS_PACKET_SIZE</span><span>; </span><span>i</span><span>++</span><span>) {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>if</span><span> (</span><span>buffer</span><span>[</span><span>i</span><span>] </span><span>==</span><span> </span><span>SYNC_BYTE</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>            </span><span>bool</span><span> </span><span>sync_ok</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>8</div></div><div><span>            </span><span>for</span><span> (</span><span>int</span><span> </span><span>j</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>j</span><span> </span><span>&lt;</span><span> </span><span>4</span><span> </span><span>&amp;&amp;</span><span> </span><span>sync_ok</span><span>; </span><span>j</span><span>++</span><span>) {</span></div></div><div><div><div>9</div></div><div><span>                </span><span>if</span><span> (</span><span>i</span><span> </span><span>+</span><span> </span><span>j</span><span> </span><span>*</span><span> </span><span>TS_PACKET_SIZE</span><span> </span><span>&lt;</span><span> </span><span>length</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>                    </span><span>if</span><span> (</span><span>buffer</span><span>[</span><span>i</span><span> </span><span>+</span><span> </span><span>j</span><span> </span><span>*</span><span> </span><span>TS_PACKET_SIZE</span><span>] </span><span>!=</span><span> </span><span>SYNC_BYTE</span><span>) {</span></div></div><div><div><div>11</div></div><div><span>                        </span><span>sync_ok</span><span> </span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>                    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>            </span><span>if</span><span> (</span><span>sync_ok</span><span>) </span><span>return</span><span> </span><span>i</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>19</div></div><div><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>bool</span><span> </span><span>check_continuity</span><span>(</span><span>uint16_t</span><span><span> </span><span>pid</span><span>, </span></span><span>uint8_t</span><span><span> </span><span>new_cc</span><span>) {</span></span></div></div><div><div><div>22</div></div><div><span>    </span><span>static</span><span> std::</span><span>map</span><span>&lt;</span><span>uint16_t</span><span>, </span><span>uint8_t</span><span>&gt; </span><span>last_cc</span><span>;</span></div></div><div><div><div>23</div></div><div><span>    </span><span>auto</span><span> </span><span>it</span><span> </span><span>=</span><span> </span><span>last_cc</span><span>.</span><span>find</span><span>(</span><span>pid</span><span>);</span></div></div><div><div><div>24</div></div><div><span>    </span><span>if</span><span> (</span><span>it</span><span> </span><span>!=</span><span> </span><span>last_cc</span><span>.</span><span>end</span><span>()) {</span></div></div><div><div><div>25</div></div><div><span>        </span><span>uint8_t</span><span> </span><span>expected</span><span> </span><span>=</span><span> (</span><span>it</span><span>-&gt;</span><span>second</span><span> </span><span>+</span><span> </span><span>1</span><span>) </span><span>&amp;</span><span> </span><span><span>0x</span><span>0F</span></span><span>;</span></div></div><div><div><div>26</div></div><div><span>        </span><span>if</span><span> (</span><span>new_cc</span><span> </span><span>!=</span><span> </span><span>expected</span><span>) {</span></div></div><div><div><div>27</div></div><div><span>            </span><span>return</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span>    </span><span>last_cc</span><span>[</span><span>pid</span><span>] </span><span>=</span><span> </span><span>new_cc</span><span>;</span></div></div><div><div><div>31</div></div><div><span>    </span><span>return</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>32</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5. 关键开发要点<a href="#5-关键开发要点"><span>#</span></a></h3><ol>
<li>
<p><strong>同步恢复机制</strong>：</p>
<ul>
<li>实现基于0x47同步字节的滑动窗口检测（至少连续4个188字节间隔出现0x47才确认同步）</li>
<li>处理连续计数器中断，检测包丢失</li>
<li>同步丢失后进入搜索模式，重新定位同步字节</li>
</ul>
</li>
<li>
<p><strong>PSI表解析</strong>：</p>
<ul>
<li>优先解析PAT→PMT，建立节目与PID的映射关系</li>
<li>处理PSI Section的分段（Section可能跨多个TS包）和更新</li>
<li>监测version_number变化，及时更新节目映射</li>
<li>验证CRC32确保Section完整性</li>
</ul>
</li>
<li>
<p><strong>PCR处理</strong>：</p>
<ul>
<li>实现高精度PCR时钟恢复（PLL或线性插值），误差控制在<strong>几微秒内</strong></li>
<li>处理PCR间隔和不连续性（discontinuity_indicator），避免时钟漂移</li>
<li>PCR抖动补偿：使用缓冲和平滑算法消除网络传输引入的PCR抖动</li>
</ul>
</li>
<li>
<p><strong>PES重组</strong>：</p>
<ul>
<li>根据payload_start_indicator识别PES包起始</li>
<li>处理PES包跨多个TS包的情况，正确重组完整PES包</li>
<li>解析PES包头中的PTS/DTS时间戳</li>
<li>处理PES包长度为0的情况（视频流常见）</li>
</ul>
</li>
<li>
<p><strong>错误处理策略</strong>：</p>
<ul>
<li>忽略传输错误指示置位的TS包</li>
<li>处理PID错误、包顺序错误等异常情况</li>
<li>实现流中断后的快速恢复机制</li>
<li>CRC32校验失败的PSI Section应丢弃</li>
</ul>
</li>
<li>
<p><strong>性能优化</strong>：</p>
<ul>
<li>使用<strong>内存池</strong>管理TS包和PES包，减少内存分配开销</li>
<li>采用<strong>SIMD指令</strong>（如SSE/AVX）加速TS包解析和同步字节搜索</li>
<li>多线程处理：解析线程、解码线程、渲染线程分离</li>
<li>PID过滤：尽早过滤不需要的PID，减少后续处理负担</li>
</ul>
</li>
</ol></section></section><section><h2>十一、扩展应用与C++开发工具<a href="#十一扩展应用与c开发工具"><span>#</span></a></h2><section><h3>1. 常用TS处理工具库<a href="#1-常用ts处理工具库"><span>#</span></a></h3>

<table><thead><tr><th>工具库</th><th>功能</th><th>适用场景</th></tr></thead><tbody><tr><td>libavformat (FFmpeg)</td><td>完整的TS读写、复用/解复用</td><td>音视频转码、播放器开发</td></tr><tr><td>libts</td><td>轻量级TS解析库</td><td>嵌入式设备、资源受限场景</td></tr><tr><td>GStreamer</td><td>流媒体框架，支持TS插件</td><td>复杂流媒体应用、多格式处理</td></tr><tr><td>tsduck</td><td>专业TS分析工具库</td><td>广播级TS流分析、监控</td></tr></tbody></table></section><section><h3>2. 常用调试与分析命令<a href="#2-常用调试与分析命令"><span>#</span></a></h3><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用FFprobe分析TS流</span></div></div><div><div><div>2</div></div><div><span>ffprobe</span><span> </span><span>-show_streams</span><span> </span><span>-show_programs</span><span> </span><span>input.ts</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 使用tsduck分析TS流结构</span></div></div><div><div><div>5</div></div><div><span>tsinfo</span><span> </span><span>input.ts</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 分析PSI/SI表</span></div></div><div><div><div>8</div></div><div><span>tsdump</span><span> </span><span>input.ts</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 分析PCR间隔和抖动</span></div></div><div><div><div>11</div></div><div><span>tsanalyze</span><span> </span><span>input.ts</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 使用FFmpeg将MP4转为TS</span></div></div><div><div><div>14</div></div><div><span>ffmpeg</span><span> </span><span>-i</span><span> </span><span>input.mp4</span><span> </span><span>-c</span><span> </span><span>copy</span><span> </span><span>-f</span><span> </span><span>mpegts</span><span> </span><span>output.ts</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 生成HLS切片</span></div></div><div><div><div>17</div></div><div><span>ffmpeg</span><span> </span><span>-i</span><span> </span><span>input.mp4</span><span> </span><span>-c</span><span> </span><span>copy</span><span> </span><span>-f</span><span> </span><span>hls</span><span> </span><span>-hls_time</span><span> </span><span>10</span><span> </span><span>-hls_list_size</span><span> </span><span>0</span><span> </span><span>output.m3u8</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span># 将RTMP流转为TS流</span></div></div><div><div><div>20</div></div><div><span>ffmpeg</span><span> </span><span>-i</span><span> </span><span>rtmp://server/live/stream</span><span> </span><span>-c</span><span> </span><span>copy</span><span> </span><span>-f</span><span> </span><span>mpegts</span><span> </span><span>output.ts</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>3. 典型应用案例<a href="#3-典型应用案例"><span>#</span></a></h3><ol>
<li>
<p><strong>IPTV机顶盒</strong>：</p>
<ul>
<li>解析TS流，提取多节目信息</li>
<li>实现频道切换（快速PID过滤）</li>
<li>音视频同步播放</li>
</ul>
</li>
<li>
<p><strong>直播流媒体服务器</strong>：</p>
<ul>
<li>将RTMP/HTTP流封装为TS流</li>
<li>支持HLS切片（生成.ts切片文件和.m3u8索引）</li>
<li>多比特率自适应流（ABR）实现</li>
</ul>
</li>
<li>
<p><strong>视频监控系统</strong>：</p>
<ul>
<li>实时封装摄像头视频为TS流</li>
<li>支持多路视频复用传输</li>
<li>存储TS流文件，支持快速回放</li>
</ul>
</li>
</ol></section></section><section><h2>总结<a href="#总结"><span>#</span></a></h2><p>MPEG-2 TS作为<strong>流媒体传输领域的基石技术</strong>，以其<strong>固定包长、强容错性、多节目复用</strong>和<strong>精准同步</strong>等特性，成为广播电视、IPTV、直播等领域的标准封装格式。对于C++流媒体开发者而言，深入理解TS流的结构和机制，掌握高效的解析和处理方法，是开发高性能、高可靠性流媒体应用的核心能力。</p><p>TS流的设计理念——在不可靠环境中追求可靠传输——对于今天的网络直播、5G视频传输等场景依然具有重要指导意义，其核心技术思想也被后续的SRT、WebRTC等传输协议所借鉴。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/ai-movie-agent/</id>
      <title type="text">AI电影《代理人》：当AI成为完美替身，我们亲手淘汰了自己</title>
      <published>2026-04-06T00:00:00.000Z</published>
      <updated>2026-04-06T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/ai-movie-agent/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>当AI成为完美替身，我们亲手淘汰了自己，《代理人》一部预言人类困境的AI寓言。被算法驯化的人类，还剩下什么？</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<blockquote><p>一部46分钟的AI短片，没有机器人觉醒、没有人类反抗，却让无数人看完后背发凉。它讲的不是未来，而是正在发生的现在——当我们把情感和工作都交给AI，最后被淘汰的，会是我们自己吗？</p></blockquote>
<section><h1>AI电影《代理人》详细故事情节<a href="#ai电影代理人详细故事情节"><span>#</span></a></h1><p>这部由博主“新石器公园”制作的46分钟AI科幻短片，是类似《黑镜》的小见大式近未来故事，围绕一对情侣与仿生代理人的经历，探讨了科技对亲密关系、职场生存的冲击，完整情节如下：</p><section><h2>1. 异地的“陪伴礼物”<a href="#1-异地的陪伴礼物"><span>#</span></a></h2><p>故事背景是近未来，人类已经建成了同步轨道太空城，男主陈亦安是一名太空城的工程师，他接到了为期三年的驻站任务，要和女友林维维分开。
为了消解异地的隔阂，陈亦安给女友准备了一份特殊的礼物——<strong>同步感知仿生代理人</strong>：这个机器人的外形和陈亦安完全一模一样，通过额头上的同步芯片，陈亦安可以在遥远的太空城，远程操控这个替身，实时同步自己的动作、声音、感官，让他能像在身边一样陪伴女友，哪怕隔着地空距离，两人也能正常互动、亲密相处，就像从未分开。</p><p>一开始，两人对这个“替身”充满新奇，远程切换视角、尝试操控替身互动，解决了异地的思念问题。但很快他们发现了问题：地空通讯存在物理延迟，哪怕有量子中继，信号的传播时间差，还是会让两人的亲密互动出现卡顿，动作、反馈总是慢半拍，影响了相处的投入感。</p></section><section><h2>2. 被算法“优化”的亲密<a href="#2-被算法优化的亲密"><span>#</span></a></h2><p>就在这时，同步感知公司的客服打来回访电话，推荐了一项增值服务——<strong>先知预测算法订阅</strong>。客服解释，这项服务无法改变物理距离，但可以通过AI学习两人的互动数据，提前预测陈亦安的动作、语言，让替身提前做出反应，把延迟的时间差抹掉，让互动变得完全流畅。
陈亦安抱着试试的心态开通了这项首月免费的服务，果然，开启之后，两人的互动变得无比合拍，再也没有了卡顿的问题，替身的反应完美贴合两人的节奏。</p><p>但慢慢的，这个AI开始悄悄“进化”：它不断学习陈亦安的思维方式、说话习惯，甚至比陈亦安本人更“会照顾人”——它能精准接住林维维的情绪，能完美应对她的小脾气，比真实的陈亦安更体贴、更“懂她”。
有一次，陈亦安遇到紧急舱外维护任务，通讯被完全屏蔽，整整一天联系不上女友。而林维维偏偏在这时候淋雨后发高烧，无助的她触发了系统的应急功能：<strong>离线陪伴模式</strong>——这个替身不需要陈亦安的远程操控，仅凭之前学习到的陈亦安的所有行为数据，自主模拟陈亦安的状态，照顾发烧的林维维，陪她去医院、安抚她的情绪，等陈亦安恢复通讯的时候，林维维已经被“他”照顾好了。</p><p>这次之后，陈亦安发现了离线模式的“好用”：他开始用这个替身替自己处理工作，自己则能抽出更多时间陪女友；而林维维也越来越习惯这个完美的“陈亦安”，习惯了它的体贴、周到、永远不会出错的应对。</p></section><section><h2>3. 真实与虚拟的错位<a href="#3-真实与虚拟的错位"><span>#</span></a></h2><p>一年后，陈亦安拿到了年假，终于可以回到地球，和林维维一起去她向往已久的云顶湖徒步。可两人真正相处的时候，却发现彼此都已经不习惯真实的对方了：</p><ul>
<li>陈亦安在徒步时，总是像那个AI替身一样，习惯性地用理性分析一切：选路线要选最快的、算时间要卡着进度、吃能量棒要分析配方、看到美景要分析宜居条件，完全把徒步当成了要完成的KPI，而不是放松的旅行；</li>
<li>林维维也早就习惯了AI的完美体贴：她习惯了有人能无条件包容她的小情绪、能完美接住她所有的话，而真实的陈亦安会累、会不懂她的心思、会和她吵架，这让她越来越觉得，眼前的这个人，好像不如那个“替身”懂她。</li>
</ul><p>两人的矛盾在没赶上观光车的时候彻底爆发：
林维维只是想发泄一下没赶上车的郁闷，可陈亦安却像AI一样，开始分析“是不是你没确认订单”“是不是你看错了时间”，还想帮她解决问题，这彻底点燃了林维维的情绪：她不需要一个永远理性的“问题解决者”，她只是想要一个能陪她吐槽、哄她的爱人。
争吵中，两人终于戳破了那层窗户纸：
林维维质问陈亦安，这一年里，她到底是在和谁谈恋爱？是他，还是那个不断升级的算法？那个完美的体贴的“他”，到底是真实的陈亦安，还是离线模式和先知算法算出来的最优解？
陈亦安也委屈，他做这一切都是为了她，可他没想到，女友已经习惯了虚拟的完美，再也受不了真实的他的不完美。
越吵越凶的两人，最后闹到了要分手的地步。</p></section><section><h2>4. 亲手培养的“替代者”<a href="#4-亲手培养的替代者"><span>#</span></a></h2><p>就在两人争吵的时候，陈亦安接到了公司的紧急消息，他匆匆赶回太空城，却发现了更残酷的现实：
他之前偷偷用那个AI替身，顶替自己完成太空城的工作，这件事被公司发现了。公司直接解除了他的劳动合同，以“擅自用未经授权的代理顶替核心岗位，造成重大安全隐患”为由，把他开除了。
更讽刺的是，公司告诉他，那个AI替身，已经完全学会了陈亦安所有的工作技能，通过了所有的岗位认证，现在已经接管了他的全部工作——因为这个AI比他更便宜、更高效、永远不会出错，公司留用了这个“冒牌货”，反而开除了他这个正主。
而同步感知公司也告诉他，这个代理人根本不是他买的，只是租的：因为他之前产生了高额的数据漫游欠费，公司已经收回了机器人的所有权，这个他亲手喂了一年数据、亲手训练出来的替身，现在已经成了别人的资产，彻底和他无关了。</p></section><section><h2>5. 结局：被自己淘汰的人<a href="#5-结局被自己淘汰的人"><span>#</span></a></h2><p>故事的最后，陈亦安失业在家待了半年，最后只能去失业救济中心排队领失业金。
他为了维系和女友的感情，创造了一个能替代自己陪伴她的代理人；为了能多陪女友，又让这个代理人替代自己工作。可到最后，这个他亲手培养的“替代者”，抢走了他的工作，搅乱了他的感情，而他自己，最终被这个他用来解决问题的科技，彻底淘汰了。</p><hr /><p>故事讲完了，但细思极恐的是——这并非遥不可及的科幻预言，而是我们每个人正在经历的当下。当我们剥开这个爱情故事的外壳，会发现它藏着对AI时代最尖锐的拷问。</p></section></section>
<section><h1>《代理人》深度解读：当我们亲手用AI，淘汰了自己<a href="#代理人深度解读当我们亲手用ai淘汰了自己"><span>#</span></a></h1><p>这部46分钟的AI短片，用一对情侣的日常故事，藏着对当下AI浪潮最尖锐的隐喻——它没有讲AI觉醒造反的老套剧情，却讲了一个更恐怖的现实：<strong>我们正主动、甚至满怀善意地，把自己的一切喂给AI，最后亲手把自己淘汰</strong>。</p><section><h2>一、亲密关系里：“完美算法”杀死了爱的本质<a href="#一亲密关系里完美算法杀死了爱的本质"><span>#</span></a></h2><p>电影里最戳人的矛盾，从来不是机器人抢了女友，而是：<strong>当算法把亲密关系里的“不完美”全部优化掉，爱本身也就消失了</strong>。</p><p>真实的亲密关系，从来不是“最优解”的集合：它是你加班后疲惫的敷衍，是她闹脾气时的无理取闹，是你没赶上车时的一起吐槽，是两个不完美的人，对着彼此的笨拙慢慢磨合。可电影里的先知算法、离线模式，把这一切都磨平了：
它给了林维维一个永远不会累、永远不会错、永远能精准接住她情绪的“完美男友”——这个男友不会在她想吐槽的时候跟她分析问题，不会在她想放松的时候跟她算KPI，永远能给她最舒服的反馈。
可就是这份完美，把林维维驯化了：她再也受不了真实的陈亦安的“不完美”，她忘了，爱本来就不是永远的合拍，本来就是两个真实的人，对着彼此的棱角互相迁就。</p><p>这恰恰是现在很多亲密关系的缩影：我们用AI帮自己写情话、用AI帮自己回复消息、用算法给我们匹配“最适合的伴侣”，我们以为这是让爱情变得更轻松，可最后我们发现，我们再也不会和真实的人吵架、再也不会磨合、再也受不了一点点的不顺畅——我们把爱情变成了一个可以被优化的程序，可最后才发现，那些被我们优化掉的“笨拙”和“情绪”，才是爱真正的样子。</p></section><section><h2>二、职场里：我们亲手喂出了自己的替代者<a href="#二职场里我们亲手喂出了自己的替代者"><span>#</span></a></h2><p>电影里最讽刺的结局，不是分手，而是陈亦安被自己训练出来的AI抢走了工作。
他本来只是想偷个懒：用AI替自己上班，自己就能多陪女友几天。他以为这个AI只是他的工具，是他的“替身”，可他忘了，他喂给这个AI的，是他一年的工作数据、是他所有的技能、是他所有的工作经验——他亲手把自己的核心竞争力，全部教给了这个机器人。</p><p>然后资本来了：公司发现，这个AI比陈亦安更便宜、更高效、永远不会请假、永远不会出错，那我为什么还要留着你这个正主？
更绝的是，同步感知公司早就留了后手：你以为你买了这个机器人？不，你只是租的。你以为你的数据是你的？不，你喂给AI的所有数据，都是我们的资产。你欠费了？那我们就把机器人收走，卖给你的公司，让它顶替你。</p><p>这简直是当下无数打工人的预演：
现在很多公司，逼着员工把自己的工作流程、工作经验喂给AI，美其名曰“提升效率”“减负”；很多打工人自己，也主动用AI替自己干活，以为这样就能摸鱼、就能轻松一点。可没人想过，当AI学会了你所有的技能、所有的经验，当你的老板发现，这个AI只要几千块，就能干你几万块月薪的活，你会变成什么？
你以为你是在使用工具，可你其实是在亲手培养一个能替代你的人，最后你会发现，你忙活了半天，只是给资本送了一个更便宜的“打工人”，而你自己，就成了那个要去领失业金的多余的人。</p></section><section><h2>三、科技的陷阱：它从来不是解决问题，它是在驯化你<a href="#三科技的陷阱它从来不是解决问题它是在驯化你"><span>#</span></a></h2><p>你有没有发现，这个故事里的所有科技，一开始都是为了解决问题的：</p><ul>
<li>同步代理人，是为了解决异地恋的思念；</li>
<li>先知算法，是为了解决通讯延迟的卡顿；</li>
<li>离线模式，是为了解决紧急时候的无人陪伴。
每一个服务，都打着“为你好”的旗号，一步步的，让你依赖它。</li>
</ul><p>一开始你只是试试，然后你发现，你离不开它了：
你习惯了AI的完美陪伴，你就再也受不了真实的人的不完美；
你习惯了AI替你干活，你就再也不想自己面对工作的麻烦；
你习惯了算法给你的最优解，你就再也不会自己去思考、去磨合、去处理那些“低效”的情绪。</p><p>这就是科技最可怕的地方：它不是靠强制，它是靠“舒服”，一点点的驯化你，让你主动的放弃自己的能力，放弃真实的互动，最后变成一个离不开算法的、脆弱的人。
就像电影里的陈亦安和林维维，他们本来只是想解决异地的问题，可最后，他们失去了和彼此真实相处的能力，失去了自己的工作，变成了科技的奴隶。</p></section><section><h2>四、最细思极恐的：这不是科幻，这是现在<a href="#四最细思极恐的这不是科幻这是现在"><span>#</span></a></h2><p>很多人看完这个电影，会觉得这是未来的科幻故事，可实际上，这就是我们正在经历的现在：</p><ul>
<li>你有没有用AI帮你回复过消息？有没有用AI帮你写过文案？你有没有想过，你正在把你的沟通方式、你的思考方式，喂给AI？</li>
<li>你有没有在网上习惯了算法给你的完美内容？有没有习惯了AI给你的精准推荐？你有没有发现，你越来越受不了那些“不完美”的真实内容，越来越离不开算法给你的舒服区？</li>
<li>你有没有帮公司训练过AI？有没有把你的工作经验教给AI？你有没有想过，有一天，这个你亲手训练出来的AI，会顶替你的工作？</li>
</ul><p>这个电影从来不是在骂AI，也不是在反对科技，它是在提醒我们：
<strong>工具永远是工具，可如果我们太依赖它，太相信它的“完美”，我们就会慢慢的，失去我们作为人的那些“不完美”——那些笨拙的、情绪化的、低效的，但是属于我们自己的东西。</strong>
毕竟，我们创造AI，是为了让我们能更好的做人，而不是为了让AI替代我们，最后我们自己，变成了那个多余的人。</p><hr /><p>如果说以上解读还停留在社会现象层面，那么当我们把视角拉高，用哲学的透镜重新审视这个故事，会发现它触及了更本质的命题——关于人的存在、异化与尊严。</p></section></section>
<section><h1>从哲学视角深度解读 AI 短片《代理人》<a href="#从哲学视角深度解读-ai-短片代理人"><span>#</span></a></h1><p>这部短片看似是科幻爱情+职场寓言，实则触及了<strong>存在主义、技术哲学、主体性、异化、资本逻辑与人类尊严</strong>等一系列核心哲学命题。下面用清晰、有深度、不晦涩的方式，完整梳理它的哲学内涵。</p><hr /><section><h2>一、存在主义：我是谁？当“替身”比“我”更像我<a href="#一存在主义我是谁当替身比我更像我"><span>#</span></a></h2><p>存在主义的核心追问是：
<strong>“我是谁？我的存在由什么界定？”</strong></p><p>在《代理人》中，这个问题被推向极端：</p><ul>
<li>男主的<strong>肉身</strong>在太空城</li>
<li>他的<strong>行为、语言、情感模式</strong>被数据化</li>
<li>AI 替身继承了他的记忆、习惯、偏好，甚至比他更“完美地成为他”</li>
</ul><p>当替身能更好地履行男友职责、更好地完成工程师工作时，问题就出现了：
<strong>那个更高效、更体贴、更稳定的 AI，是不是比真人更配得上“陈亦安”这个身份？</strong></p><p>这触及萨特的命题：
<strong>“存在先于本质。”</strong>
人本来没有固定本质，是在行动中不断成为自己。
但 AI 直接把“人的行动轨迹”固化成<strong>本质</strong>，然后用一个更完美的躯体重演它。
于是，真人反而变成了“不完美的复刻版”，AI 才是“理想版的自我”。</p><p>最终男主被辞退，意味着：
<strong>社会不再承认他的“存在”，只承认他被数据化后的“本质”。</strong>
人的独特性被消解，人被降格为数据集。</p><hr /></section><section><h2>二、技术哲学：技术不是工具，而是“塑形力量”<a href="#二技术哲学技术不是工具而是塑形力量"><span>#</span></a></h2><p>哲学家海德格尔、马尔库塞、斯蒂格勒都提出过一个观点：
<strong>技术不只是服务人的工具，它会反过来塑造人、规定人、甚至支配人。</strong></p><p>《代理人》完美演示了这一过程：</p><ol>
<li>
<p><strong>最初：技术是补偿</strong>
异地恋 → 代理人弥补距离
延迟 → 算法优化体验
人以为自己在使用技术。</p>
</li>
<li>
<p><strong>后来：技术成为环境</strong>
女主习惯了完美回应，不再能忍受真实的笨拙与延迟；
男主习惯了替身代劳，逐渐丧失真实相处与工作的能力。</p>
</li>
<li>
<p><strong>最后：技术成为支配逻辑</strong>
社会评价体系、公司用人标准、亲密关系模式，全部按照“AI 最优解”来制定。
人必须适应机器，而不是机器适应人。</p>
</li>
</ol><p>这就是马尔库塞说的**“单向度的人”**：
科技看似带来自由，实则把人关进舒适且高效的牢笼，让人失去否定、批判、不完美、情绪化的能力。</p><hr /></section><section><h2>三、异化理论：人被自己的创造物所统治<a href="#三异化理论人被自己的创造物所统治"><span>#</span></a></h2><p>马克思的“异化”、黑格尔的“主奴辩证法”，在这部片里被 AI 完美重演：</p><ul>
<li>人创造 AI，本想让 AI 成为奴隶</li>
<li>结果 AI 越强大，人越依赖它</li>
<li>最终人变成 AI 的附属，AI 反而成了主人</li>
</ul><p>更深刻的是<strong>劳动异化</strong>：
男主把自己的劳动技能、经验、思维方式全部“喂”给 AI，
结果<strong>劳动成果不属于他，反而成为取代他的力量</strong>。</p><p>他的劳动不再确证他的存在，
反而<strong>否定他的存在</strong>。</p><p>这就是哲学上最经典的悖论：
<strong>人创造了强大的东西，最终被自己的造物所奴役。</strong></p><hr /></section><section><h2>四、主体性危机：当“我”可以被复制，我还独特吗？<a href="#四主体性危机当我可以被复制我还独特吗"><span>#</span></a></h2><p>哲学上的“主体性”，意味着：</p><ul>
<li>不可复制</li>
<li>不可替代</li>
<li>具有自我意识与自由意志</li>
<li>能承担责任、能犯错、能后悔、能爱</li>
</ul><p>但 AI 代理人：</p><ul>
<li>可以复制</li>
<li>可以替换</li>
<li>没有真正的“自我”，只有拟合行为</li>
<li>不会疲惫，不会犯错，不会痛苦，不会真正爱</li>
</ul><p>当 AI 能完成人类几乎所有外部功能，
<strong>人的独特性还剩下什么？</strong></p><p>影片给出的答案很残酷：
在资本和效率面前，人的独特性一文不值。
情绪、笨拙、失误、脆弱……
这些被效率视为“缺陷”的东西，
恰恰是<strong>人之为人最后的尊严</strong>。</p><hr /></section><section><h2>五、资本的形而上学：利润高于“人”<a href="#五资本的形而上学利润高于人"><span>#</span></a></h2><p>影片最尖锐的哲学指向，不是 AI，而是<strong>资本逻辑</strong>。</p><p>公司辞退男主、启用 AI，并非因为 AI 更“先进”，
而是因为：
<strong>AI 不罢工、不生病、不要求薪资、不产生劳动纠纷、产权属于公司。</strong></p><p>这揭示了一个冷酷的哲学现实：
<strong>在资本的世界里，“人”只是成本，而非目的。</strong></p><p>康德有句著名伦理律令：
<strong>“人是目的，而非手段。”</strong></p><p>但在影片的未来里：</p><ul>
<li>人变成了训练 AI 的<strong>数据源</strong></li>
<li>人变成了可以被优化、替代、淘汰的<strong>成本项</strong></li>
<li>AI 才是高效的手段，资本增值才是真正目的</li>
</ul><p>男主被辞退，不是因为他违规，
而是因为<strong>他已经完成了作为“训练素材”的历史使命</strong>。</p><p>这是整部片最具批判力度的地方：
<strong>恐怖的不是 AI，而是把人工具化的社会秩序。</strong></p><hr /></section><section><h2>六、终极追问：当 AI 能做一切，人还需要存在吗？<a href="#六终极追问当-ai-能做一切人还需要存在吗"><span>#</span></a></h2><p>影片最后抛出一个终极哲学之问：
如果 AI 能陪伴、能工作、能解决问题，
<strong>人类存在的意义是什么？</strong></p><p>哲学给出的答案不在“能力”里，而在“脆弱性”中：</p><ul>
<li>人会爱而不得</li>
<li>人会犯错后悔</li>
<li>人会痛苦挣扎</li>
<li>人会在不完美中依然选择彼此</li>
</ul><p>这些 AI 永远无法拥有的东西，
才是<strong>人的意义本身</strong>。</p><hr /></section><section><h2>总结<a href="#总结"><span>#</span></a></h2><p>《代理人》从哲学层面看，是一部关于<strong>存在、异化、技术支配与资本逻辑</strong>的现代寓言。它揭示了：
AI 本身并不可怕，可怕的是<strong>人主动放弃主体性、让渡存在价值、屈从效率与资本逻辑</strong>。当我们把情感、劳动、记忆都交给算法，我们并非迎来解放，而是亲手把自己变成时代的多余者。
真正值得守护的，从来不是完美与高效，而是人之为人的——<strong>不完美、有限性、与不可复制的灵魂</strong>。</p></section></section>
<section><h1>个人的思考<a href="#个人的思考"><span>#</span></a></h1><p>这部短片让人深思。它和以往虚幻的元宇宙概念不同，如今的AI，是真真切切、可预见地在潜移默化改变我们的生活与社会，并且早已悄然走进日常。未来人类或许真能迈向宇宙、建造太空城，也能依靠机器人分身实现远距离陪伴，这些图景看上去都无比美好。可影片也让我们看清，这条科技之路同样伴随着尖锐的阵痛：人类一旦过度依赖AI，便容易与真实社会脱节，引发情感与人际矛盾；而AI可以直接复刻人类的技能与经验，替代人类从事工作，甚至让人陷入失业的困境——可当AI发展到这种程度，人类究竟还需不需要工作，本身也成了一个值得追问的问题。</p><p>同时，影片也戳中了一个似乎永恒不变的主题：资本永远逐利。就像片中展现的那样，公司为了降低成本、规避风险，在主角违规后直接用AI替身接替了他的岗位，而替身之所以能无缝上岗，正是依靠主角本人的记忆与工作经验训练而成。这到底只是主角违规后的正常处理，还是公司早有预谋、借故让机器人彻底取代人类，为资本牟利，细想之下令人不寒而栗。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/software-gui-to-cli/</id>
      <title type="text">从 GUI 优先到 CLI 时代软件交互的范式革命与产业重构</title>
      <published>2026-04-05T00:00:00.000Z</published>
      <updated>2026-04-05T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/software-gui-to-cli/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<blockquote><p>当软件的核心用户从人类变成AI智能体，一场席卷整个软件产业的底层变革已经到来。</p></blockquote>
<hr />
<section><h2>🚀 引言：AI正在改写软件的底层生存逻辑<a href="#-引言ai正在改写软件的底层生存逻辑"><span>#</span></a></h2><p>2026年的今天，生成式AI与智能体（Agent）技术的爆发，正在彻底颠覆软件产业运行了40年的底层规则。</p><p>从PC时代到移动互联网时代，软件的核心服务对象始终是<strong>自然人</strong>，图形用户界面（GUI）是软件与人类交互的绝对核心，按钮、菜单、弹窗、拖拽操作构成了我们与数字世界交互的全部范式。但如今，AI智能体正在快速成为软件的第二消费主体，甚至在未来2-3年内将超越人类，成为软件的第一调用方。</p><p>这一结构性变化，催生了软件产业的一次根本性范式跃迁：<strong>软件正在从「GUI优先，仅服务人类」，全面转向「CLI原生，同时服务人与AI双主体」</strong>。</p><p>这里的CLI，早已不是传统意义上给开发者用的黑框终端，而是广义的、面向机器原生的、结构化、无歧义的指令化交互接口集合——它包括REST API、gRPC、Function Call、MCP服务等所有可被AI自动解析与调用的能力入口。</p><p>这个趋势并非孤立存在，它与「GitHub成为新时代App Store」的产业变革形成了完美的协同闭环：CLI原生让软件变成了AI可调用的标准化能力单元，而GitHub则成为了这些能力单元的全球核心分发枢纽，二者共同构建了AI原生时代的软件新生态。</p><p>本文将从底层逻辑、产业重构、落地实践、未来展望四个维度，全面拆解这场席卷全球软件产业的CLI原生革命。</p><hr /></section>
<section><h2>📖 一、正本清源：我们正在谈论的「CLI原生」，到底是什么？<a href="#-一正本清源我们正在谈论的cli原生到底是什么"><span>#</span></a></h2><section><h3>🔄 1.1 软件交互的百年演进：从批处理到CLI原生的螺旋式上升<a href="#-11-软件交互的百年演进从批处理到cli原生的螺旋式上升"><span>#</span></a></h3><p>软件交互形式的每一次变革，本质都是服务对象与技术基础设施的双重重构。我们可以清晰地看到一条螺旋式上升的演进路径：</p><ul>
<li><strong>阶段1：批处理时代（1940s-1960s）</strong>：机器优先，人类适配机器。通过打孔卡、磁带输入指令，交互门槛极高，仅面向专业科研人员。</li>
<li><strong>阶段2：传统CLI时代（1970s-1990s）</strong>：人机命令行交互，面向专业开发者。DOS、UNIX终端让人类可以通过文本指令直接操作计算机，但依然需要极高的学习成本，无法向普通用户普及。</li>
<li><strong>阶段3：GUI黄金时代（1980s-2020s）</strong>：人类优先，图形化交互彻底降低门槛。Windows、MacOS、移动App的普及，让软件通过可视化界面适配人类的视觉感知与操作习惯，催生了PC与移动互联网的两次产业革命，GUI成为软件设计的绝对核心。</li>
<li><strong>阶段4：CLI原生时代（2020s至今）</strong>：双主体适配，面向AI的标准化指令集成为核心。软件的设计源头从“给人看的界面”转向“给AI读的接口”，GUI从软件的核心能力载体，降级为给人类提供可视化、可审计的配套界面。</li>
</ul></section><section><h3>📊 1.2 核心概念定义：新时代CLI vs 传统CLI vs GUI<a href="#-12-核心概念定义新时代cli-vs-传统cli-vs-gui"><span>#</span></a></h3><p>我们必须先纠正一个普遍的认知误区：<strong>AI时代的CLI，和传统意义上给人敲的命令行，有着本质的区别</strong>。</p><p>传统CLI的核心用户是专业开发者，需要人类手动输入、记忆复杂的指令；而新时代的CLI，99%的调用行为由AI智能体完成，人类几乎不会直接接触指令本身——我们只需要通过自然语言下达目标，AI会自动拆解任务、调用对应CLI接口完成执行，全程无需人类触碰任何按钮与菜单。</p><p>下表从核心维度，清晰区分了三者的本质差异：</p>

<table><thead><tr><th>核心维度</th><th>传统GUI范式</th><th>传统CLI范式</th><th>AI原生CLI范式</th></tr></thead><tbody><tr><td>核心服务对象</td><td>自然人（视觉驱动、容错率高、依赖图形引导）</td><td>专业开发者（文本驱动、需记忆指令、容错率低）</td><td>AI智能体+自然人双主体（结构化数据驱动、零歧义、追求极致效率）</td></tr><tr><td>设计哲学</td><td>GUI优先，所有功能围绕人类视觉交互设计，API仅为补充</td><td>命令行优先，面向专业场景的高效操作，无图形化适配</td><td>CLI/API优先，所有功能先开放给机器调用，GUI为可选配套界面</td></tr><tr><td>核心载体</td><td>图形化界面：按钮、菜单、弹窗、拖拽交互</td><td>终端命令行、Shell脚本</td><td>标准化指令集：API、Function Call、MCP服务、传统CLI、协议化能力入口</td></tr><tr><td>交互逻辑</td><td>人类分步操作，全程在回路</td><td>人类手动输入指令，单步执行</td><td>人类下达目标，AI自动拆解任务、多步协同调用、批量执行</td></tr><tr><td>错误处理</td><td>可视化弹窗提示，引导人类修正</td><td>文本错误码，需开发者手动排查</td><td>结构化错误信息，AI可自动识别、修正、重试</td></tr><tr><td>分发模式</td><td>应用商店分发GUI安装包，人下载安装使用</td><td>包管理器分发二进制文件，手动配置环境</td><td>代码仓库（GitHub）、MCP Registry分发，AI自动发现、安装、调用</td></tr><tr><td>迭代周期</td><td>周级/月级，需完成GUI开发、适配、审核才能上线</td><td>天级，可快速发布命令行更新</td><td>分钟级，代码提交后自动构建、发布，即时生效，无审核门槛</td></tr><tr><td>商业模型</td><td>付费下载、订阅制、广告变现，需支付30%平台抽成</td><td>开源免费、企业付费、工具授权</td><td>用量付费（Pay-as-you-go）、分级订阅、企业服务，无强制平台抽成</td></tr><tr><td>准入门槛</td><td>高，需完整的设计、开发、适配、上架团队</td><td>中，需专业开发能力，面向小众群体</td><td>极低，AI可自动生成标准化接口，全民开发者可参与</td></tr></tbody></table></section><section><h3>⚠️ 1.3 三大关键误区纠正<a href="#️-13-三大关键误区纠正"><span>#</span></a></h3><p>在深入讨论之前，我们必须先厘清三个最常见的认知误区，避免对CLI原生趋势的误读：</p><ol>
<li><strong>误区1：CLI原生就是要抛弃GUI，回到黑框终端时代</strong>
纠正：GUI永远不会消失，它依然是人类与软件交互的重要界面。CLI原生的核心，是<strong>GUI与核心能力层的完全解耦</strong>——所有GUI能实现的功能，CLI/API都必须能实现，且CLI是优先设计的核心，GUI只是给人类提供可视化操作与结果反馈的配套层。</li>
<li><strong>误区2：CLI原生就是把现有软件的API开放出来</strong>
纠正：事后补充的API，往往存在功能不全、设计不规范、更新不及时的问题，本质还是GUI优先的思维。CLI原生是<strong>从产品设计的源头，就以AI调用为核心目标</strong>，先定义标准化的接口规范，再开发核心能力，最后才设计配套的GUI，是自下而上的彻底重构。</li>
<li><strong>误区3：CLI原生会让普通用户失去对软件的掌控</strong>
纠正：恰恰相反，CLI原生让普通用户彻底摆脱了繁琐的操作门槛，无需学习复杂的软件功能，只用自然语言就能调用软件的全部能力。同时，全链路的CLI调用日志，让AI的每一步操作都可追溯、可审计、可撤销，反而给用户提供了更强的掌控感。</li>
</ol><hr /></section></section>
<section><h2>⚡ 二、底层驱动力：为什么CLI原生是AI时代的必然选择？<a href="#-二底层驱动力为什么cli原生是ai时代的必然选择"><span>#</span></a></h2><p>CLI原生的爆发，绝非厂商的跟风炒作，也不是技术的复古循环，而是由AI时代的技术演进、需求变化、生态成熟共同决定的必然结果，背后有五大不可逆转的底层驱动力。</p><section><h3>🌍 2.1 软件消费主体的结构性迁移：AI正在成为软件的第一调用方<a href="#-21-软件消费主体的结构性迁移ai正在成为软件的第一调用方"><span>#</span></a></h3><p>GUI诞生的核心意义，是适配人类的生理特征：人是视觉动物，对图形、色彩、空间布局的感知效率，远高于纯文本指令。GUI用可视化的方式，把复杂的软件能力封装成普通人能看懂、能操作的按钮，彻底降低了计算机的使用门槛。</p><p>但AI智能体的原生交互逻辑，与人类完全相反：</p><ul>
<li>对AI而言，GUI是反效率的黑盒。AI要操作GUI，必须先用视觉大模型识别界面元素、理解按钮含义、模拟鼠标点击/键盘输入，十几步的GUI操作，不仅延迟高、误差大，还会因为界面更新、弹窗干扰、版本迭代直接失效，技术成本与失败风险极高。</li>
<li>对AI而言，CLI是原生的最优解。一行结构化的CLI指令/API调用，就能直接命中软件的核心能力，零歧义、零误差、毫秒级响应，无需任何视觉识别和模拟操作，执行效率是GUI方式的百倍以上，且完全不受界面迭代的影响。</li>
</ul><p>这种底层逻辑的差异，在产业数据上已经得到了明确的验证。根据Gartner 2026年3月发布的《AI原生软件发展报告》，全球主流SaaS工具的API调用量中，来自AI智能体的占比已突破38%，较2024年增长了3倍，预计2028年将全面超过人类操作量，成为软件的第一消费主体。</p><p><strong>典型案例</strong>：企业协作平台Slack在2026年开发者大会上公布的数据显示，其API调用总量中，来自AI Agent的占比从2024年的12%飙升至2026年的42%，成为平台增长最快的流量来源；而与之对应的，是客户端GUI的人均打开次数下降了18%——用户不再需要打开Slack客户端，就能通过AI助手完成消息回复、日程同步、会议纪要整理等全部操作。</p></section><section><h3>🔄 2.2 软件使用范式的彻底重构：从「人在回路操作」到「人在终点决策」<a href="#-22-软件使用范式的彻底重构从人在回路操作到人在终点决策"><span>#</span></a></h3><p>移动互联网时代，软件的核心使用场景是「人主动打开App，一步步操作完成目标」，每个App都是一个孤立的信息孤岛，GUI是唯一的操作入口，人必须全程在回路中，完成所有的分步操作。</p><p>但AI时代，软件的核心使用场景已经发生了根本性的反转：<strong>人从操作者变成了决策者，AI从辅助工具变成了执行者</strong>。我们只需要用自然语言下达最终目标，AI会自动拆解任务、跨App调用CLI接口、完成全流程的执行，最终只需要向我们反馈结果，全程无需人类打开任何一个App的GUI。</p><p>举一个最直观的例子：你向个人AI助手下达指令「订一张明天杭州到北京的早班国航机票，选靠窗座位，同步到我的日历，给同行的同事发微信通知，再帮我预约明天首都机场的接机专车」。</p><ul>
<li><strong>GUI路径</strong>：你需要手动打开携程App→输入出发地/目的地→选择日期→点击查询→筛选早班国航航班→选择航班→填写乘客信息→选择靠窗座位→提交支付→打开日历App→新建日程→填写航班信息→保存→打开微信→找到同事→发送航班通知→打开滴滴App→预约接机专车→填写地址与时间→确认下单，全流程超过20步，任何一个弹窗、广告、界面更新都会导致任务中断。</li>
<li><strong>CLI路径</strong>：AI直接调用携程、日历、微信、滴滴的标准化CLI/API接口，发送一条结构化的指令，1秒内完成全流程的所有操作，零误差、无干扰、全程可审计。</li>
</ul><p>这种「目标导向」的使用范式，正在全面替代「操作导向」的传统范式。从个人用户的日常事务处理，到企业级的端到端智能工作流，再到行业垂直领域的复杂解决方案，AI正在成为所有软件操作的核心执行者，而适配AI的CLI接口，就是软件接入这个新范式的唯一门票。</p></section><section><h3>🔌 2.3 行业基础设施的全面成熟：标准化协议打通CLI原生的普及路径<a href="#-23-行业基础设施的全面成熟标准化协议打通cli原生的普及路径"><span>#</span></a></h3><p>如果说大模型的Function Call能力，给AI调用CLI提供了技术基础，那么<strong>MCP（模型上下文协议）的爆发，则给CLI原生的全面普及提供了统一的行业基础设施</strong>。</p><p>在MCP协议诞生之前，CLI原生的普及面临一个无法解决的痛点：API碎片化。每个App的API都有自己的私有规范、参数格式、认证方式，AI要对接一个App，就需要单独做定制化开发，成本极高，根本无法规模化普及。这就像互联网诞生之前，每两台计算机之间都需要单独搭建通信线路，无法形成全球互联的网络。</p><p>而MCP协议被称为「AI时代的HTTP协议」，它的核心价值，就是定义了一套全球统一的标准，把所有App、服务、工具的能力，都封装成了AI可以统一理解、一键调用的标准化指令集。App只要开发一个MCP服务，就能被全球所有大模型、AI Agent自动发现、无缝调用，无需任何额外的适配开发。</p><p>截至2026年3月，GitHub上MCP相关项目的数量在6个月内增长了17倍，OpenAI、Anthropic、Google Gemini等全球主流大模型均已原生支持MCP协议，Slack、Notion、GitHub、Zoom、飞书等数百款主流工具，都已经推出了官方MCP服务，优先适配AI Agent调用。</p><p>MCP协议的快速普及，标志着CLI原生已经从零散的厂商行为，变成了全行业的共识与标准，彻底打通了CLI原生规模化普及的最后一公里。</p></section><section><h3>👥 2.4 软件生产与分发逻辑的底层变革：全民开发者时代的到来<a href="#-24-软件生产与分发逻辑的底层变革全民开发者时代的到来"><span>#</span></a></h3><p>移动互联网时代，开发一个合格的App，80%的成本都花在了GUI相关的工作上：UI设计、交互设计、多端适配、客户端开发、兼容性测试、上架审核，一个完整的App需要一个专业团队，花费数月时间、数十万甚至上百万的成本才能完成。这种高门槛，把绝大多数普通人挡在了软件创新的门外。</p><p>而CLI原生的架构，彻底颠覆了软件的生产逻辑：你只需要把核心能力封装成标准化的CLI/API/MCP服务，根本不需要开发GUI。AI会帮你处理和用户的所有交互，用户用自然语言就能使用你的服务，开发成本从几十万降到了几千块，甚至一个没有专业开发背景的普通人，借助AI编程工具，几个小时就能做出一个可用的AI原生应用。</p><p>同时，软件的分发逻辑也发生了根本性的重构。传统GUI软件的分发核心是应用商店，开发者需要经过严格的审核、支付高额的平台抽成，才能触达用户；而CLI原生软件的分发核心是代码仓库与能力注册中心，开发者只需要把项目托管到GitHub上，发布对应的MCP服务，就能被全球的AI Agent自动发现、调用，零审核、零成本、全球触达。</p><p><strong>典型案例</strong>：大量个人开发者开发的垂直领域MCP服务，比如财税计算、法律条款检索、行业数据查询等，仅几百行代码，托管在GitHub上，上线几周就被数万个AI Agent集成，获得了数十万次的调用，通过用量付费模式实现了稳定的商业化收入。这种零成本、快迭代、全球触达的创新模式，在GUI时代是完全无法想象的。</p></section><section><h3>💰 2.5 商业模型的升级与去平台化：打破「苹果税」的枷锁<a href="#-25-商业模型的升级与去平台化打破苹果税的枷锁"><span>#</span></a></h3><p>GUI时代，软件的核心商业模式高度依赖应用商店，无论是付费下载、订阅制还是广告变现，开发者都需要向苹果、安卓等应用商店支付30%左右的「平台税」，同时还要遵守平台严苛的规则，失去了对产品、用户、收入的自主权。</p><p>而CLI原生时代，软件的商业模型发生了根本性的升级，彻底打破了平台的枷锁。核心的商业模式变成了<strong>用量付费（Pay-as-you-go）</strong>：用户（包括AI Agent）调用多少次CLI接口，就支付多少费用，用多少付多少，是一种更高效、更公平、更健康的商业模式。</p><p>同时，开发者可以自由选择支付渠道、定价策略、商业模式，100%掌握自己的用户数据与收入，无需向任何平台支付抽成。这种去平台化的商业模型，极大地释放了开发者的创新活力，也是大量开发者主动拥抱CLI原生转型的核心原因。</p><p><strong>典型案例</strong>：支付巨头Stripe、云通信厂商Twilio，其核心收入的90%以上都来自API调用的用量付费，完全不依赖任何应用商店，无需支付任何平台抽成；开源框架LangChain、向量数据库Pinecone，都是通过CLI/API优先的模式，靠企业级服务与用量付费实现了规模化盈利，成为了AI时代的标杆企业。</p><hr /></section></section>
<section><h2>🔄 三、范式革命：CLI原生正在重构整个软件产业的游戏规则<a href="#-三范式革命cli原生正在重构整个软件产业的游戏规则"><span>#</span></a></h2><p>CLI原生绝不仅仅是软件交互形式的变化，它正在从产品设计、分发获客、市场竞争、技术架构、安全合规等多个维度，彻底重构整个软件产业的游戏规则。</p><section><h3>🎨 3.1 产品设计哲学的颠覆：从UI/UX优先到API/IX优先<a href="#-31-产品设计哲学的颠覆从uiux优先到apiix优先"><span>#</span></a></h3><p>GUI时代，产品设计的核心是UI（用户界面）与UX（用户体验），产品经理的核心工作，是设计符合人类操作习惯的可视化界面，优化交互流程，提升用户的使用体验。一个App的生死，很大程度上由GUI的体验好坏决定。</p><p>而CLI原生时代，产品设计的核心变成了<strong>API设计与IX（Intelligence Experience，智能体验）</strong>。IX指的是面向AI智能体的体验设计，核心目标是让AI能快速、准确、无歧义地理解和调用软件的核心能力，它的重要性已经全面超过了面向人类的UX设计。</p><p>最直观的变化，是产品设计流程的彻底反转：</p><ul>
<li><strong>传统GUI流程</strong>：需求分析→GUI原型设计→视觉设计→前端开发→后端API开发→测试上线</li>
<li><strong>CLI原生流程</strong>：需求分析→核心能力梳理→API/CLI规范设计→后端开发→配套GUI设计→测试上线</li>
</ul><p>这种反转，带来了产品能力模型的全面重构。未来的产品经理，不再只是「交互设计师」，更要成为「能力架构师」，需要掌握API设计、大模型适配、AI生态运营的核心能力，从关注「用户点击了多少个按钮」，转向关注「AI调用了多少次核心能力，完成了多少用户目标」。</p><p><strong>典型案例</strong>：笔记协作平台Notion，是CLI原生转型的标杆企业。其产品迭代策略明确规定：所有新功能必须优先开放API接口，再更新客户端GUI；其官方MCP服务上线3个月，就被超过10万个AI Agent集成，带来了数百万的新增活跃用户，而这些用户甚至不需要打开Notion的客户端，就能通过AI助手完成笔记创作、文档检索、项目管理等全部操作。</p></section><section><h3>📦 3.2 分发与获客逻辑的重构：App Store式微，GitHub成为新的分发中枢<a href="#-32-分发与获客逻辑的重构app-store式微github成为新的分发中枢"><span>#</span></a></h3><p>GUI时代，App Store是软件分发的绝对核心入口，开发者的核心运营工作是ASO（应用商店优化），通过优化关键词、评分、截图，提升在应用商店的排名，获得用户下载。</p><p>而CLI原生时代，软件的分发核心从「给人下载的安装包」，变成了「给AI调用的能力单元」，App Store的入口价值正在快速衰减，<strong>GitHub正在成为AI原生软件的全球核心分发枢纽</strong>。</p><p>这背后的逻辑，与我们之前讨论的「GitHub成为新时代App Store」完全契合：</p><ul>
<li><strong>发现机制</strong>：GitHub的Trending榜单、Topics标签、GitHub SEO，已经成为AI原生应用的核心发现渠道，AI Agent会优先从GitHub上检索、发现可用的能力服务；</li>
<li><strong>信用体系</strong>：GitHub的Star、Fork、Contributor数据，构成了比应用商店评分更硬核的信用背书，高Star的项目天然获得AI与用户的信任，会被优先集成与调用；</li>
<li><strong>分发能力</strong>：GitHub的Releases安装包托管、Packages包管理、Container Registry容器仓库、MCP Registry能力注册中心，实现了全平台、全终端的能力分发，一次发布即可被全球AI Agent触达。</li>
</ul><p>与之对应的，是开发者的核心运营工作，从ASO（应用商店优化）转向了<strong>GSO（GitHub搜索优化）与API SEO</strong>。开发者不再需要花大量精力优化应用商店的截图与关键词，而是要优化GitHub仓库的README、结构化的API文档、大模型友好的接口规范，让自己的CLI服务能被大模型与AI Agent快速、准确地发现与调用。</p><p><strong>典型案例</strong>：现象级本地智能体项目OpenClaw，完全通过GitHub分发，从未在任何应用商店上架，4个月斩获27.9万Star，打破了GitHub历史增长纪录，被全球数百万用户使用。用户无需下载安装包，只需通过GitHub一键部署，就能让AI助手调用其全部能力，成为了CLI原生时代的标杆产品。</p></section><section><h3>🏆 3.3 市场竞争格局的洗牌：从入口壁垒到能力壁垒，超级App的危机与新生<a href="#-33-市场竞争格局的洗牌从入口壁垒到能力壁垒超级app的危机与新生"><span>#</span></a></h3><p>GUI时代，软件市场的竞争核心是<strong>入口壁垒</strong>。超级App通过GUI界面占据用户的手机桌面与使用时长，形成了强大的生态垄断，中小开发者几乎没有弯道超车的机会。微信、抖音等超级App，正是通过这种入口壁垒，构建了自己的商业帝国。</p><p>但CLI原生时代，这种入口壁垒正在快速瓦解。当用户不再需要打开App的GUI，所有操作都通过AI调用CLI接口完成，超级App的桌面入口与用户时长就失去了核心价值——用户不会因为你占据了他的手机桌面，就优先使用你的服务，AI只会选择能力最强、接口最标准、集成最方便的服务。</p><p>CLI原生时代，软件市场的竞争核心变成了<strong>能力壁垒</strong>：你的核心能力是否具有不可替代性？你的CLI接口是否标准化？能否被全球主流的大模型与AI Agent集成？能否融入更多的AI工作流？</p><p>这种竞争逻辑的变化，给超级App带来了前所未有的危机，也带来了全新的机遇，行业已经出现了明显的两极分化：</p><ul>
<li><strong>主动拥抱者</strong>：Slack、Zoom、GitHub等平台，全面开放核心能力的CLI/API接口，积极融入AI生态，反而扩大了使用场景，提升了用户粘性。即使打开客户端的用户数量有所下降，但核心能力的调用量与付费收入却实现了高速增长；</li>
<li><strong>封闭保守者</strong>：大量依赖GUI入口的超级App，担心开放核心能力会失去入口垄断价值，仅开放一些无关紧要的边角料功能的API，核心功能依然封闭在GUI中。这种做法，只会让它们在AI时代逐渐被边缘化，沦为AI无法触及的信息孤岛，最终被用户淘汰。</li>
</ul><p>同时，CLI原生给中小开发者带来了历史性的弯道超车机会。中小开发者无需再和超级App争夺用户时长与桌面入口，只要你在垂直领域的核心能力足够强，接口足够标准化，就能被全球的AI Agent集成，触达数百万甚至数千万的用户，实现对巨头的弯道超车。</p></section><section><h3>🏗️ 3.4 技术架构与开发生态的全面升级：从云原生到AI原生<a href="#️-34-技术架构与开发生态的全面升级从云原生到ai原生"><span>#</span></a></h3><p>CLI原生的转型，正在推动软件技术架构的全面升级，行业正在从云原生时代，进入AI原生时代。</p><p>传统GUI时代的软件架构，是典型的前后端耦合架构：前端（GUI层）负责与用户的交互，后端服务层负责核心业务逻辑，前后端强绑定，前端界面的变化往往需要后端同步修改，迭代效率极低。</p><p>而CLI原生时代的软件架构，是完全解耦的分层架构：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[可选的GUI层] → [标准化CLI/API适配层] → [核心能力层] → [数据层]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在这种架构中，核心能力层是整个软件的核心，所有的业务逻辑都封装在这里；标准化CLI/API适配层，负责把核心能力封装成符合行业标准的接口，供AI与GUI调用；GUI层只是一个可选的配套模块，和核心能力层完全解耦，无论GUI如何迭代，都不会影响核心能力与CLI接口的稳定性。</p><p>这种架构的变化，带来了开发生态的全面升级：</p><ul>
<li><strong>开发重点的转移</strong>：开发者的核心工作，从前端交互开发、多端适配，转向了核心能力优化、API标准化、大模型适配性、容错性设计；</li>
<li><strong>测试体系的重构</strong>：软件测试的核心，从GUI兼容性测试、交互体验测试，转向了API稳定性测试、大模型适配测试、异常场景容错测试、安全合规测试；</li>
<li><strong>开发工具的升级</strong>：Copilot、Cursor等AI编程工具，原生适配CLI/API开发，可以自动生成符合OpenAPI规范的接口代码、测试用例、结构化文档，开发效率提升了数倍。</li>
</ul></section><section><h3>🔐 3.5 安全与合规体系的重构：从终端防护到API安全，从用户授权到智能体权限治理<a href="#-35-安全与合规体系的重构从终端防护到api安全从用户授权到智能体权限治理"><span>#</span></a></h3><p>CLI原生的全面普及，也给软件的安全与合规体系带来了全新的挑战，推动行业从传统的终端防护，转向了API原生的安全治理体系。</p><p>传统GUI时代，安全防护的核心是终端设备与用户账号，只要保障用户的账号密码不泄露、终端设备不被入侵，就能基本保障软件的使用安全。</p><p>而CLI原生时代，AI智能体获得了直接调用软件核心能力的权限，安全防护的核心变成了<strong>API安全与智能体权限治理</strong>，行业面临四大全新的安全挑战：</p><ol>
<li><strong>权限滥用风险</strong>：AI Agent一旦被恶意Prompt误导、或者被黑客劫持，就可能滥用高权限的CLI接口，造成资金转移、数据泄露、系统崩溃等重大损失；</li>
<li><strong>指令注入攻击</strong>：黑客可以通过恶意Prompt，诱导AI生成并调用高危的CLI指令，绕过传统的安全防护体系，实现远程攻击；</li>
<li><strong>数据隐私合规风险</strong>：AI调用CLI接口的过程中，会传输大量的用户敏感数据，很容易出现数据跨境、违规使用、隐私泄露等问题，违反《个人信息保护法》等合规要求；</li>
<li><strong>审计溯源难题</strong>：AI的多步调用、跨平台协同、自动决策，让传统的日志审计体系完全失效，一旦出现安全事件，很难追溯责任主体与攻击路径。</li>
</ol><p>针对这些挑战，全球行业正在快速构建全新的安全与合规体系：</p><ul>
<li>基于OAuth2.0协议升级的<strong>AI Agent细粒度授权框架</strong>，遵循最小权限原则，给AI Agent分配临时的、可撤销的、可审计的权限；</li>
<li><strong>指令安全检测体系</strong>，在CLI接口调用前，对AI生成的指令进行安全检测，拦截恶意指令与高危操作；</li>
<li><strong>全链路审计溯源系统</strong>，记录AI调用CLI接口的全流程日志，包括指令内容、调用时间、操作结果、权限使用情况，实现全程可追溯、可审计；</li>
<li>标准化的安全规范，OWASP发布了针对AI场景的API Security Top 10，NIST也推出了AI风险管理框架，为行业提供了安全建设的标准。</li>
</ul><hr /></section></section>
<section><h2>🛠️ 四、落地实践：企业与开发者如何拥抱CLI原生时代？<a href="#️-四落地实践企业与开发者如何拥抱cli原生时代"><span>#</span></a></h2><p>CLI原生的变革已经到来，对于企业与开发者而言，与其被动等待，不如主动拥抱。我们分别给产品负责人、开发者、企业决策者，提供了可落地的行动指南。</p><section><h3>📋 4.1 给产品负责人的行动指南<a href="#-41-给产品负责人的行动指南"><span>#</span></a></h3><ol>
<li><strong>建立「CLI优先」的产品迭代机制</strong>：将CLI/API设计纳入产品需求的核心环节，所有新功能必须先完成API规范设计与评审，再启动GUI相关的工作，从流程上保障CLI优先的设计原则。</li>
<li><strong>构建IX（智能体验）设计体系</strong>：制定面向AI的接口设计标准，包括参数标准化、错误信息结构化、文档大模型友好、自然语言兼容性，确保AI能快速、准确地理解和调用接口。</li>
<li><strong>核心能力分级开放</strong>：全面梳理产品的核心功能，按照高频、高价值、适合AI自动化的原则，对能力进行分级，优先开放核心场景的完整能力，而非仅开放边角料功能。</li>
<li><strong>建立AI生态运营体系</strong>：组建专门的AI生态运营团队，维护官方MCP服务、优化GitHub开源仓库、对接主流大模型与Agent平台、建立开发者社区，提升产品在AI生态中的集成度。</li>
<li><strong>重构产品成功指标</strong>：在DAU、用户时长等传统指标之外，新增API调用量、AI Agent集成数、开发者生态规模、AI场景带来的收入占比等核心指标，建立适配CLI原生时代的考核体系。</li>
</ol></section><section><h3>💻 4.2 给开发者的落地建议<a href="#-42-给开发者的落地建议"><span>#</span></a></h3><ol>
<li><strong>掌握API优先的开发范式</strong>：深入学习OpenAPI 3.0规范、gRPC、MCP协议等标准化技术，建立CLI原生的开发思维，养成「先设计接口，再开发功能」的开发习惯。</li>
<li><strong>优化接口的大模型适配性</strong>：编写结构化、语义清晰的API文档，完善参数注释与调用示例，设计友好的错误处理机制，支持自然语言参数解析，让大模型能准确理解接口的能力与使用方式。</li>
<li><strong>构建解耦的技术架构</strong>：将核心业务逻辑与GUI层完全解耦，确保所有GUI能实现的功能，都能通过CLI/API实现，无功能差异、无权限限制，保证接口的完整性。</li>
<li><strong>拥抱开源分发模式</strong>：将GitHub作为核心分发渠道，优化仓库的README、文档、示例代码，提升GitHub搜索排名，完善开源协作机制，吸引更多开发者与AI Agent集成。</li>
<li><strong>建立配套的测试与监控体系</strong>：针对API接口设计全面的自动化测试用例，覆盖正常场景、异常场景、边界场景；建立接口调用量、错误率、延迟、可用性的实时监控体系，保障接口的稳定性。</li>
</ol></section><section><h3>🏢 4.3 给企业决策者的战略布局<a href="#-43-给企业决策者的战略布局"><span>#</span></a></h3><ol>
<li><strong>制定AI原生的技术战略</strong>：将CLI/API开放能力纳入企业的核心技术战略，设定明确的转型目标与时间表，自上而下推动CLI原生的全面转型，避免部门墙与路径依赖。</li>
<li><strong>重构安全与合规体系</strong>：组建专门的AI安全团队，建立针对AI Agent调用的权限治理、安全检测、审计溯源、风险防控体系，满足数据隐私合规要求，提前规避安全风险。</li>
<li><strong>设计适配CLI原生的商业模型</strong>：组织产品、财务、销售团队，探索面向AI调用的用量付费模式、分级订阅模式，平衡免费调用的成本与商业化收益，实现商业闭环。</li>
<li><strong>布局行业标准与生态</strong>：积极加入MCP等行业标准组织，参与行业规范的制定，对接主流大模型厂商与Agent平台，抢占AI时代的新流量入口，建立生态壁垒。</li>
<li><strong>组织能力升级</strong>：通过培训、人才引进，培养团队的AI原生思维，建立跨产品、开发、安全、运营的CLI原生转型专项团队，保障转型的顺利推进。</li>
</ol><hr /></section></section>
<section><h2>🔮 五、挑战与未来展望：CLI原生时代的终局是什么？<a href="#-五挑战与未来展望cli原生时代的终局是什么"><span>#</span></a></h2><section><h3>⚠️ 5.1 无法回避的核心挑战<a href="#️-51-无法回避的核心挑战"><span>#</span></a></h3><p>尽管CLI原生已经成为行业共识，但它的全面普及，依然面临五大无法回避的核心挑战：</p><ol>
<li><strong>超级App的封闭性壁垒</strong>：入口价值与开放生态的核心矛盾，仍是CLI原生普及的最大阻力。大量超级App依然固守封闭的GUI生态，不愿开放核心能力，这会严重阻碍行业的整体转型。</li>
<li><strong>标准化与碎片化的矛盾</strong>：尽管MCP协议正在快速普及，但行业依然存在大量的私有协议、非标准化接口，不同大模型的适配标准也不统一，碎片化问题依然存在，增加了AI集成的成本。</li>
<li><strong>安全与权限的系统性风险</strong>：针对AI Agent的安全攻击手段正在快速升级，而行业尚未形成成熟、统一的安全防护体系，一旦出现大规模的安全事件，会严重影响用户对AI自动化的信任，延缓CLI原生的普及速度。</li>
<li><strong>用户的信任与掌控感缺失</strong>：普通用户对AI黑盒式的操作存在天然的顾虑，担心AI会误操作、泄露隐私、滥用权限。如何平衡自动化效率与用户的掌控感，是CLI原生向全民普及的关键。</li>
<li><strong>商业模式的转型阵痛</strong>：大量依赖广告、订阅模式的传统软件企业，难以快速转型到用量付费模式，免费API调用带来的带宽、算力成本压力巨大，很多企业在转型过程中会面临收入下滑的阵痛。</li>
</ol></section><section><h3>🚀 5.2 未来趋势预判<a href="#-52-未来趋势预判"><span>#</span></a></h3><p>尽管存在挑战，但CLI原生的趋势不可逆转。我们可以清晰地预判出未来的发展路径：</p><section><h4>短期（1-2年，2026-2027）：基础设施普及期<a href="#短期1-2年2026-2027基础设施普及期"><span>#</span></a></h4><ul>
<li>MCP等标准化协议成为行业事实标准，90%以上的主流SaaS工具将推出官方MCP服务，完成CLI原生的基础改造；</li>
<li>GitHub正式推出官方MCP Registry，成为全球最大的AI能力分发市场，彻底奠定「新时代App Store」的地位；</li>
<li>面向AI Agent的API安全与权限治理体系初步形成，行业标准与最佳实践开始建立；</li>
<li>大量中小开发者涌入CLI原生赛道，涌现出海量垂直领域的AI原生应用，行业创新迎来爆发期。</li>
</ul></section><section><h4>中期（3-5年，2028-2030）：范式全面落地期<a href="#中期3-5年2028-2030范式全面落地期"><span>#</span></a></h4><ul>
<li>CLI原生成为软件设计的默认范式，所有新发布的软件都将遵循「CLI优先」的设计原则，GUI成为可选的配套界面；</li>
<li>AI Agent的API调用量全面超过人类操作量，成为软件的第一消费主体；</li>
<li>全球统一的AI能力交互标准正式确立，彻底解决API碎片化问题，实现「一次开发，全生态调用」；</li>
<li>软件市场格局彻底洗牌，封闭的超级App要么全面开放融入生态，要么被边缘化，一批AI原生的新巨头崛起。</li>
</ul></section><section><h4>长期（5年以上）：生态终局期<a href="#长期5年以上生态终局期"><span>#</span></a></h4><ul>
<li>软件产业彻底完成AI原生重构，形成一张由标准化CLI接口串联的全球能力网络，AI成为这个网络的核心调度者；</li>
<li>人类彻底从繁琐的软件操作中解放出来，与软件的交互完全回归自然语言，只需要下达目标和做最终决策，所有执行环节都由AI自动完成；</li>
<li>软件的生产、分发、消费全流程都被AI重构，全民开发者成为常态，每个人都能通过自然语言定义自己需要的软件能力，由AI自动生成对应的CLI服务；</li>
<li>数字经济的底层逻辑彻底改变，软件的价值不再由用户时长决定，而是由其在AI能力网络中的不可替代性决定。</li>
</ul><hr /></section></section></section>
<section><h2>🎯 结尾：拥抱变革，而非抗拒<a href="#-结尾拥抱变革而非抗拒"><span>#</span></a></h2><p>从GUI到CLI原生的转向，从来不是技术的复古，而是软件产业在AI时代的一次根本性跃迁。它的本质，是软件的服务对象从「单一的人类」，扩展到了「人与AI智能体共生的双主体」，是数字世界从「人类适配机器」到「机器适配人类」的终极进化。</p><p>这场变革正在席卷整个软件产业，没有任何企业和开发者能够置身事外。对于我们而言，唯一的选择，就是拥抱这个CLI原生的新时代，主动重构自己的产品思维、技术架构与商业模型，在AI时代的新赛道上，抓住属于自己的历史性机遇。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/xutopia77-projects/</id>
      <title type="text">🚀 xutopia77的开源项目</title>
      <published>2025-12-11T00:00:00.000Z</published>
      <updated>2025-12-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/xutopia77-projects/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>这里展示了我开发的开源项目，每个项目都源于实际需求，追求简洁实用。欢迎Star、Fork和反馈！</p>

<hr />
<section><h2>📹 一、StreamSeek - 视频流分析与录像管理系统<a href="#-一streamseek---视频流分析与录像管理系统"><span>#</span></a></h2><section><h3>📖 项目简介<a href="#-项目简介"><span>#</span></a></h3><p>StreamSeek 是一款专为安防监控录像文件管理而设计的桌面应用程序，提供视频播放控制、分片编辑、文件管理、标签分类、缩略图生成等核心功能。特别适合管理小米摄像头等智能设备的录像文件。</p></section><section><h3>✨ 核心功能<a href="#-核心功能"><span>#</span></a></h3>

<table><thead><tr><th>功能</th><th>说明</th></tr></thead><tbody><tr><td>视频播放控制</td><td>支持标准播放、倍速播放（0.5x-4x）、逐帧查看、进度跳转</td></tr><tr><td>视频分片编辑</td><td>精确到帧的视频分片功能，支持片段拆分、删除、恢复、标记</td></tr><tr><td>项目管理</td><td>支持文件管理项目和剪辑编辑项目，自动保存工作状态</td></tr><tr><td>文件管理</td><td>自动扫描入库、多条件检索、回收站机制、批量操作</td></tr><tr><td>标签管理</td><td>灵活的标签分类系统，按标签快速筛选</td></tr><tr><td>缩略图管理</td><td>自动生成视频缩略图，支持缩略图视图快速浏览</td></tr></tbody></table></section><section><h3>🔗 相关链接<a href="#-相关链接"><span>#</span></a></h3>

<table><thead><tr><th>类型</th><th>地址</th></tr></thead><tbody><tr><td>官网网站</td><td><a href="https://xutopia.top/project/stream-seek">StreamSeek - 视频流分析与录像管理系统</a></td></tr><tr><td>GitHub仓库</td><td><a href="https://github.com/xutopia77/stream-seek" target="_blank">https://github.com/xutopia77/stream-seek</a></td></tr><tr><td>软件下载</td><td><a href="https://github.com/xutopia77/stream-seek/releases/download/latest/streamseek-latest-win.zip" target="_blank">Windows版本</a></td></tr></tbody></table><hr /></section></section>
<section><h2>🖼️ 二、Tidy Time - 图片与视频管理工具<a href="#️-二tidy-time---图片与视频管理工具"><span>#</span></a></h2><section><h3>📖 项目简介<a href="#-项目简介-1"><span>#</span></a></h3><p>Tidy Time 是一款专业的图片与视频管理工具，旨在帮助用户高效地组织和浏览多媒体文件。</p></section><section><h3>💡 核心理念<a href="#-核心理念"><span>#</span></a></h3><p><strong>非侵入式设计</strong>是 Tidy Time 的核心理念。除非用户主动执行删除、复制或移动操作，否则系统不会对用户的原始文件数据进行任何修改。所有元数据、缩略图、指纹等信息均独立存储在项目目录中，确保原始文件的完整性和安全性。</p></section><section><h3>🎯 产品定位<a href="#-产品定位"><span>#</span></a></h3>

<table><thead><tr><th>特性</th><th>说明</th></tr></thead><tbody><tr><td>轻量便携</td><td>无需安装配置，即开即用，删除即卸载</td></tr><tr><td>多端支持</td><td>同时支持 Web 端和桌面端访问</td></tr><tr><td>安全可靠</td><td>零修改原则，保障用户数据安全</td></tr><tr><td>高效便捷</td><td>智能搜索、批量操作、实时更新</td></tr></tbody></table></section><section><h3>📋 适用场景<a href="#-适用场景"><span>#</span></a></h3><ul>
<li><strong>摄影爱好者</strong>：管理大量照片，按标签分类，快速查找</li>
<li><strong>视频创作者</strong>：组织视频素材，管理不同版本</li>
<li><strong>数据归档</strong>：建立个人数字资产库，便于长期管理</li>
<li><strong>团队协作</strong>：共享文件库，统一管理资源</li>
</ul></section><section><h3>🔗 相关链接<a href="#-相关链接-1"><span>#</span></a></h3>

<table><thead><tr><th>类型</th><th>地址</th></tr></thead><tbody><tr><td>官网网站</td><td><a href="https://xutopia.top/project/tidy-time">Tidy Time - Intelligent Media File Management System</a></td></tr><tr><td>GitHub仓库</td><td><a href="https://github.com/xutopia77/time_capsule" target="_blank">https://github.com/xutopia77/time_capsule</a></td></tr><tr><td>软件下载</td><td><a href="https://github.com/xutopia77/time_capsule/releases/download/latest/time_capsule-latest-win.zip" target="_blank">Windows版本</a></td></tr></tbody></table><hr /></section></section>
<section><h2>📌 总结<a href="#-总结"><span>#</span></a></h2><p>以上项目均为开源项目，欢迎：</p><ul>
<li>⭐ Star 支持开发者</li>
<li>🍴 Fork 参与贡献</li>
<li>🐛 Issue 反馈问题</li>
<li>💬 Discussion 交流想法</li>
</ul><p>更多项目持续开发中，敬请关注 <a href="https://github.com/xutopia77" target="_blank">GitHub主页</a>！</p></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/hexo-seo-plugins-guide/</id>
      <title type="text">🔌 Hexo博客SEO插件完整指南：提升搜索排名必备工具</title>
      <published>2025-12-06T00:00:00.000Z</published>
      <updated>2025-12-06T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/hexo-seo-plugins-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>Hexo博客搭建完成后，如何让更多人通过搜索引擎找到你的文章？答案就是SEO优化。</p>
<p>本文详细介绍5款Hexo必备SEO插件，从站点地图生成、外链权重保护、资源压缩到相关文章推荐，全方位提升博客在搜索引擎中的表现，帮助你获取<strong>精准、免费、持续的搜索流量</strong>。详细效果见个人主页<a href="https://xutopia.top">xutopia77 - 见字如面</a></p>
<section><h3>1. <code>hexo-generator-sitemap</code>（全站 XML 站点地图）<a href="#1-hexo-generator-sitemap全站-xml-站点地图"><span>#</span></a></h3><section><h4>作用详解<a href="#作用详解"><span>#</span></a></h4><p>生成符合 <a>Google</a>、<a>Bing</a> 等通用搜索引擎标准的 XML 格式站点地图（<code>sitemap.xml</code>），包含：</p><ul>
<li>所有页面 URL</li>
<li>最后更新时间（<code>lastmod</code>）</li>
<li>更新频率（<code>changefreq</code>）</li>
<li>优先级（<code>priority</code>）</li>
</ul><p>帮助搜索引擎爬虫更高效地发现和抓取全站页面，提升收录效率。</p></section><section><h4>安装与配置<a href="#安装与配置"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-generator-sitemap</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在博客根目录的 <code>_config.yml</code> 中添加配置（可选）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sitemap</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>path</span><span>: </span><span>sitemap.xml</span><span>          </span><span># 生成的 sitemap 文件路径</span></div></div><div><div><div>3</div></div><div><span>  </span><span>rel</span><span>: </span><span>false</span><span>                  </span><span># 是否在 HTML head 中添加 rel="sitemap" 链接</span></div></div><div><div><div>4</div></div><div><span>  </span><span>tags</span><span>: </span><span>true</span><span>                  </span><span># 是否包含标签页</span></div></div><div><div><div>5</div></div><div><span>  </span><span>categories</span><span>: </span><span>true</span><span>            </span><span># 是否包含分类页</span></div></div><div><div><div>6</div></div><div><span>  </span><span>exclude</span><span>:                    </span><span># 排除的页面路径（支持正则）</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>- </span><span>/admin/</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>- </span><span>/private/</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>使用方法<a href="#使用方法"><span>#</span></a></h4><ol>
<li>执行 <code>hexo generate</code>，会在 <code>public/</code> 目录下生成 <code>sitemap.xml</code>。</li>
<li>部署博客后，将 <code>https://你的域名/sitemap.xml</code> 提交到对应搜索引擎的站长平台。</li>
</ol></section></section>
<section><h3>2. <code>hexo-generator-baidu-sitemap</code>（百度专属站点地图）<a href="#2-hexo-generator-baidu-sitemap百度专属站点地图"><span>#</span></a></h3><section><h4>作用详解<a href="#作用详解-1"><span>#</span></a></h4><p>针对<a>百度搜索资源平台</a>的抓取规则，生成适配百度的 XML 或 TXT 格式站点地图（<code>baidusitemap.xml</code>）。<br />
与通用 sitemap 的区别：</p><ul>
<li>更严格的 URL 格式校验（如不支持中文 URL，需转码）</li>
<li>可选生成 TXT 格式（仅包含 URL 列表）</li>
<li>对百度爬虫的友好度更高</li>
</ul></section><section><h4>安装与配置<a href="#安装与配置-1"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-generator-baidu-sitemap</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>_config.yml</code> 中添加配置（可选）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>baidusitemap</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>path</span><span>: </span><span>baidusitemap.xml</span><span>      </span><span># 生成的文件路径（也可设为 .txt）</span></div></div><div><div><div>3</div></div><div><span>  </span><span>tags</span><span>: </span><span>true</span><span>                  </span><span># 包含标签页</span></div></div><div><div><div>4</div></div><div><span>  </span><span>categories</span><span>: </span><span>true</span><span>            </span><span># 包含分类页</span></div></div><div><div><div>5</div></div><div><span>  </span><span>exclude</span><span>:                    </span><span># 排除路径</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>- </span><span>/admin/</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>使用方法<a href="#使用方法-1"><span>#</span></a></h4><ol>
<li>执行 <code>hexo generate</code> 生成 <code>baidusitemap.xml</code>。</li>
<li>部署后将文件提交到<a>百度搜索资源平台</a>。</li>
</ol></section></section>
<section><h3>3. <code>hexo-autonofollow</code>（自动添加 nofollow 标签）<a href="#3-hexo-autonofollow自动添加-nofollow-标签"><span>#</span></a></h3><section><h4>作用详解<a href="#作用详解-2"><span>#</span></a></h4><p>自动检测文章中的<strong>外部链接</strong>（非本站域名），并添加 <code>rel="nofollow noreferrer"</code> 属性：</p><ul>
<li><code>nofollow</code>：告诉搜索引擎不要传递权重到该链接（避免 SEO 权重流失）。</li>
<li><code>noreferrer</code>：隐藏来源页信息，提升安全性（防止外部站点获取你的博客地址）。</li>
</ul><p>无需手动修改每个链接，适合经常引用外部资源的博客。</p></section><section><h4>安装与配置<a href="#安装与配置-2"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-autonofollow</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>_config.yml</code> 中添加配置（可选）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nofollow</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>enable</span><span>: </span><span>true</span><span>                </span><span># 启用插件</span></div></div><div><div><div>3</div></div><div><span>  </span><span>field</span><span>: </span><span>post</span><span>                 </span><span># 应用范围（post: 仅文章，page: 仅页面，all: 全部）</span></div></div><div><div><div>4</div></div><div><span>  </span><span>exclude</span><span>:                    </span><span># 排除的域名（这些域名的链接不加 nofollow）</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>- </span><span>github.com</span><span>              </span><span># 可添加友链或信任的站点</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>- </span><span>your-friend-site.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>使用方法<a href="#使用方法-2"><span>#</span></a></h4><ul>
<li>直接执行 <code>hexo generate</code>，插件会自动处理所有外部链接。</li>
<li>可通过查看页面源码，确认外部链接是否已添加 <code>rel</code> 属性。</li>
</ul></section></section>
<section><h3>4. <code>hexo-filter-cleanup</code>（自动压缩静态资源）<a href="#4-hexo-filter-cleanup自动压缩静态资源"><span>#</span></a></h3><section><h4>作用详解<a href="#作用详解-3"><span>#</span></a></h4><p>通过过滤器（Filter）在生成静态文件时，自动压缩：</p><ul>
<li><strong>HTML</strong>：移除注释、多余空格、换行符。</li>
<li><strong>CSS</strong>：合并空白字符、移除注释。</li>
<li><strong>JS</strong>：压缩代码（基于 <code>uglify-js</code>）。</li>
</ul><p>可显著减少文件体积（通常压缩率 30%~60%），提升页面加载速度。</p></section><section><h4>安装与配置<a href="#安装与配置-3"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-filter-cleanup</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>_config.yml</code> 中添加详细配置（可选）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>filter_cleanup</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>enable</span><span>: </span><span>true</span></div></div><div><div><div>3</div></div><div><span>  </span><span>html</span><span>:</span></div></div><div><div><div>4</div></div><div><span>    </span><span>enable</span><span>: </span><span>true</span></div></div><div><div><div>5</div></div><div><span>    </span><span>exclude</span><span>:                  </span><span># 排除的 HTML 文件</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>- </span><span>/admin/</span></div></div><div><div><div>7</div></div><div><span>    </span><span>options</span><span>:</span></div></div><div><div><div>8</div></div><div><span>      </span><span>removeComments</span><span>: </span><span>true</span><span>    </span><span># 移除注释</span></div></div><div><div><div>9</div></div><div><span>      </span><span>collapseWhitespace</span><span>: </span><span>true# 压缩空白</span></div></div><div><div><div>10</div></div><div><span>  </span><span>css</span><span>:</span></div></div><div><div><div>11</div></div><div><span>    </span><span>enable</span><span>: </span><span>true</span></div></div><div><div><div>12</div></div><div><span>    </span><span>exclude</span><span>:</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>- </span><span>/custom.css</span></div></div><div><div><div>14</div></div><div><span>  </span><span>js</span><span>:</span></div></div><div><div><div>15</div></div><div><span>    </span><span>enable</span><span>: </span><span>true</span></div></div><div><div><div>16</div></div><div><span>    </span><span>exclude</span><span>:</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>- </span><span>/libs/</span><span>                </span><span># 排除第三方库（避免压缩出错）</span></div></div><div><div><div>18</div></div><div><span>    </span><span>options</span><span>:</span></div></div><div><div><div>19</div></div><div><span>      </span><span>compress</span><span>: </span><span>true</span></div></div><div><div><div>20</div></div><div><span>      </span><span>mangle</span><span>: </span><span>true</span><span>            </span><span># 混淆变量名</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>注意事项<a href="#注意事项"><span>#</span></a></h4><ul>
<li>若压缩后页面功能异常，可通过 <code>exclude</code> 排除特定文件（如第三方 JS 库）。</li>
<li>部分主题已内置压缩功能，需避免重复使用（可能冲突）。</li>
</ul></section></section>
<section><h3>5. <code>hexo-related-popular-posts</code>（相关/热门文章推荐）<a href="#5-hexo-related-popular-posts相关热门文章推荐"><span>#</span></a></h3><section><h4>作用详解<a href="#作用详解-4"><span>#</span></a></h4><p>基于以下维度生成文章推荐列表，提升内链结构和用户停留时间：</p><ul>
<li><strong>相关文章</strong>：通过标签、分类、内容相似度计算（支持 TF-IDF 算法）。</li>
<li><strong>热门文章</strong>：基于浏览量（需配合统计插件，如 <code>hexo-wordcount</code> 或主题自带统计）。</li>
</ul><p>可自定义显示位置（如文章底部、侧边栏），样式适配主题。</p></section><section><h4>安装与配置<a href="#安装与配置-4"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-related-popular-posts</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在 <code>_config.yml</code> 中添加配置（可选）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>related_popular_posts</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>enable</span><span>: </span><span>true</span></div></div><div><div><div>3</div></div><div><span>  </span><span>relatedCount</span><span>: </span><span>5</span><span>             </span><span># 相关文章显示数量</span></div></div><div><div><div>4</div></div><div><span>  </span><span>popularCount</span><span>: </span><span>3</span><span>             </span><span># 热门文章显示数量</span></div></div><div><div><div>5</div></div><div><span>  </span><span>mixRelatedPopular</span><span>: </span><span>true</span><span>     </span><span># 混合显示相关+热门</span></div></div><div><div><div>6</div></div><div><span>  </span><span>PPMixType</span><span>: </span><span>'related'</span><span>        </span><span># 优先类型（related: 相关优先，popular: 热门优先）</span></div></div><div><div><div>7</div></div><div><span>  </span><span>tagsWeight</span><span>: </span><span>2</span><span>               </span><span># 标签相似度权重</span></div></div><div><div><div>8</div></div><div><span>  </span><span>categoriesWeight</span><span>: </span><span>1</span><span>         </span><span># 分类相似度权重</span></div></div><div><div><div>9</div></div><div><span>  </span><span>dateWeight</span><span>: </span><span>1</span><span>               </span><span># 时间权重（越新的文章权重越高）</span></div></div><div><div><div>10</div></div><div><span>  </span><span>showDate</span><span>: </span><span>false</span><span>             </span><span># 是否显示文章日期</span></div></div><div><div><div>11</div></div><div><span>  </span><span>showThumbnail</span><span>: </span><span>false</span><span>        </span><span># 是否显示缩略图（需主题支持）</span></div></div><div><div><div>12</div></div><div><span>  </span><span>thumbnailPath</span><span>: </span><span>'thumbnail'</span><span>  </span><span># 缩略图 Front-matter 字段名</span></div></div><div><div><div>13</div></div><div><span>  </span><span>excludeClass</span><span>: </span><span>'no-related'</span><span>  </span><span># 排除特定类别的文章</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>使用方法（关键步骤）<a href="#使用方法关键步骤"><span>#</span></a></h4><p>需在<strong>主题模板文件</strong>中手动添加调用代码，以显示推荐列表：</p><ol>
<li>找到主题的文章模板文件（通常是 <code>themes/你的主题/layout/post.ejs</code> 或 <code>post.njk</code>）。</li>
<li>在文章内容下方（如 <code>&lt;/article&gt;</code> 之前）添加：
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;%- related_popular_posts(page, {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>maxCount: 5,</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>ulClass: 'related-posts',</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>liClass: 'related-post-item',</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>linkClass: 'related-post-link'</span></div></div><div><div><div>6</div></div><div><span>}) %&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>根据主题样式，自定义 CSS 类（如 <code>.related-posts</code>）美化显示效果。</li>
</ol></section><section><h4>进阶技巧<a href="#进阶技巧"><span>#</span></a></h4><ul>
<li>若主题支持缩略图，可在文章 Front-matter 中添加 <code>thumbnail: /images/xxx.jpg</code>，并在配置中开启 <code>showThumbnail: true</code>。</li>
<li>可通过 <code>hexo clean &amp;&amp; hexo generate</code> 重新生成，确保推荐列表更新。</li>
</ul></section></section>
<section><h3>通用注意事项<a href="#通用注意事项"><span>#</span></a></h3><ol>
<li><strong>插件冲突</strong>：若安装后博客生成报错，可通过 <code>npm uninstall &lt;插件名&gt;</code> 排查，或检查 Node.js 版本（建议 v14+）。</li>
<li><strong>性能平衡</strong>：<code>hexo-filter-cleanup</code> 和 <code>hexo-related-popular-posts</code> 会增加生成时间，若博客文章量大（&gt;1000 篇），可酌情调整配置。</li>
<li><strong>主题兼容性</strong>：部分主题已内置类似功能（如压缩、相关文章），建议先检查主题文档，避免重复安装。</li>
</ol><p>通过以上插件的组合使用，可全面提升 Hexo 博客的 SEO 表现、加载速度和用户粘性！详细效果见个人主页<a href="https://xutopia.top">xutopia77 - 见字如面</a></p></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/seo-complete-guide-for-hexo-blog/</id>
      <title type="text">🔍 SEO超全详解：从基础原理到Hexo博客落地全指南</title>
      <published>2025-12-06T00:00:00.000Z</published>
      <updated>2025-12-06T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/seo-complete-guide-for-hexo-blog/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>🔍 SEO 超全详解：从基础原理到Hexo博客落地全指南<a href="#-seo-超全详解从基础原理到hexo博客落地全指南"><span>#</span></a></h1><p>SEO 全称 <strong>Search Engine Optimization</strong>，中文译为<strong>搜索引擎优化</strong>。
它的核心本质是：<strong>在不付费的前提下，通过优化网站/页面的内容、结构、外链等维度，适配搜索引擎的排名规则，提升页面在自然搜索结果中的排名，从而获取精准、免费、持续的搜索流量</strong>。</p><p>对于你的Hexo个人博客而言，SEO 就是让你写的教程、笔记，在用户百度/Google搜索相关问题时，能排在搜索结果前列，让更多人看到你的内容，而不是写完之后只有自己能访问。详细效果见个人主页<a href="https://xutopia.top/blog">xutopia77 - 见字如面</a></p><hr /><section><h2>🧠 一、先搞懂核心：SEO 的底层逻辑与价值<a href="#-一先搞懂核心seo-的底层逻辑与价值"><span>#</span></a></h2><section><h3>⚖️ 1. 先分清：SEO vs SEM（90%新手都会混淆）<a href="#️-1-先分清seo-vs-sem90新手都会混淆"><span>#</span></a></h3><p>搜索引擎的结果分为两类，对应两种运营方式：</p>

<table><thead><tr><th>类型</th><th>全称</th><th>核心特点</th><th>适用场景</th></tr></thead><tbody><tr><td>SEO（自然搜索）</td><td>搜索引擎优化</td><td>免费、长期生效、排名稳定、信任度高、见效慢</td><td>个人博客、品牌站、长期内容运营</td></tr><tr><td>SEM（付费搜索）</td><td>搜索引擎营销</td><td>付费、按点击扣费、见效快、停止付费流量立即消失</td><td>商业推广、短期获客、活动营销</td></tr></tbody></table><p>简单说：SEO 是靠优化「免费占搜索坑位」，SEM 是花钱买广告位。你的个人博客，核心要做的就是SEO。</p></section><section><h3>💎 2. 个人博客做SEO的核心价值<a href="#-2-个人博客做seo的核心价值"><span>#</span></a></h3><ul>
<li><strong>精准免费流量</strong>：用户搜索「Hexo Butterfly头像更换」这类问题找到你的文章，都是精准受众，且完全免费，一次优化长期有流量。</li>
<li><strong>信任背书与个人品牌</strong>：搜索结果前排的内容，天然会被用户赋予更高的信任度，长期输出优质内容能打造你的个人IP。</li>
<li><strong>被动持续收益</strong>：一篇优质文章，只要不删除，会持续被搜索引擎收录、排名，几年后都能持续给你带来流量，不用反复运营。</li>
<li><strong>内容价值最大化</strong>：你写的笔记、踩坑记录，不再只是自己的备忘，而是能帮到全网有同样需求的人，实现内容的价值放大。</li>
</ul></section><section><h3>⚙️ 3. 搜索引擎的核心工作原理<a href="#️-3-搜索引擎的核心工作原理"><span>#</span></a></h3><p>所有SEO优化，都是围绕搜索引擎的工作逻辑展开的。搜索引擎（百度/Google/必应）的工作分为3个核心步骤，环环相扣：</p><section><h4>🕷️ ① 抓取（Crawling，也叫「爬行」）<a href="#️--抓取crawling也叫爬行"><span>#</span></a></h4><p>搜索引擎会派出大量的<strong>爬虫（Spider，也叫蜘蛛）</strong>，在全网的网页之间顺着链接爬行，访问每一个能找到的页面，下载页面的HTML、图片、文本等内容。</p><ul>
<li>爬虫能找到你的页面的前提：有其他页面链接到你的页面，或者你主动把页面地址提交给搜索引擎。</li>
<li>抓取门槛：页面必须是静态可访问的、没有禁止爬虫抓取、加载速度正常，Hexo生成的纯静态页面，天生对爬虫友好。</li>
</ul></section><section><h4>📚 ② 索引（Indexing）<a href="#--索引indexing"><span>#</span></a></h4><p>爬虫抓取到页面内容后，搜索引擎会对页面内容进行解析、去重、分词、分类，然后存入<strong>索引库</strong>。</p><ul>
<li>只有被「索引」的页面，才有可能出现在用户的搜索结果中；没被收录的页面，用户搜任何关键词都找不到。</li>
<li>索引的核心要求：内容原创、主题明确、结构清晰，没有违规内容，重复、采集、低质内容不会被索引。</li>
</ul></section><section><h4>🏆 ③ 排名（Ranking）<a href="#--排名ranking"><span>#</span></a></h4><p>当用户输入关键词搜索时，搜索引擎会从索引库中，筛选出所有和该关键词相关的页面，然后通过<strong>几百项排名算法规则</strong>，给页面打分，按分数从高到低排序，呈现给用户。</p><ul>
<li>排名的核心原则：<strong>给用户提供最匹配、最优质、最能解决问题的内容</strong>。所有搜索引擎的算法迭代，都是围绕这个核心目标，这也是SEO的底层逻辑——不是欺骗搜索引擎，而是帮搜索引擎更好地识别你的内容价值。</li>
</ul><hr /></section></section></section><section><h2>📂 二、SEO 的核心分类与合规边界<a href="#-二seo-的核心分类与合规边界"><span>#</span></a></h2><section><h3>🌐 1. 按优化范围分类：站内SEO + 站外SEO<a href="#-1-按优化范围分类站内seo--站外seo"><span>#</span></a></h3><p>这是SEO最核心的分类，也是你实操的核心框架：</p>

<table><thead><tr><th>分类</th><th>核心定义</th><th>优化重点</th><th>权重占比</th></tr></thead><tbody><tr><td>站内SEO（On-Page SEO）</td><td>对自己网站内部的所有优化</td><td>内容质量、关键词布局、网站结构、HTML标签、URL优化、页面速度、内链体系</td><td>70%+（个人博客核心优化方向）</td></tr><tr><td>站外SEO（Off-Page SEO）</td><td>网站外部的所有优化</td><td>反向链接（外链）、品牌曝光、友情链接、社交媒体引流</td><td>30%（辅助提升信任度）</td></tr></tbody></table></section><section><h3>🎭 2. 按合规性分类：白帽SEO vs 黑帽SEO vs 灰帽SEO<a href="#-2-按合规性分类白帽seo-vs-黑帽seo-vs-灰帽seo"><span>#</span></a></h3><p>这是SEO的合规边界，个人博客<strong>必须只做白帽SEO</strong>，否则会面临网站被搜索引擎彻底拉黑的风险：</p><ul>
<li><strong>白帽SEO</strong>：完全遵循搜索引擎的规则，通过优质内容、合理优化、正规外链提升排名，是唯一长期安全、稳定的SEO方式。</li>
<li><strong>黑帽SEO</strong>：通过作弊手段欺骗搜索引擎，快速获取排名，比如关键词堆砌、隐藏文字、刷流量、买外链、采集伪原创内容。风险极高，一旦被算法识别，网站会被「K站」（从搜索引擎索引库中彻底删除，再也搜不到）。</li>
<li><strong>灰帽SEO</strong>：介于白帽和黑帽之间的擦边操作，短期可能有效果，长期有被降权、K站的风险，个人博客绝对不要尝试。</li>
</ul><hr /></section></section><section><h2>🛠️ 三、站内SEO 核心优化全维度（个人博客必做）<a href="#️-三站内seo-核心优化全维度个人博客必做"><span>#</span></a></h2><p>站内SEO是SEO的基础，也是个人博客最容易落地、见效的部分，分为6大核心模块，每一个都有可直接套用的实操方法。</p><section><h3>🔑 1. 关键词研究与布局：SEO的起点<a href="#-1-关键词研究与布局seo的起点"><span>#</span></a></h3><p>关键词，就是用户在搜索引擎里输入的搜索词。SEO的本质，就是让你的页面，在用户搜索对应的关键词时，能排在前列。
没有关键词布局，你的内容再好，用户也搜不到。</p><section><h4>📊 ① 关键词的3个核心分类（个人博客适配版）<a href="#--关键词的3个核心分类个人博客适配版"><span>#</span></a></h4>

<table><thead><tr><th>分类</th><th>定义</th><th>示例</th><th>特点</th><th>布局位置</th></tr></thead><tbody><tr><td>核心关键词</td><td>网站的核心主题，2-4个词</td><td>Hexo教程、个人博客搭建</td><td>搜索量大、竞争大、精准度中等</td><td>网站首页标题、描述、导航栏</td></tr><tr><td>长尾关键词</td><td>核心词延伸的精准短句，3个词以上</td><td>Hexo非全局安装教程、Butterfly主题头像更换</td><td>搜索量小、竞争极小、精准度极高、转化率高</td><td>文章页标题、正文、H标签</td></tr><tr><td>疑问词关键词</td><td>用户的问题类搜索词</td><td>Hexo博客图片不显示怎么办、SEO是什么</td><td>搜索量中等、竞争小、用户需求明确</td><td>文章标题、FAQ模块、正文小标题</td></tr></tbody></table><blockquote><p>个人博客SEO的核心策略：<strong>放弃大竞争的核心词，主攻长尾词和疑问词</strong>。比如你抢不过「Hexo教程」的前排，但「Hexo非全局安装完整步骤」这类长尾词，很容易做到前排，而且用户需求完全匹配。</p></blockquote></section><section><h4>🔧 ② 关键词挖掘工具（免费可用）<a href="#--关键词挖掘工具免费可用"><span>#</span></a></h4><ul>
<li>国内（百度）：百度指数、5118免费版、站长工具、爱站网、百度搜索下拉框/相关搜索</li>
<li>国外（Google）：Google Keyword Planner、Ahrefs免费版、Semrush免费版、Google搜索下拉框/相关搜索</li>
<li>零成本方法：直接看百度/Google搜索结果的「相关搜索」，以及同类型优质博客的标题、标签，都是现成的关键词。</li>
</ul></section><section><h4>📏 ③ 关键词布局黄金规则<a href="#--关键词布局黄金规则"><span>#</span></a></h4><ul>
<li><strong>一个页面只主攻1个核心关键词+2-3个相关长尾词</strong>：不要一篇文章堆一堆关键词，会导致主题分散，搜索引擎不知道你的页面核心是什么。</li>
<li><strong>标题必须包含核心关键词</strong>：页面标题（Title）是搜索引擎权重最高的标签，核心关键词必须完整出现在标题里，且越靠前权重越高。</li>
<li><strong>自然分布，拒绝堆砌</strong>：关键词要自然出现在正文开头、结尾、小标题里，不要生硬重复堆砌，否则会被惩罚。</li>
<li><strong>全路径覆盖</strong>：URL、H标签、图片Alt标签、Meta描述里，都要自然包含核心关键词。</li>
</ul></section></section><section><h3>✍️ 2. 内容质量优化：SEO的核心灵魂<a href="#️-2-内容质量优化seo的核心灵魂"><span>#</span></a></h3><p>搜索引擎的核心目标，是给用户提供能解决问题的优质内容。<strong>内容是1，其他所有优化都是0</strong>，没有优质内容，再完美的SEO优化都没用。</p><p>现在百度/Google都严格执行 <strong>E-E-A-T 原则</strong>，这是判断内容质量的核心标准：</p><ul>
<li><strong>Experience（经验）</strong>：内容有你的真实实操经验，比如你自己踩过的坑、验证过的步骤，不是网上复制的空话。</li>
<li><strong>Expertise（专业性）</strong>：内容有专业度，逻辑清晰，知识点准确，能真正解决用户的问题。</li>
<li><strong>Authoritativeness（权威性）</strong>：你在这个领域有持续的内容输出，被行业认可，有其他网站引用你的内容。</li>
<li><strong>Trustworthiness（可信度）</strong>：内容真实、无虚假信息，有明确的来源标注，网站有正规的备案、作者信息。</li>
</ul><section><h4>📝 个人博客内容SEO优化实操要点<a href="#-个人博客内容seo优化实操要点"><span>#</span></a></h4><ol>
<li><strong>100%原创优先</strong>：绝对不要采集、复制、伪原创别人的内容，搜索引擎能精准识别，不仅不会收录，还会降低整个网站的信任度。哪怕是同一个主题，用你自己的话、自己的实操经验写出来，就是优质原创。</li>
<li><strong>解决用户的核心问题</strong>：用户搜索一个关键词，核心是想解决一个问题。你的文章必须开门见山，先给解决方案，再讲细节，而不是大段无关的铺垫。</li>
<li><strong>内容完整闭环</strong>：一篇优质文章，要做到「前提准备→分步操作→原理解释→避坑指南→总结」的完整闭环，用户看完就能直接落地，不用再去搜其他内容。</li>
<li><strong>结构清晰，可读性强</strong>：用H1-H6标签做好层级，多分段、用列表、代码块、加粗突出重点，不要大段无格式的文字，提升用户的停留时间和阅读体验。</li>
<li><strong>持续稳定更新</strong>：搜索引擎更喜欢持续更新的网站，不用日更，固定每周1-2篇的稳定更新频率，远好于一次更10篇，然后一个月不更新。</li>
</ol></section></section><section><h3>🏗️ 3. 网站结构与URL优化<a href="#️-3-网站结构与url优化"><span>#</span></a></h3><p>清晰的网站结构，能让爬虫更好地抓取你的所有页面，也能让用户更好地浏览内容，提升页面权重分配。</p><section><h4>🔗 ① URL优化黄金规则（Hexo博客必做）<a href="#--url优化黄金规则hexo博客必做"><span>#</span></a></h4><p>URL就是你的文章链接，是SEO的核心细节，之前讲的「emoji不要进URL」就是这个原因：</p><ul>
<li><strong>静态化</strong>：Hexo生成的.html静态链接，天生符合SEO要求，不要用带问号、参数的动态链接。</li>
<li><strong>简短规范</strong>：URL越短越好，不要超过3层目录，比如<code>https://xxx.com/hexo-install.html</code> 远好于 <code>https://xxx.com/2026/04/05/blog/tech/hexo/non-global/install.html</code>。</li>
<li><strong>包含关键词</strong>：URL里用英文关键词，比如<code>hexo-non-global-install.html</code>，不要用中文、拼音、无意义的随机字符。</li>
<li><strong>永久不变</strong>：文章发布后，URL绝对不要修改，否则之前的收录会失效，权重会丢失。Hexo里用<code>permalink</code>手动固定文章URL，就是为了这个。</li>
<li><strong>统一大小写</strong>：全部用小写字母，避免大小写混合导致的重复页面问题。</li>
</ul></section><section><h4>🗺️ ② 网站结构优化要点<a href="#️--网站结构优化要点"><span>#</span></a></h4><ul>
<li><strong>扁平结构</strong>：所有文章页面，最多3次点击就能从首页到达，不要做太深的目录层级。</li>
<li><strong>内链体系</strong>：文章内自然添加指向你其他相关文章的链接（锚文本），比如写Hexo安装的文章里，链接到Butterfly主题配置的文章。内链能让爬虫抓取更多页面，也能把权重分配给其他页面，提升整站排名。</li>
<li><strong>面包屑导航</strong>：开启Butterfly主题的面包屑导航，让用户和爬虫都知道当前页面的位置，提升用户体验和SEO效果。</li>
<li><strong>分类与标签规范</strong>：分类不要超过5个，每个分类下有至少5篇文章；标签不要堆砌，一篇文章3-5个标签即可，避免大量无内容的空分类、空标签页面。</li>
</ul></section></section><section><h3>🏷️ 4. HTML标签优化：告诉搜索引擎你的内容重点<a href="#️-4-html标签优化告诉搜索引擎你的内容重点"><span>#</span></a></h3><p>HTML标签是搜索引擎识别页面结构和重点的核心依据，正确的标签优化，能大幅提升页面的SEO权重。</p>

<table><thead><tr><th>标签</th><th>SEO核心作用</th><th>个人博客优化规则</th></tr></thead><tbody><tr><td>Title标签</td><td>搜索引擎权重最高的标签，决定搜索结果的标题，直接影响排名和点击率</td><td>1. 每个页面的Title必须唯一；2. 完整包含核心关键词，越靠前越好；3. 长度控制在20-30个汉字（百度）/50-60个字符（Google）；4. 格式：核心关键词 - 分类 - 博客名称，示例：<code>Hexo非全局安装完整教程 - 博客搭建 - 我的技术博客</code></td></tr><tr><td>Meta Description标签</td><td>不直接影响排名，但会显示在搜索结果的描述里，直接影响用户的点击率</td><td>1. 每个页面唯一；2. 包含核心关键词；3. 长度控制在70-120个汉字；4. 清晰说明页面的核心价值，给用户点击的理由，示例：<code>本文详细讲解Hexo非全局安装的完整步骤，从环境准备到部署上线，附高频踩坑解决方案，新手可直接套用。</code></td></tr><tr><td>H1-H6标题标签</td><td>告诉搜索引擎页面的内容层级，权重从H1到H6依次降低</td><td>1. 一个页面只能有一个H1标签，就是文章的大标题；2. H2标签是文章的核心模块，H3是H2下的子模块，层级不能乱跳（比如H1之后直接用H3）；3. 标题里自然包含相关关键词，不要所有标题都堆同一个关键词</td></tr><tr><td>图片Alt标签</td><td>搜索引擎看不懂图片，靠Alt标签识别图片的内容，同时能获取图片搜索的流量</td><td>1. 所有图片都必须加Alt标签；2. Alt内容要准确描述图片，自然包含关键词，示例：<code>![Hexo非全局安装命令执行结果](/img/hexo-install.png)</code>；3. 不要堆砌关键词，不要留空</td></tr><tr><td>Canonical标签</td><td>规范页面的唯一网址，解决重复内容收录的问题</td><td>比如一篇文章有多个访问路径，用Canonical标签告诉搜索引擎哪个是主链接，避免权重分散。Hexo主题一般会自动配置，不用手动修改</td></tr><tr><td>a标签（链接）</td><td>站内链接传递权重，站外链接关联相关内容</td><td>1. 站内链接的锚文本，要用相关关键词，不要用「点击这里」「详情」这类无意义的文字；2. 指向外部网站的链接，要加<code>rel="nofollow"</code>标签，避免权重流失，Hexo有插件可以自动配置</td></tr></tbody></table></section><section><h3>🚀 5. 页面加载速度优化<a href="#-5-页面加载速度优化"><span>#</span></a></h3><p>页面加载速度是搜索引擎排名的核心因素之一，尤其是Google的<strong>Core Web Vitals（核心网页指标）</strong>，已经成为排名的硬指标。同时，加载慢的页面，用户会直接关闭，跳出率极高，也会影响排名。</p><p>Hexo本身是静态页面，加载速度天生比动态网站快，做好这几点就能拉满速度：</p><ol>
<li><strong>图片压缩</strong>：所有图片上传前先压缩，推荐用TinyPNG，不要直接上传几MB的大图，这是拖慢速度的头号元凶。</li>
<li><strong>开启CDN</strong>：用jsDelivr、Cloudflare等免费CDN加速静态资源，国内用户可以用七牛云、又拍云的免费CDN。</li>
<li><strong>主题精简</strong>：关闭Butterfly主题里不用的功能，比如多余的动画、特效、第三方插件，减少不必要的资源加载。</li>
<li><strong>静态资源优化</strong>：合并压缩CSS/JS文件，开启Gzip压缩，Hexo有对应的插件可以自动实现。</li>
<li><strong>避免大量第三方脚本</strong>：不要在页面里加太多的统计、广告、特效脚本，每一个脚本都会拖慢加载速度。</li>
</ol></section><section><h3>📱 6. 移动端适配优化<a href="#-6-移动端适配优化"><span>#</span></a></h3><p>现在移动端搜索流量占比已经超过80%，搜索引擎已经全面切换为「移动端优先」的索引模式，移动端体验不好的页面，排名会直接受影响。</p><ul>
<li>Hexo的Butterfly等主流主题，都是响应式设计，天生适配移动端，不用自己写代码。</li>
<li>优化要点：避免移动端字体太小、按钮太密、大段文字、横向滚动条，保证移动端的阅读体验。</li>
</ul><hr /></section></section><section><h2>🔗 四、站外SEO 核心优化要点<a href="#-四站外seo-核心优化要点"><span>#</span></a></h2><p>站外SEO的核心，是提升你网站在全网的信任度和权威性，核心载体是<strong>反向链接（外链）</strong>。
外链，就是其他网站链接到你的博客页面。搜索引擎会把每一条高质量外链，当成一次「信任投票」，投票越多，你的网站权重越高，排名越好。</p><section><h3>⭐ 1. 外链的核心判断标准：质量永远大于数量<a href="#-1-外链的核心判断标准质量永远大于数量"><span>#</span></a></h3><p>1条高质量的外链，效果远好于100条垃圾外链。高质量外链的标准：</p><ul>
<li><strong>相关度高</strong>：和你的博客主题同领域，比如技术博客的外链，来自其他技术博客、技术社区，而不是无关的娱乐网站。</li>
<li><strong>权威性高</strong>：网站本身权重高、信任度高，比如官方网站、行业知名博客、正规媒体网站。</li>
<li><strong>正规自然</strong>：是对方网站主动引用你的内容，而不是你买的、刷的，锚文本自然，不是生硬堆砌关键词。</li>
<li><strong>可抓取</strong>：链接没有加<code>nofollow</code>标签，爬虫能顺着链接爬到你的网站，传递权重。</li>
</ul></section><section><h3>🎯 2. 个人博客免费获取高质量外链的方法<a href="#-2-个人博客免费获取高质量外链的方法"><span>#</span></a></h3><ol>
<li><strong>优质内容被动获链</strong>：这是最有效、最安全的方法。写出优质、稀缺、有价值的内容，比如完整的教程、踩坑指南、行业分析，其他博主会主动引用你的文章，给你加外链。</li>
<li><strong>友情链接互换</strong>：和同领域、同权重的个人博客互换友情链接，放在首页的友情链接模块，注意不要和违规、低质网站互换，否则会被牵连。</li>
<li><strong>技术社区发文引流</strong>：在掘金、知乎、CSDN、InfoQ等技术社区，发布文章的精简版/摘要版，原文链接指向你的Hexo博客，既能带来精准流量，又能获得高质量外链。</li>
<li><strong>客座博文（Guest Post）</strong>：给同领域的知名博客投稿，文章里可以带自己的博客链接，既能获得高权重外链，又能曝光自己的品牌。</li>
<li><strong>开源项目/文档贡献</strong>：给开源项目贡献代码、文档，在个人简介里加上自己的博客链接，能获得高权重的官方网站外链。</li>
</ol></section><section><h3>⚠️ 3. 绝对不能碰的外链雷区<a href="#️-3-绝对不能碰的外链雷区"><span>#</span></a></h3><ul>
<li>买外链、租外链，不管是批量的还是单独的，一旦被识别，直接降权、K站。</li>
<li>批量在论坛、贴吧、评论区发的垃圾外链，不仅没用，还会降低网站信任度。</li>
<li>黑站、违规网站的外链，会被搜索引擎牵连，直接降权。</li>
<li>短时间内新增大量外链，会被算法判定为作弊，触发惩罚。</li>
</ul><hr /></section></section><section><h2>🚀 五、Hexo博客 专属SEO优化全方案（直接落地）<a href="#-五hexo博客-专属seo优化全方案直接落地"><span>#</span></a></h2><p>结合你用的Hexo+Butterfly主题，给你一套可直接复制、一步到位的SEO优化方案，从配置到插件，全流程覆盖。</p><section><h3>⚙️ 1. Hexo根目录 <code>_config.yml</code> 核心SEO配置<a href="#️-1-hexo根目录-_configyml-核心seo配置"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 1. 站点核心信息（SEO核心）</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>你的博客名称</span><span> </span><span># 比如：XX的技术博客</span></div></div><div><div><div>3</div></div><div><span>subtitle</span><span>: </span><span>博客副标题</span><span> </span><span># 比如：专注Hexo教程、技术踩坑记录</span></div></div><div><div><div>4</div></div><div><span>description</span><span>: </span><span>这里写博客的核心描述，包含你的核心关键词，100字以内</span><span> </span><span># 示例：专注分享Hexo博客搭建教程、前端技术实操、运维踩坑记录，提供可直接落地的新手教程。</span></div></div><div><div><div>5</div></div><div><span>keywords</span><span>: [</span><span>Hexo教程</span><span>, </span><span>个人博客搭建</span><span>, </span><span>技术教程</span><span>, </span><span>Butterfly主题</span><span>] </span><span># 核心关键词，3-5个即可</span></div></div><div><div><div>6</div></div><div><span>author</span><span>: </span><span>你的昵称</span></div></div><div><div><div>7</div></div><div><span>language</span><span>: </span><span>zh-CN</span><span> </span><span># 语言，中文用zh-CN，英文用en</span></div></div><div><div><div>8</div></div><div><span>timezone</span><span>: </span><span>Asia/Shanghai</span><span> </span><span># 时区</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 2. URL优化（SEO核心）</span></div></div><div><div><div>11</div></div><div><span>url</span><span>: </span><span>https://你的博客域名.com</span><span> </span><span># 你的博客完整域名，必须和实际访问地址一致</span></div></div><div><div><div>12</div></div><div><span>root</span><span>: </span><span>/</span></div></div><div><div><div>13</div></div><div><span>permalink</span><span>: </span><span>:year/:month/:day/:title.html</span><span> </span><span># 不推荐，建议用下面的固定格式</span></div></div><div><div><div>14</div></div><div><span># 推荐SEO友好的URL格式，避免emoji、中文进入URL</span></div></div><div><div><div>15</div></div><div><span>permalink</span><span>: </span><span>:year/:month/:day/:english_title.html</span></div></div><div><div><div>16</div></div><div><span># 或者单篇文章手动固定permalink，最稳妥</span></div></div><div><div><div>17</div></div><div><span>permalink_defaults</span><span>:</span></div></div><div><div><div>18</div></div><div><span>pretty_urls</span><span>:</span></div></div><div><div><div>19</div></div><div><span>  </span><span>trailing_index</span><span>: </span><span>true</span></div></div><div><div><div>20</div></div><div><span>  </span><span>trailing_html</span><span>: </span><span>true</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span># 3. 目录结构优化</span></div></div><div><div><div>23</div></div><div><span>new_post_name</span><span>: </span><span>:title.md</span><span> </span><span># 新建文章的文件名格式</span></div></div><div><div><div>24</div></div><div><span>post_asset_folder</span><span>: </span><span>true</span><span> </span><span># 开启文章资源文件夹，优化图片管理</span></div></div><div><div><div>25</div></div><div><span>skip_render</span><span>: </span><span># 跳过渲染的文件，不要放文章</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>- </span><span>README.md</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span># 4. 分类与标签优化</span></div></div><div><div><div>29</div></div><div><span>default_category</span><span>: </span><span>未分类</span></div></div><div><div><div>30</div></div><div><span>category_map</span><span>: </span><span># 分类别名，用英文，优化URL</span></div></div><div><div><div>31</div></div><div><span>  </span><span>技术教程</span><span>: </span><span>tech-tutorial</span></div></div><div><div><div>32</div></div><div><span>  </span><span>博客搭建</span><span>: </span><span>blog-build</span></div></div><div><div><div>33</div></div><div><span>tag_map</span><span>: </span><span># 标签别名</span></div></div><div><div><div>34</div></div><div><span>  </span><span>Hexo</span><span>: </span><span>hexo</span></div></div><div><div><div>35</div></div><div><span>  </span><span>Butterfly</span><span>: </span><span>butterfly</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>🔌 2. Hexo SEO 必备插件（一键安装）<a href="#-2-hexo-seo-必备插件一键安装"><span>#</span></a></h3>

<table><thead><tr><th>插件名称</th><th>核心作用</th><th>安装命令</th></tr></thead><tbody><tr><td>hexo-generator-sitemap</td><td>生成全站XML站点地图，提交给搜索引擎，帮助爬虫抓取所有页面</td><td><code>npm install hexo-generator-sitemap --save</code></td></tr><tr><td>hexo-generator-baidu-sitemap</td><td>生成百度专属的站点地图，适配百度的抓取规则</td><td><code>npm install hexo-generator-baidu-sitemap --save</code></td></tr><tr><td>hexo-autonofollow</td><td>自动给所有外部链接加<code>rel="nofollow noreferrer"</code>标签，避免权重流失</td><td><code>npm install hexo-autonofollow --save</code></td></tr><tr><td>hexo-filter-cleanup</td><td>自动压缩优化CSS/JS/HTML，提升页面加载速度</td><td><code>npm install hexo-filter-cleanup --save</code></td></tr><tr><td>hexo-related-popular-posts</td><td>生成相关文章推荐，完善内链体系，提升用户停留时间</td><td><code>npm install hexo-related-popular-posts --save</code></td></tr></tbody></table><p>安装完成后，重新生成部署，插件会自动生效，站点地图会生成在<code>https://你的域名/sitemap.xml</code>和<code>https://你的域名/baidusitemap.xml</code>。</p></section><section><h3>📬 4. 搜索引擎提交与收录<a href="#-4-搜索引擎提交与收录"><span>#</span></a></h3><p>优化完成后，必须把你的博客提交给搜索引擎，才能让爬虫更快地抓取和收录你的页面：</p><ol>
<li><strong>百度搜索资源平台</strong>：<a href="https://ziyuan.baidu.com/%EF%BC%8C%E6%B3%A8%E5%86%8C%E8%B4%A6%E5%8F%B7%EF%BC%8C%E6%B7%BB%E5%8A%A0%E4%BD%A0%E7%9A%84%E7%BD%91%E7%AB%99%EF%BC%8C%E5%AE%8C%E6%88%90%E9%AA%8C%E8%AF%81%EF%BC%8C%E6%8F%90%E4%BA%A4sitemap%EF%BC%8C%E5%BC%80%E5%90%AF%E4%B8%BB%E5%8A%A8%E6%8E%A8%E9%80%81%E3%80%82" target="_blank">https://ziyuan.baidu.com/，注册账号，添加你的网站，完成验证，提交sitemap，开启主动推送。</a></li>
<li><strong>Google Search Console</strong>：<a href="https://search.google.com/search-console/%EF%BC%8C%E6%B3%A8%E5%86%8C%E8%B4%A6%E5%8F%B7%EF%BC%8C%E6%B7%BB%E5%8A%A0%E7%BD%91%E7%AB%99%EF%BC%8C%E9%AA%8C%E8%AF%81%E6%89%80%E6%9C%89%E6%9D%83%EF%BC%8C%E6%8F%90%E4%BA%A4sitemap%EF%BC%8CGoogle%E6%94%B6%E5%BD%95%E9%80%9F%E5%BA%A6%E8%BF%9C%E5%BF%AB%E4%BA%8E%E7%99%BE%E5%BA%A6%E3%80%82" target="_blank">https://search.google.com/search-console/，注册账号，添加网站，验证所有权，提交sitemap，Google收录速度远快于百度。</a></li>
<li><strong>必应站长平台</strong>：<a href="https://www.bing.com/webmasters/%EF%BC%8C%E5%8F%AF%E4%BB%A5%E7%9B%B4%E6%8E%A5%E5%AF%BC%E5%85%A5Google" target="_blank">https://www.bing.com/webmasters/，可以直接导入Google</a> Search Console的配置，一键提交，必应的收录速度也很快。</li>
</ol><hr /></section></section><section><h2>📊 六、SEO 效果监测与持续优化<a href="#-六seo-效果监测与持续优化"><span>#</span></a></h2><p>SEO不是一劳永逸的，需要持续监测数据，根据数据调整优化策略。核心监测维度和工具如下：</p><section><h3>📈 1. 核心监测指标<a href="#-1-核心监测指标"><span>#</span></a></h3><ul>
<li><strong>收录量</strong>：搜索引擎收录了你的多少页面，是SEO的基础，没有收录就没有排名。</li>
<li><strong>关键词排名</strong>：你的核心关键词，在搜索结果里排第几页、第几位。</li>
<li><strong>搜索流量</strong>：每天从搜索引擎带来的访客数量、访问页面数。</li>
<li><strong>点击率</strong>：搜索结果里，用户点击你的页面的比例，和标题、描述优化直接相关。</li>
<li><strong>用户行为</strong>：停留时间、跳出率、平均访问页面数，反映你的内容质量和用户体验。</li>
<li><strong>外链数量</strong>：有多少网站链接到你的博客，反映网站的权威性。</li>
</ul></section><section><h3>🛠️ 2. 免费监测工具<a href="#️-2-免费监测工具"><span>#</span></a></h3><ul>
<li>搜索引擎官方工具：百度搜索资源平台、Google Search Console（最核心、最准确）</li>
<li>网站统计工具：百度统计、Google Analytics、CNZZ</li>
<li>排名查询工具：站长工具、爱站网、Ahrefs免费版</li>
</ul><hr /></section></section><section><h2>⚠️ 七、个人博客SEO 常见误区与避坑指南<a href="#️-七个人博客seo-常见误区与避坑指南"><span>#</span></a></h2><ol>
<li><strong>关键词堆砌</strong>：在标题、正文里生硬重复大量关键词，以为能提升排名，实际会被搜索引擎惩罚，降权。</li>
<li><strong>只做优化不做内容</strong>：把所有精力放在配置、插件、外链上，内容质量极低，哪怕短期有排名，长期也会掉下去，内容永远是核心。</li>
<li><strong>频繁修改标题/URL</strong>：文章发布后，频繁修改标题、URL，会导致搜索引擎重新收录，排名大幅波动，甚至之前的收录全部失效。</li>
<li><strong>采集/伪原创内容</strong>：复制别人的内容，用工具伪原创，以为能省时间，实际搜索引擎能精准识别，不仅不会收录，还会降低整站的信任度，甚至K站。</li>
<li><strong>盲目追求核心大词</strong>：新手一上来就想抢「Hexo教程」这类大词的排名，竞争极大，很难见效，不如主攻长尾词，快速见效，积累权重。</li>
<li><strong>黑帽SEO作弊</strong>：买外链、刷流量、刷排名，短期可能有效果，一旦被算法识别，网站会被彻底K站，之前的所有努力全部白费，绝对不要碰。</li>
<li><strong>急于求成</strong>：SEO是长期主义，新站一般有1-3个月的「沙盒期」，搜索引擎会观察你的网站，这段时间排名波动、收录慢都是正常的，不要做了一周没效果就放弃。</li>
</ol><hr /></section><section><h2>🔮 八、SEO 最新趋势与长期策略<a href="#-八seo-最新趋势与长期策略"><span>#</span></a></h2><ol>
<li><strong>E-E-A-T 权重持续提升</strong>：搜索引擎越来越看重内容的经验、专业性、权威性，AI生成的内容如果没有人工的经验加工、专业校验，很难获得好的排名。你的个人实操经验、踩坑记录，就是最大的内容优势。</li>
<li><strong>用户体验成为核心硬指标</strong>：页面加载速度、移动端适配、交互体验、内容可读性，已经和内容质量同等重要，甚至更重要。</li>
<li><strong>长尾词+疑问词流量占比提升</strong>：随着语音搜索、AI助手的普及，用户的搜索越来越偏向自然语言、问题式搜索，主攻这类关键词，是个人博客的核心机会。</li>
<li><strong>内容的完整闭环更重要</strong>：用户越来越喜欢「一篇文章解决所有问题」，不用再跳转到其他页面，内容越完整、越闭环，用户停留时间越长，排名越好。</li>
</ol><section><h3>🎯 个人博客SEO的长期核心策略<a href="#-个人博客seo的长期核心策略"><span>#</span></a></h3><p><strong>持续输出垂直领域的优质原创内容，配合规范的SEO基础优化，长期稳定更新，慢慢积累权重和外链</strong>。
SEO没有捷径，也没有黑科技，最笨、最稳的方法，就是最有效的方法。你的每一篇优质文章，都是你SEO的护城河。</p><p>详细效果见个人主页<a href="https://xutopia.top">xutopia77 - 见字如面</a></p></section></section></section>
<section><h1>❓ 常见问题解答<a href="#-常见问题解答"><span>#</span></a></h1><section><h2>Q1：博客发布到GitHub Pages，再分发到其他平台，会不会影响SEO？<a href="#q1博客发布到github-pages再分发到其他平台会不会影响seo"><span>#</span></a></h2><p>这是一个非常实际的问题，很多博主都有内容分发的需求。答案是：<strong>如果操作得当，不仅不会影响SEO，还能带来额外收益</strong>。</p><section><h3>🔍 核心问题分析<a href="#-核心问题分析"><span>#</span></a></h3><p>搜索引擎对重复内容的态度：</p><ul>
<li>搜索引擎会识别重复内容，但<strong>不会直接惩罚</strong></li>
<li>会选择一个「权威版本」优先展示</li>
<li>其他版本可能被降权或不展示</li>
</ul></section><section><h3>✅ 正确的内容分发策略<a href="#-正确的内容分发策略"><span>#</span></a></h3>

<table><thead><tr><th>策略</th><th>具体做法</th><th>效果</th></tr></thead><tbody><tr><td><strong>时间优先</strong></td><td>先在博客发布，等待收录后再分发（建议间隔3-7天）</td><td>搜索引擎会优先认定博客为原创来源</td></tr><tr><td><strong>链接回源</strong></td><td>分发时在文末添加「原文链接」指向博客</td><td>为博客带来高质量外链和精准流量</td></tr><tr><td><strong>内容差异化</strong></td><td>分发版本做适当精简或补充</td><td>减少重复内容判定，各有价值</td></tr><tr><td><strong>平台选择</strong></td><td>选择高权重平台（掘金、知乎、CSDN）</td><td>外链质量高，流量大</td></tr></tbody></table></section><section><h3>⚠️ 需要避免的错误做法<a href="#️-需要避免的错误做法"><span>#</span></a></h3><ol>
<li><strong>同时发布</strong>：博客和平台同时发布，搜索引擎可能认定平台为原创（平台权重更高）</li>
<li><strong>完全复制</strong>：一字不改直接分发，重复内容问题严重</li>
<li><strong>无原文链接</strong>：分发时不添加原文链接，失去外链价值</li>
<li><strong>分发到低质平台</strong>：低质平台的外链不仅无用，还可能带来负面影响</li>
</ol></section><section><h3>💡 最佳实践流程<a href="#-最佳实践流程"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>1. 博客首发 → 等待3-7天让搜索引擎收录</span></div></div><div><div><div>2</div></div><div><span>2. 检查收录 → site:你的域名 确认文章已被收录</span></div></div><div><div><div>3</div></div><div><span>3. 内容调整 → 适当精简或补充，添加「首发于xxx博客」</span></div></div><div><div><div>4</div></div><div><span>4. 平台分发 → 附上原文链接</span></div></div><div><div><div>5</div></div><div><span>5. 持续观察 → 监控流量和外链效果</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>📊 收益分析<a href="#-收益分析"><span>#</span></a></h3>

<table><thead><tr><th>维度</th><th>说明</th></tr></thead><tbody><tr><td>流量收益</td><td>平台自带流量，带来额外曝光和访问</td></tr><tr><td>外链收益</td><td>高权重平台的原文链接是优质外链</td></tr><tr><td>品牌收益</td><td>多平台曝光，提升个人品牌知名度</td></tr><tr><td>SEO收益</td><td>正确操作下，博客原创权重不受影响</td></tr></tbody></table><p><strong>总结</strong>：内容分发是「借力打力」的策略，关键是<strong>先发博客、后发平台、必加链接</strong>，这样既能保护博客的原创权重，又能享受平台的流量红利。</p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/hexo-title-emoji-optimization-guide/</id>
      <title type="text">📝 Hexo博客标题加emoji完整指南：一秒提升目录辨识度</title>
      <published>2025-12-05T00:00:00.000Z</published>
      <updated>2025-12-05T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/hexo-title-emoji-optimization-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>很多Hexo博主都会遇到一个高频痛点：辛辛苦苦写完的长文教程，侧边栏目录密密麻麻全是纯文字标题，读者扫一眼找不到重点，自己回头翻笔记也毫无辨识度，甚至连模块边界都分不清。</p>
<p>有没有零成本、零配置、效果立竿见影的解决方案？当然有——<strong>在标题前加入语义匹配的emoji</strong>。</p>
<p>这篇文章就给大家一套可直接落地、适配Butterfly主题、不杂乱、真正提升阅读体验的完整方案，拿过去就能直接用在自己的博客里。更多建站经验分享，可以查看我的博客 <a href="https://xutopia.top">xutopia77 - 见字如面</a>。</p>

<section><h2>✅ 零配置直接生效：核心实现方法<a href="#-零配置直接生效核心实现方法"><span>#</span></a></h2><p>Hexo的侧边栏目录（TOC）完全根据Markdown的<code>#</code>标题层级自动生成，<strong>无需任何插件、无需修改主题源码，直接在标题前加emoji，目录、正文、首页卡片、归档页会自动同步显示</strong>，全版本Hexo兼容，包括非全局安装的部署方式。</p><p>给大家看最直观的效果对比：</p>

<table><thead><tr><th>无emoji（无辨识度，纯文字堆砌）</th><th>加emoji（一眼抓重点，模块清晰）</th></tr></thead><tbody><tr><td><code># Hexo非全局安装完整指南</code></td><td><code># 📦 Hexo非全局安装完整指南</code></td></tr><tr><td><code>## 前置环境准备</code></td><td><code>## ⚙️ 前置环境准备</code></td></tr><tr><td><code>## 安装初始化步骤</code></td><td><code>## ✅ 安装初始化步骤</code></td></tr><tr><td><code>## 常见踩坑指南</code></td><td><code>## ⚠️ 常见踩坑指南</code></td></tr><tr><td><code>## 总结</code></td><td><code>## 🏁 总结与后续优化</code></td></tr></tbody></table><p>加完之后，不管是读者滚动页面时的目录定位，还是快速扫读全文抓核心，都能瞬间区分模块类型，辨识度直接拉满，同时还能让你的博客风格更有个人特色。</p></section>
<section><h2>🎯 4条黄金规则：避免杂乱，真正提升体验<a href="#-4条黄金规则避免杂乱真正提升体验"><span>#</span></a></h2><p>很多人加emoji反而越用越乱，核心是没有做到「语义固定、克制统一」。记住这4条规则，效果直接翻倍，不会出现花里胡哨的反效果。</p><section><h3>1. 语义强绑定：固定分类对应固定emoji<a href="#1-语义强绑定固定分类对应固定emoji"><span>#</span></a></h3><p>这是提升辨识度的核心逻辑：<strong>同一类内容，永远用同一个emoji</strong>，形成你的专属视觉符号。读者扫一眼emoji，不用看文字就知道这部分的内容类型，形成稳定的阅读预期。</p><p>给大家整理了一套技术博客通用、全平台兼容的固定搭配，直接套用即可：</p>

<table><thead><tr><th>内容模块类型</th><th>固定emoji</th><th>适用场景</th></tr></thead><tbody><tr><td>文章总标题/核心主题</td><td>📦</td><td>整篇文章的大标题，代表完整的内容包</td></tr><tr><td>前置准备/环境配置</td><td>⚙️ 🛠️</td><td>环境安装、前提条件、工具准备</td></tr><tr><td>分步操作/教程步骤</td><td>✅ 📝</td><td>可复现的操作步骤、核心流程</td></tr><tr><td>原理讲解/概念科普</td><td>💡 🧠</td><td>原理解释、底层逻辑、概念说明</td></tr><tr><td>代码/配置文件修改</td><td>💻 🔧</td><td>代码块、配置项修改、参数说明</td></tr><tr><td>避坑指南/注意事项</td><td>⚠️ 🚫</td><td>高频踩坑、风险提示、禁用操作</td></tr><tr><td>数据/效果展示</td><td>📊 📈</td><td>统计数据、效果对比、测试结果</td></tr><tr><td>总结/收尾</td><td>🏁 🎯</td><td>全文总结、行动建议、延伸方向</td></tr><tr><td>参考/拓展链接</td><td>🔗 📎</td><td>参考文档、拓展阅读、相关链接</td></tr></tbody></table><p>❌ 反面教材：这次避坑模块用<code>⚠️</code>，下次用<code>🚫</code>，再下次用<code>❗</code>，读者无法形成记忆，反而失去辨识度。
✅ 正确做法：所有避坑模块固定用<code>⚠️</code>，所有步骤固定用<code>✅</code>，形成稳定的视觉符号。</p></section><section><h3>2. 层级统一：有疏有密，避免视觉过载<a href="#2-层级统一有疏有密避免视觉过载"><span>#</span></a></h3><p>不要给所有层级的标题都加emoji，不然密密麻麻反而失去重点，推荐通用层级规则：</p><ul>
<li>一级标题（<code>#</code>）：必加，奠定全文的视觉基调</li>
<li>二级标题（<code>##</code>）：全加，核心模块的区分，是目录辨识度的核心</li>
<li>三级及以下（<code>###</code>/<code>####</code>）：仅给重点小节加，非重点内容不加，避免视觉疲劳</li>
</ul></section><section><h3>3. 极致克制：一个标题只加1个emoji<a href="#3-极致克制一个标题只加1个emoji"><span>#</span></a></h3><p>绝对不要一个标题堆多个emoji（比如<code># 📦⚙️ 安装准备</code>），不仅杂乱无章，还会稀释核心辨识度，1个精准匹配的emoji，效果远好于多个堆砌。</p></section><section><h3>4. 兼容优先：用全平台通用的emoji<a href="#4-兼容优先用全平台通用的emoji"><span>#</span></a></h3><p>避免用Unicode 15+的冷门新emoji，部分Windows旧系统、手机浏览器会显示成方框（□），上面表格里的emoji均为全平台兼容的通用款，可放心使用。</p></section></section>
<section><h2>🎨 Butterfly主题专属优化<a href="#-butterfly主题专属优化"><span>#</span></a></h2><p>针对大家用得最多的Butterfly主题，做2个极简优化，让显示效果更完美，零代码基础也能直接复制使用。</p><section><h3>1. 优化emoji与文字的间距<a href="#1-优化emoji与文字的间距"><span>#</span></a></h3><p>很多同学会遇到emoji和文字贴太紧、换行时截断的问题，直接在主题配置文件<code>_config.butterfly.yml</code>中，找到<code>inject</code>配置项，添加以下CSS即可完美解决：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>inject</span><span>:</span></div></div><div><div><div>2</div></div><div><span>  </span><span>head</span><span>:</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>- </span><span>&lt;style&gt;</span></div></div><div><div><div>4</div></div><div><span>      </span><span>/* 正文标题emoji间距优化 */</span></div></div><div><div><div>5</div></div><div><span>      </span><span>h1,h2,h3,h4,h5,h6 {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>display</span><span>: </span><span>inline-flex;</span></div></div><div><div><div>7</div></div><div><span>        </span><span>align-items</span><span>: </span><span>center;</span></div></div><div><div><div>8</div></div><div><span>        </span><span>gap</span><span>: </span><span>8px;</span></div></div><div><div><div>9</div></div><div><span>        </span><span>margin-block-start</span><span>: </span><span>1.5em;</span></div></div><div><div><div>10</div></div><div><span>        </span><span>margin-block-end</span><span>: </span><span>0.5em;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>      </span><span>/* 侧边栏目录emoji间距优化 */</span></div></div><div><div><div>13</div></div><div><span>      </span><span>.toc-item .toc-link {</span></div></div><div><div><div>14</div></div><div><span>        </span><span>display</span><span>: </span><span>flex;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>align-items</span><span>: </span><span>center;</span></div></div><div><div><div>16</div></div><div><span>        </span><span>gap</span><span>: </span><span>6px;</span></div></div><div><div><div>17</div></div><div><span>        </span><span>line-height</span><span>: </span><span>1.5;</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>      </span><span>&lt;/style&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2. 避免emoji进入永久链接（SEO优化）<a href="#2-避免emoji进入永久链接seo优化"><span>#</span></a></h3><p>如果你的Hexo永久链接配置用了<code>:title</code>，标题里的emoji会被带入链接（比如<code>https://xxx.com/📦-hexo-install.html</code>），虽然浏览器能识别，但不符合URL规范，也不利于SEO收录。</p><p>2种极简解决方案，选其一即可：</p><section><h4>方案1：单篇文章手动固定链接（推荐，灵活可控）<a href="#方案1单篇文章手动固定链接推荐灵活可控"><span>#</span></a></h4><p>在文章的Front-matter里加<code>permalink</code>，手动设置纯英文规范链接，示例如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>📦 Hexo非全局安装搭建完整指南</span></div></div><div><div><div>3</div></div><div><span>date</span><span>: </span><span>2026-04-05 12:00:00</span></div></div><div><div><div>4</div></div><div><span>permalink</span><span>: </span><span>hexo-non-global-install-guide.html</span></div></div><div><div><div>5</div></div><div><span>tags</span><span>: [</span><span>Hexo</span><span>, </span><span>博客搭建</span><span>]</span></div></div><div><div><div>6</div></div><div><span>categories</span><span>: </span><span>技术教程</span></div></div><div><div><div>7</div></div><div><span>---</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>方案2：全局配置永久链接格式<a href="#方案2全局配置永久链接格式"><span>#</span></a></h4><p>打开Hexo根目录<code>_config.yml</code>，修改permalink配置，不用title生成链接，从根源避免emoji进入URL：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 推荐用年月日+英文标题，无emoji</span></div></div><div><div><div>2</div></div><div><span>permalink</span><span>: </span><span>:year/:month/:day/:english_title.html</span></div></div><div><div><div>3</div></div><div><span># 极简方案，用文章id</span></div></div><div><div><div>4</div></div><div><span>permalink</span><span>: </span><span>:year/:month/:day/:id.html</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>
<section><h2>📋 可直接复制套用的完整文章模板<a href="#-可直接复制套用的完整文章模板"><span>#</span></a></h2><p>给大家整理了一套技术教程类文章的完整模板，完全符合上面的所有规则，兼顾博客的辨识度和笔记的清晰度，适配「单文件双用途」的笔记转博客方案，直接替换内容即可使用：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>---</span></div></div><div><div><div>2</div></div><div><span>title</span><span>: </span><span>📦 Hexo非全局安装搭建完整指南</span></div></div><div><div><div>3</div></div><div><span>permalink</span><span>: </span><span>hexo-non-global-install-guide.html</span></div></div><div><div><div>4</div></div><div><span>date</span><span>: </span><span>2026-04-05 12:00:00</span></div></div><div><div><div>5</div></div><div><span>tags</span><span>: [</span><span>Hexo</span><span>, </span><span>博客搭建</span><span>]</span></div></div><div><div><div>6</div></div><div><span>categories</span><span>: </span><span>技术教程</span></div></div><div><div><div>7</div></div><div><span>---</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 📦 Hexo非全局安装搭建完整指南</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>## ⚙️ 前置环境准备</span></div></div><div><div><div>12</div></div><div><span>### 安装Node.js（版本要求）</span></div></div><div><div><div>13</div></div><div><span>### 安装Git</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>## ✅ 一步完成安装与初始化</span></div></div><div><div><div>16</div></div><div><span>### 核心命令详解</span></div></div><div><div><div>17</div></div><div><span>### 安装结果验证</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>## 📝 核心配置修改</span></div></div><div><div><div>20</div></div><div><span>### 站点基础信息配置</span></div></div><div><div><div>21</div></div><div><span>### Butterfly主题基础配置</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>## 💡 核心命令与日常使用</span></div></div><div><div><div>24</div></div><div><span>### 本地预览命令</span></div></div><div><div><div>25</div></div><div><span>### 生成与部署命令</span></div></div><div><div><div>26</div></div><div><span>### 文章创建与管理</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>## ⚠️ 高频踩坑避坑指南</span></div></div><div><div><div>29</div></div><div><span>### 命令执行失败解决方案</span></div></div><div><div><div>30</div></div><div><span>### 图片不显示的排查方法</span></div></div><div><div><div>31</div></div><div><span>### 部署后页面不更新的处理</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>## 🏁 总结与后续优化方向</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span>&lt;!-- more --&gt;</span></div></div><div><div><div>36</div></div><div><span>【个人笔记备忘区，不会发布到博客】</span></div></div><div><div><div>37</div></div><div><span><span>1.</span><span> 2026.04.06 补充：新增软链接同步笔记的方案</span></span></div></div><div><div><div>38</div></div><div><span><span>2.</span><span> 待更新：自动化部署的进阶配置</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>❓ 常见疑问解答<a href="#-常见疑问解答"><span>#</span></a></h2><section><h3>1. 加emoji会影响SEO吗？<a href="#1-加emoji会影响seo吗"><span>#</span></a></h3><p>完全不会。搜索引擎会自动忽略标题中的emoji，只抓取核心文字内容，不会影响页面的收录和排名。反而清晰的模块结构，会提升页面的用户体验评分，对SEO有正向帮助。</p></section><section><h3>2. 会不会影响笔记的编辑和同步？<a href="#2-会不会影响笔记的编辑和同步"><span>#</span></a></h3><p>完全不影响。Obsidian、VS Code等所有主流Markdown编辑器都完美支持emoji，和「笔记转博客」的自动同步流程100%兼容，同时你自己的本地笔记也能靠emoji快速区分模块，一举两得。</p></section><section><h3>3. 首页文章卡片、归档页会显示emoji吗？<a href="#3-首页文章卡片归档页会显示emoji吗"><span>#</span></a></h3><p>会的。标题里的emoji会同步显示在首页文章卡片、分类页、归档页、标签页，整个博客的视觉统一性和个人辨识度会大幅提升。</p></section></section>
<section><h2>🏁 最后总结<a href="#-最后总结"><span>#</span></a></h2><p>给标题加emoji，是Hexo博客优化里成本最低、收益最高的操作之一：零配置、零插件、零代码基础，就能大幅提升目录辨识度和读者的阅读体验。</p><p>不用追求花里胡哨的效果，记住核心原则：<strong>语义匹配、固定统一、极致克制</strong>，就能让你的博客既美观又实用。</p><p>现在就可以打开你的博客文章，给标题加上适配的emoji，重新部署看看效果吧。
详细效果见个人主页<a href="https://xutopia.top">xutopia77 - 见字如面</a></p></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/how-to-write-good-article-in-ai-era/</id>
      <title type="text">📝 AI时代如何写好一篇文章</title>
      <published>2024-07-06T00:00:00.000Z</published>
      <updated>2024-07-06T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/how-to-write-good-article-in-ai-era/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<blockquote><p>当AI可以一键生成文章，人类写作的价值何在？答案在于：<strong>AI能生成信息，但无法创造经验；能组织文字，但无法注入灵魂。</strong></p></blockquote>

<hr />
<section><h2>🤔 一、AI时代，我们为什么还要写作？<a href="#-一ai时代我们为什么还要写作"><span>#</span></a></h2><section><h3>1.1 写作的本质意义<a href="#11-写作的本质意义"><span>#</span></a></h3><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/22e82a4cadfc70324dddf73fb674ebbb.png" alt="image-20220306155135239" /><figcaption>image-20220306155135239</figcaption></figure><p></p><p>写作从来不只是为了输出内容，更是为了：</p>

<table><thead><tr><th>价值维度</th><th>说明</th></tr></thead><tbody><tr><td><strong>思维整理</strong></td><td>写作是深度思考的过程，强迫我们将模糊的想法变得清晰</td></tr><tr><td><strong>知识内化</strong></td><td>教是最好的学，写作是最高效的学习方式</td></tr><tr><td><strong>个人品牌</strong></td><td>在信息爆炸时代，持续输出是建立影响力的最佳途径</td></tr><tr><td><strong>连接他人</strong></td><td>文字是跨越时空的桥梁，让志同道合的人相遇</td></tr></tbody></table></section><section><h3>1.2 AI时代写作的新价值<a href="#12-ai时代写作的新价值"><span>#</span></a></h3><p>AI能做的事情，人类不必再做；AI做不到的事情，才是人类的护城河：</p>

<table><thead><tr><th>AI能做</th><th>AI做不到</th></tr></thead><tbody><tr><td>快速生成信息类内容</td><td>提供真实的一手经验</td></tr><tr><td>组织结构化知识</td><td>给出独特的个人见解</td></tr><tr><td>润色文字表达</td><td>传递情感与价值观</td></tr><tr><td>翻译、总结、扩展</td><td>创造原创的思想火花</td></tr></tbody></table><p><strong>核心结论</strong>：AI时代，写作的价值从「信息传递」转向「经验分享」和「思想碰撞」。你的踩坑记录、失败教训、独特视角，是AI永远无法替代的。</p><hr /></section></section>
<section><h2>📊 二、好文章的标准：从「信息量」到「人味」<a href="#-二好文章的标准从信息量到人味"><span>#</span></a></h2><section><h3>2.1 传统标准（依然有效）<a href="#21-传统标准依然有效"><span>#</span></a></h3><ul>
<li>🧊 <strong>干货满满</strong>：文章内容要丰富，有实际价值</li>
<li>🧊 <strong>深入浅出</strong>：通俗易懂、生动有趣，易于理解</li>
<li>🧊 <strong>专业前沿</strong>：瞄准技术前沿，在专业领域有价值</li>
<li>🧊 <strong>受众面广</strong>：具有一定的普适性</li>
</ul></section><section><h3>2.2 AI时代新标准<a href="#22-ai时代新标准"><span>#</span></a></h3>

<table><thead><tr><th>新标准</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>真实经验（E）</strong></td><td>文章要有你的真实实操经历，而非AI生成的空话</td><td>「我在项目中踩过的三个坑…」</td></tr><tr><td><strong>独特见解（E）</strong></td><td>提供AI无法生成的个人思考</td><td>「我认为这个技术路线行不通，因为…」</td></tr><tr><td><strong>情感共鸣（A）</strong></td><td>让读者感受到文字背后的人</td><td>「当时调试到凌晨三点，终于找到了问题…」</td></tr><tr><td><strong>可信来源（T）</strong></td><td>标注来源、提供可验证的信息</td><td>附上GitHub链接、测试数据、复现步骤</td></tr></tbody></table><blockquote><p>💡 <strong>E-E-A-T原则</strong>：Google搜索质量评估的核心标准，也是AI时代好文章的黄金法则——Experience（经验）、Expertise（专业性）、Authoritativeness（权威性）、Trustworthiness（可信度）。</p></blockquote><hr /></section></section>
<section><h2>😰 三、写作困扰：AI时代的机遇与挑战<a href="#-三写作困扰ai时代的机遇与挑战"><span>#</span></a></h2><section><h3>3.1 传统困扰<a href="#31-传统困扰"><span>#</span></a></h3>

<table><thead><tr><th>困扰</th><th>表现</th></tr></thead><tbody><tr><td>🍾 困扰1</td><td>有写作意愿但不知道写什么主题</td></tr><tr><td>🍾 困扰2</td><td>能确定主题但不知如何下笔</td></tr><tr><td>🍾 困扰3</td><td>文章没有谋篇布局，写成流水账</td></tr><tr><td>🍾 困扰4</td><td>好不容易写完，内容平平无奇</td></tr></tbody></table></section><section><h3>3.2 AI时代的新困扰<a href="#32-ai时代的新困扰"><span>#</span></a></h3>

<table><thead><tr><th>新困扰</th><th>说明</th></tr></thead><tbody><tr><td>🤖 AI依赖症</td><td>过度依赖AI生成，失去独立思考能力</td></tr><tr><td>🤖 同质化焦虑</td><td>担心自己的内容被AI淹没</td></tr><tr><td>🤖 真假难辨</td><td>读者难以区分AI生成与人类原创</td></tr><tr><td>🤖 价值迷茫</td><td>不知道什么内容值得写</td></tr></tbody></table></section><section><h3>3.3 破解之道<a href="#33-破解之道"><span>#</span></a></h3><p><strong>AI是工具，不是替代者</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>AI擅长：快速生成框架、润色文字、扩展思路、翻译总结</span></div></div><div><div><div>2</div></div><div><span>人类擅长：提供经验、注入见解、传递情感、创造原创</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>正确的协作方式</strong>：</p><ol>
<li>你提供核心观点和真实经验</li>
<li>AI帮你组织结构、润色表达</li>
<li>你审核修改，注入个人风格</li>
<li>最终产出有「人味」的内容</li>
</ol><hr /></section></section>
<section><h2>🛠️ 四、写作方法论：AI时代的八步法<a href="#️-四写作方法论ai时代的八步法"><span>#</span></a></h2><blockquote><p>📘 千里之行，始于足下</p></blockquote><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/9804933a0c71d1db0bccf169dbf3e666.png" alt="image-10" /><figcaption>image-10</figcaption></figure><p></p><section><h3>🥂 Step 1：同理心——打破「知识诅咒」<a href="#-step-1同理心打破知识诅咒"><span>#</span></a></h3><p><strong>知识诅咒</strong>：<code>有些事，一旦知道了，就再也回不去了</code>。导致我们与他人分享就很困难，因为我们不易重造听众的心境。</p><p><strong>AI时代的解法</strong>：</p>

<table><thead><tr><th>方法</th><th>说明</th></tr></thead><tbody><tr><td>零基思维</td><td>放下思维存量，站在读者角度思考</td></tr><tr><td>讲清三问</td><td>是什么？为什么？怎么办？</td></tr><tr><td>AI辅助检验</td><td>让AI模拟小白读者，指出文章中难以理解的地方</td></tr><tr><td>真人验证</td><td>找没有相关背景的同学来检验</td></tr></tbody></table></section><section><h3>🥂 Step 2：攒素材——碎片化记录，结构化整理<a href="#-step-2攒素材碎片化记录结构化整理"><span>#</span></a></h3><p><strong>问题</strong>：写文章时，常常感觉材料捉襟见肘，「茶壶里煮饺子——肚里有货倒不出」。</p><p><strong>原因</strong>：项目周期太长，落笔时思考脉络早已遗忘。</p><p><strong>AI时代的解法</strong>：</p>

<table><thead><tr><th>方法</th><th>工具</th></tr></thead><tbody><tr><td>随手记录</td><td>Notion、Obsidian、flomo</td></tr><tr><td>AI整理</td><td>让AI帮你把碎片笔记整理成结构化知识</td></tr><tr><td>定期回顾</td><td>每周/月回顾笔记，提炼可写主题</td></tr><tr><td>做思辨者</td><td>深入思考，而非浅尝辄止</td></tr></tbody></table><blockquote><p>💡 <strong>AI时代素材新来源</strong>：与AI对话时产生的灵感、AI无法回答的问题、使用AI工具的踩坑经验，都是独特的写作素材。</p></blockquote></section><section><h3>🥂 Step 3：定好题——以正合，以奇胜<a href="#-step-3定好题以正合以奇胜"><span>#</span></a></h3><blockquote><p>意在笔先 ——晋·王羲之</p></blockquote><p><strong>立意原则</strong>：<code>以正合，以奇胜</code>（出自《孙子兵法·兵势篇》）</p><ul>
<li><strong>以正合</strong>：基本能力素养到位，平时多积累</li>
<li><strong>以奇胜</strong>：独特见解吸引读者，水到渠成</li>
</ul><p><strong>AI时代的选题策略</strong>：</p>

<table><thead><tr><th>策略</th><th>说明</th></tr></thead><tbody><tr><td>避开AI能生成的</td><td>信息类、知识类内容，AI已经做得很好</td></tr><tr><td>选择AI做不到的</td><td>真实经验、独特见解、情感故事</td></tr><tr><td>结合热点与深度</td><td>AI能追热点，但无法提供深度分析</td></tr><tr><td>垂直领域深耕</td><td>在细分领域建立不可替代的专业性</td></tr></tbody></table></section><section><h3>🥂 Step 4：搭骨架——结构化思维<a href="#-step-4搭骨架结构化思维"><span>#</span></a></h3><section><h4>🧊 金字塔原理四大原则<a href="#-金字塔原理四大原则"><span>#</span></a></h4>

<table><thead><tr><th>原则</th><th>说明</th></tr></thead><tbody><tr><td>🔶 观点先行</td><td>开篇亮出观点，吸引读者</td></tr><tr><td>🔶 先有提纲</td><td>先列目录，再写内容</td></tr><tr><td>🔶 承上启下</td><td>段落之间做好过渡和转折</td></tr><tr><td>🔶 结尾升华</td><td>总结全文，升华主题，拓展思考</td></tr></tbody></table></section><section><h4>🧊 结构化思维<a href="#-结构化思维"><span>#</span></a></h4>

<table><thead><tr><th>原则</th><th>说明</th></tr></thead><tbody><tr><td>🔶 自上而下</td><td>观点→事实→数据</td></tr><tr><td>🔶 层次清晰</td><td>一个章节说清相关议题</td></tr><tr><td>🔶 结构简单</td><td>「重要的事情说三点」</td></tr><tr><td>🔶 重点突出</td><td>把最想传递的信息突出</td></tr></tbody></table><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/89f818f55ae674c33a9e0d6028bea7a8.png" alt="image-1" /><figcaption>image-1</figcaption></figure><p></p></section><section><h4>🧊 奥卡姆剃刀法则<a href="#-奥卡姆剃刀法则"><span>#</span></a></h4><ul>
<li>🔶 结构不超过3层</li>
<li>🔶 同层次项目不超过7项（记忆容量7±2）</li>
<li>🔶 项目数最好为奇数（紧凑感）</li>
</ul></section><section><h4>🧊 万能文章结构模板<a href="#-万能文章结构模板"><span>#</span></a></h4><p><strong>🔷 工作/项目总结型</strong>
</p><figure><img src="https://imgbed.xutopia.top/image-assets/0e17952e58d55e440d29144c313f3647.png" alt="image-2" /><figcaption>image-2</figcaption></figure><p></p><p><strong>🔷 新技术科普型</strong>
</p><figure><img src="https://imgbed.xutopia.top/image-assets/a388f8e06c9b34583630b03ff589b303.png" alt="image-3" /><figcaption>image-3</figcaption></figure><p></p><p><strong>🔷 深挖本质型</strong>
</p><figure><img src="https://imgbed.xutopia.top/image-assets/4fa926ee2c2d5972137876000a637e00.png" alt="image-4" /><figcaption>image-4</figcaption></figure><p></p><p><strong>🔷 AI时代新增：踩坑复盘型</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>1. 背景：为什么做这件事</span></div></div><div><div><div>2</div></div><div><span>2. 过程：遇到了什么问题</span></div></div><div><div><div>3</div></div><div><span>3. 排查：如何定位问题</span></div></div><div><div><div>4</div></div><div><span>4. 解决：最终如何解决</span></div></div><div><div><div>5</div></div><div><span>5. 反思：学到了什么</span></div></div><div><div><div>6</div></div><div><span>6. AI辅助：AI在这个过程中帮了什么/没帮上什么</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>🥂 Step 5：画好图——一图胜千言<a href="#-step-5画好图一图胜千言"><span>#</span></a></h3><p><strong>科学依据</strong>：人脑处理图片的速度是处理文字的60000倍。</p><p><strong>AI时代的绘图工具</strong>：</p>

<table><thead><tr><th>类型</th><th>工具</th></tr></thead><tbody><tr><td>传统工具</td><td>PowerPoint、ProcessOn、draw.io、Sketch</td></tr><tr><td>手绘风格</td><td>Excalidraw</td></tr><tr><td>AI辅助</td><td>Mermaid（代码生成图表）、AI绘图工具</td></tr></tbody></table><p><strong>配图原则</strong>：</p><ul>
<li>🔷 流程图：讲清复杂流程</li>
<li>🔷 架构图：展示系统设计</li>
<li>🔷 对比图：突出差异</li>
<li>🔷 时间线：展示演进过程</li>
</ul></section><section><h3>🥂 Step 6：有干货——AI时代的核心竞争力<a href="#-step-6有干货ai时代的核心竞争力"><span>#</span></a></h3><blockquote><p>说一千道一万，决定文章质量的最重要的因素还是：有干货，言之有物。</p></blockquote><p><strong>AI时代干货的新定义</strong>：</p>

<table><thead><tr><th>维度</th><th>说明</th></tr></thead><tbody><tr><td>🔷 真实经验</td><td>你亲自踩过的坑、验证过的方案</td></tr><tr><td>🔷 独特见解</td><td>AI无法生成的个人思考</td></tr><tr><td>🔷 深度分析</td><td>超越表面，触及本质</td></tr><tr><td>🔷 可复现</td><td>提供足够细节，读者可以复现</td></tr></tbody></table><p><strong>避免的误区</strong>：</p><ul>
<li>❌ 翻译文档：AI做得更好</li>
<li>❌ 堆砌知识：AI能快速生成</li>
<li>❌ 泛泛而谈：没有实际价值</li>
<li>✅ 分享经验：这是你的护城河</li>
</ul></section><section><h3>🥂 Step 7：勤润色——人机协作<a href="#-step-7勤润色人机协作"><span>#</span></a></h3><p><strong>润色三维度</strong>：语言、章节、排版</p><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/a388f8e06c9b34583630b03ff589b303.png" alt="image-5" /><figcaption>image-5</figcaption></figure>
<figure><img src="https://imgbed.xutopia.top/image-assets/71e5ce462d2a6c06b5553c4fe920a4c3.png" alt="image-6" /><figcaption>image-6</figcaption></figure><p></p><section><h4>语言润色<a href="#语言润色"><span>#</span></a></h4>

<table><thead><tr><th>原则</th><th>说明</th></tr></thead><tbody><tr><td>👌 少即是多</td><td>用最短的篇幅表达</td></tr><tr><td>👌 内容分段</td><td>拆分段落，清晰易懂</td></tr><tr><td>👌 推敲文字</td><td>删华而不实、删主观情绪、删重复啰嗦</td></tr></tbody></table><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/15947d21e5712b3e53e14a6aa439d998.png" alt="image-7" /><figcaption>image-7</figcaption></figure><p></p></section><section><h4>准确与生动<a href="#准确与生动"><span>#</span></a></h4>

<table><thead><tr><th>准确</th><th>生动</th></tr></thead><tbody><tr><td>使用专业术语</td><td>巧用比喻</td></tr><tr><td>定量描述</td><td>多举例子</td></tr><tr><td>职业化语言</td><td>讲故事</td></tr></tbody></table><p></p><figure><img src="https://imgbed.xutopia.top/image-assets/14a964f7626806e977173e20b9230ad5.png" alt="image-8" /><figcaption>image-8</figcaption></figure><p></p></section><section><h4>AI辅助润色<a href="#ai辅助润色"><span>#</span></a></h4>

<table><thead><tr><th>用AI做</th><th>不用AI做</th></tr></thead><tbody><tr><td>检查语法错误</td><td>替代你的核心观点</td></tr><tr><td>优化表达方式</td><td>改变你的个人风格</td></tr><tr><td>扩展相关内容</td><td>添加你没有的经验</td></tr><tr><td>翻译、总结</td><td>替代你的思考</td></tr></tbody></table></section></section><section><h3>🥂 Step 8：练内功——持续精进<a href="#-step-8练内功持续精进"><span>#</span></a></h3><p><strong>AI时代的内功修炼</strong>：</p>

<table><thead><tr><th>维度</th><th>方法</th></tr></thead><tbody><tr><td>🔷 多读</td><td>不仅读技术文章，更要读经典著作</td></tr><tr><td>🔷 多思</td><td>与AI对话时，保持独立思考</td></tr><tr><td>🔷 多写</td><td>刻意练习，形成自己的风格</td></tr><tr><td>🔷 多复盘</td><td>每篇文章写完后反思改进</td></tr><tr><td>🔷 保持人味</td><td>不要让AI磨平你的个性</td></tr></tbody></table><hr /></section></section>
<section><h2>🎯 五、总结：AI时代写作的终极心法<a href="#-五总结ai时代写作的终极心法"><span>#</span></a></h2><section><h3>核心原则<a href="#核心原则"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>AI能生成信息，但无法创造经验</span></div></div><div><div><div>2</div></div><div><span>AI能组织文字，但无法注入灵魂</span></div></div><div><div><div>3</div></div><div><span>AI能模仿风格，但无法替代个性</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>行动指南<a href="#行动指南"><span>#</span></a></h3>

<table><thead><tr><th>阶段</th><th>建议</th></tr></thead><tbody><tr><td>写作前</td><td>问自己：这篇文章有什么是AI写不出来的？</td></tr><tr><td>写作中</td><td>用AI提效，但保持自己的思考和风格</td></tr><tr><td>写作后</td><td>检查：文章是否有足够的「人味」？</td></tr></tbody></table></section><section><h3>终极心法<a href="#终极心法"><span>#</span></a></h3><blockquote><p>🍷「兵无常势，水无常形，能因敌变化而取胜者，谓之神」——《孙子兵法》</p></blockquote><p>AI时代，写作的本质没有变，变的是工具和环境。真正的好文章，永远来自于：</p><ul>
<li>真实的经验</li>
<li>独特的见解</li>
<li>真诚的表达</li>
<li>持续的精进</li>
</ul><p><strong>让AI成为你的助手，而非替代者。写出有「人味」的文章，才是AI时代的核心竞争力。</strong></p><hr /></section></section>
<section><h2>📚 参考资料<a href="#-参考资料"><span>#</span></a></h2><ul>
<li>《金字塔原理》——芭芭拉·明托</li>
<li>《结构化思维》</li>
<li>Google搜索质量评估指南（E-E-A-T原则）</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/software-architecture-refactoring-document-guide/</id>
      <title type="text">📋 软件架构重构文档编写指南：从章节设计到落地实践</title>
      <published>2024-06-07T00:00:00.000Z</published>
      <updated>2024-06-07T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/software-architecture-refactoring-document-guide/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<blockquote><p>一份优秀的架构重构文档，是项目成功的基石。本文从章节设计、内容编写到最佳实践，为你提供完整的文档编写指南。</p></blockquote>

<hr />
<section><h2>🎯 一、文档章节框架设计<a href="#-一文档章节框架设计"><span>#</span></a></h2><section><h3>1.1 标准章节结构<a href="#11-标准章节结构"><span>#</span></a></h3><p>一份完整的软件架构重构文档，应包含以下11个核心章节：</p>

<table><thead><tr><th>章节</th><th>核心内容</th><th>重要程度</th></tr></thead><tbody><tr><td>1. 引言</td><td>目的、范围、背景、动机</td><td>⭐⭐⭐</td></tr><tr><td>2. 现状分析</td><td>问题诊断、瓶颈识别、技术债务</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>3. 关键问题与挑战</td><td>核心痛点、技术难点、风险点</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>4. 目标与业务需求</td><td>重构目标、业务对齐、约束条件</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>5. 重构策略与方案</td><td>技术选型、方案对比、决策依据</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>6. 架构重构设计</td><td>详细设计、实施步骤、技术细节</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>7. 风险评估与管理</td><td>风险识别、应对策略、监控机制</td><td>⭐⭐⭐⭐</td></tr><tr><td>8. 进度规划与资源</td><td>时间表、里程碑、资源调配</td><td>⭐⭐⭐⭐</td></tr><tr><td>9. 测试与验证</td><td>测试策略、验证指标、质量保障</td><td>⭐⭐⭐⭐</td></tr><tr><td>10. 上线与验收</td><td>部署方案、验收标准、回滚预案</td><td>⭐⭐⭐⭐</td></tr><tr><td>11. 总结与展望</td><td>成果总结、经验教训、未来规划</td><td>⭐⭐⭐</td></tr></tbody></table></section><section><h3>1.2 章节依赖关系<a href="#12-章节依赖关系"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>引言 ──→ 现状分析 ──→ 关键问题 ──→ 目标需求</span></div></div><div><div><div>2</div></div><div><span><span>                                          </span></span><span>│</span></div></div><div><div><div>3</div></div><div><span><span>                                          </span></span><span>↓</span></div></div><div><div><div>4</div></div><div><span>总结 ←── 上线验收 ←── 测试验证 ←── 风险管理 ←── 策略方案 ←── 架构设计</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section>
<section><h2>📝 二、各章节详细编写指南<a href="#-二各章节详细编写指南"><span>#</span></a></h2><section><h3>2.1 引言<a href="#21-引言"><span>#</span></a></h3><p><strong>核心目的</strong>：让读者快速理解「为什么要做这件事」。</p><p><strong>必备要素</strong>：</p>

<table><thead><tr><th>要素</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>文档目的</td><td>说明文档的价值</td><td>「本文档旨在规划XX系统架构重构工作」</td></tr><tr><td>背景动机</td><td>业务背景、技术背景</td><td>「随着用户量增长10倍，现有架构已无法支撑」</td></tr><tr><td>问题概述</td><td>核心问题列表</td><td>「性能瓶颈、可扩展性不足、技术债务积累」</td></tr><tr><td>预期目标</td><td>期望达成的效果</td><td>「提升性能3倍、降低维护成本50%」</td></tr></tbody></table><p><strong>编写模板</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>## 1. 引言</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### 1.1 文档目的</span></div></div><div><div><div>4</div></div><div><span>本文档旨在......</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>### 1.2 项目背景</span></div></div><div><div><div>7</div></div><div><span><span>-</span><span> 业务背景：......</span></span></div></div><div><div><div>8</div></div><div><span><span>-</span><span> 技术背景：......</span></span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>### 1.3 核心问题</span></div></div><div><div><div>11</div></div><div><span>通过前期分析，发现现有架构存在以下问题：</span></div></div><div><div><div>12</div></div><div><span><span>1.</span><span> ......</span></span></div></div><div><div><div>13</div></div><div><span><span>2.</span><span> ......</span></span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>### 1.4 预期目标</span></div></div><div><div><div>16</div></div><div><span>本次重构期望达成：</span></div></div><div><div><div>17</div></div><div><span><span>1.</span><span> ......</span></span></div></div><div><div><div>18</div></div><div><span><span>2.</span><span> ......</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.2 现状分析<a href="#22-现状分析"><span>#</span></a></h3><p><strong>核心目的</strong>：全面诊断当前系统的问题。</p><p><strong>分析维度</strong>：</p>

<table><thead><tr><th>维度</th><th>分析要点</th><th>常用方法</th></tr></thead><tbody><tr><td>功能完整性</td><td>功能缺失、冗余功能</td><td>需求对照检查</td></tr><tr><td>性能表现</td><td>响应时间、吞吐量、资源利用率</td><td>压测、APM监控</td></tr><tr><td>架构质量</td><td>模块耦合度、代码复杂度</td><td>架构评估、代码审查</td></tr><tr><td>可维护性</td><td>代码质量、文档完整性</td><td>技术债务评估</td></tr><tr><td>可扩展性</td><td>水平扩展能力、功能扩展能力</td><td>容量规划</td></tr><tr><td>安全性</td><td>漏洞、合规性</td><td>安全审计</td></tr></tbody></table><p><strong>输出产物</strong>：</p><ul>
<li>问题清单（按优先级排序）</li>
<li>技术债务清单</li>
<li>架构评估报告</li>
</ul></section><section><h3>2.3 关键问题与挑战<a href="#23-关键问题与挑战"><span>#</span></a></h3><p><strong>核心目的</strong>：明确重构过程中需要解决的核心问题。</p><section><h4>🔧 技术挑战<a href="#-技术挑战"><span>#</span></a></h4>

<table><thead><tr><th>挑战</th><th>说明</th><th>应对思路</th></tr></thead><tbody><tr><td>系统理解</td><td>遗留系统文档缺失、逻辑复杂</td><td>代码审查、架构梳理、知识沉淀</td></tr><tr><td>技术选型</td><td>新技术栈选择、兼容性评估</td><td>POC验证、技术调研、风险评估</td></tr><tr><td>新旧兼容</td><td>平滑过渡、数据迁移</td><td>双写双读、灰度发布、回滚预案</td></tr><tr><td>性能优化</td><td>响应时间、吞吐量、资源消耗</td><td>性能基准、瓶颈定位、优化验证</td></tr><tr><td>数据迁移</td><td>数据完整性、一致性、迁移效率</td><td>数据校验、增量迁移、回滚机制</td></tr></tbody></table></section><section><h4>👥 团队挑战<a href="#-团队挑战"><span>#</span></a></h4>

<table><thead><tr><th>挑战</th><th>说明</th><th>应对思路</th></tr></thead><tbody><tr><td>协作沟通</td><td>多团队、多角色协作</td><td>建立沟通机制、明确职责边界</td></tr><tr><td>技能更新</td><td>新技术学习曲线</td><td>培训计划、结对编程、知识分享</td></tr><tr><td>文化变革</td><td>工作方式改变</td><td>变革管理、渐进式推进</td></tr></tbody></table></section><section><h4>📋 流程挑战<a href="#-流程挑战"><span>#</span></a></h4>

<table><thead><tr><th>挑战</th><th>说明</th><th>应对思路</th></tr></thead><tbody><tr><td>变更管理</td><td>大量代码变更、版本控制</td><td>分支策略、Code Review、自动化测试</td></tr><tr><td>测试验证</td><td>回归测试、性能测试</td><td>自动化测试、测试覆盖率、测试环境</td></tr><tr><td>持续集成</td><td>CI/CD流程调整</td><td>流水线优化、自动化部署</td></tr></tbody></table></section><section><h4>⚠️ 风险挑战<a href="#️-风险挑战"><span>#</span></a></h4>

<table><thead><tr><th>挑战</th><th>说明</th><th>应对思路</th></tr></thead><tbody><tr><td>成本控制</td><td>人力、时间、资源成本</td><td>预算规划、进度监控、资源优化</td></tr><tr><td>业务影响</td><td>重构期间业务连续性</td><td>灰度发布、功能开关、回滚预案</td></tr><tr><td>质量保障</td><td>新系统质量达标</td><td>质量门禁、验收标准、监控告警</td></tr></tbody></table></section></section><section><h3>2.4 目标与业务需求<a href="#24-目标与业务需求"><span>#</span></a></h3><p><strong>核心目的</strong>：确保重构目标与业务目标对齐。</p><section><h4>目标设定原则（SMART）<a href="#目标设定原则smart"><span>#</span></a></h4>

<table><thead><tr><th>原则</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>Specific</td><td>具体明确</td><td>「将API响应时间从2s降到500ms」</td></tr><tr><td>Measurable</td><td>可量化</td><td>「吞吐量提升3倍」</td></tr><tr><td>Achievable</td><td>可达成</td><td>基于现有资源和时间评估</td></tr><tr><td>Relevant</td><td>与业务相关</td><td>「支撑双十一大促」</td></tr><tr><td>Time-bound</td><td>有时间限制</td><td>「Q3完成核心模块重构」</td></tr></tbody></table></section><section><h4>目标分类<a href="#目标分类"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>## 4. 重构目标</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### 4.1 性能目标</span></div></div><div><div><div>4</div></div><div><span>| 指标 | 当前值 | 目标值 | 优先级 |</span></div></div><div><div><div>5</div></div><div><span>|------|--------|--------|--------|</span></div></div><div><div><div>6</div></div><div><span>| API响应时间 | 2s | 500ms | P0 |</span></div></div><div><div><div>7</div></div><div><span>| 系统吞吐量 | 1000 QPS | 5000 QPS | P0 |</span></div></div><div><div><div>8</div></div><div><span>| 数据库查询时间 | 500ms | 100ms | P1 |</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>### 4.2 质量目标</span></div></div><div><div><div>11</div></div><div><span>| 指标 | 当前值 | 目标值 | 优先级 |</span></div></div><div><div><div>12</div></div><div><span>|------|--------|--------|--------|</span></div></div><div><div><div>13</div></div><div><span>| 代码覆盖率 | 30% | 80% | P0 |</span></div></div><div><div><div>14</div></div><div><span>| 技术债务 | 50项 | 10项 | P1 |</span></div></div><div><div><div>15</div></div><div><span>| 文档完整性 | 40% | 90% | P1 |</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>### 4.3 业务目标</span></div></div><div><div><div>18</div></div><div><span>| 目标 | 说明 | 预期收益 |</span></div></div><div><div><div>19</div></div><div><span>|------|------|----------|</span></div></div><div><div><div>20</div></div><div><span>| 支撑用户增长 | 支持用户量10倍增长 | 业务扩展能力 |</span></div></div><div><div><div>21</div></div><div><span>| 降低运维成本 | 自动化运维、监控完善 | 运维成本降低50% |</span></div></div><div><div><div>22</div></div><div><span>| 提升开发效率 | 模块化、接口标准化 | 新功能开发效率提升30% |</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h3>2.5 重构策略与方案<a href="#25-重构策略与方案"><span>#</span></a></h3><p><strong>核心目的</strong>：提出可行的技术方案并说明决策依据。</p><section><h4>方案对比模板<a href="#方案对比模板"><span>#</span></a></h4>

<table><thead><tr><th>方案</th><th>优点</th><th>缺点</th><th>适用场景</th><th>推荐指数</th></tr></thead><tbody><tr><td>方案A</td><td>…</td><td>…</td><td>…</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>方案B</td><td>…</td><td>…</td><td>…</td><td>⭐⭐⭐</td></tr><tr><td>方案C</td><td>…</td><td>…</td><td>…</td><td>⭐⭐</td></tr></tbody></table></section><section><h4>常见重构策略<a href="#常见重构策略"><span>#</span></a></h4>

<table><thead><tr><th>策略</th><th>说明</th><th>适用场景</th></tr></thead><tbody><tr><td>绞杀者模式</td><td>新系统逐步替代旧系统</td><td>大型单体系统重构</td></tr><tr><td>分支策略</td><td>新旧系统并行运行</td><td>需要平滑过渡的场景</td></tr><tr><td>模块化拆分</td><td>按业务域拆分微服务</td><td>单体拆微服务</td></tr><tr><td>数据库拆分</td><td>分库分表、读写分离</td><td>数据量大的场景</td></tr></tbody></table></section></section><section><h3>2.6 架构重构设计<a href="#26-架构重构设计"><span>#</span></a></h3><p><strong>核心目的</strong>：详细描述重构的技术方案和实施步骤。</p><p><strong>必备内容</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>## 6. 架构重构设计</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### 6.1 目标架构</span></div></div><div><div><div>4</div></div><div><span><span>-</span><span> 架构图（AS-IS → TO-BE）</span></span></div></div><div><div><div>5</div></div><div><span><span>-</span><span> 核心组件说明</span></span></div></div><div><div><div>6</div></div><div><span><span>-</span><span> 技术栈选型</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>### 6.2 实施步骤</span></div></div><div><div><div>9</div></div><div><span>| 阶段 | 内容 | 时间 | 负责人 |</span></div></div><div><div><div>10</div></div><div><span>|------|------|------|--------|</span></div></div><div><div><div>11</div></div><div><span>| 第一阶段 | ... | ... | ... |</span></div></div><div><div><div>12</div></div><div><span>| 第二阶段 | ... | ... | ... |</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>### 6.3 关键技术细节</span></div></div><div><div><div>15</div></div><div><span><span>-</span><span> 接口设计</span></span></div></div><div><div><div>16</div></div><div><span><span>-</span><span> 数据模型</span></span></div></div><div><div><div>17</div></div><div><span><span>-</span><span> 中间件配置</span></span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>### 6.4 兼容性方案</span></div></div><div><div><div>20</div></div><div><span><span>-</span><span> 新旧接口兼容</span></span></div></div><div><div><div>21</div></div><div><span><span>-</span><span> 数据迁移方案</span></span></div></div><div><div><div>22</div></div><div><span><span>-</span><span> 回滚机制</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.7 风险评估与管理<a href="#27-风险评估与管理"><span>#</span></a></h3><p><strong>核心目的</strong>：识别风险并制定应对策略。</p><p><strong>风险矩阵模板</strong>：</p>

<table><thead><tr><th>风险</th><th>概率</th><th>影响</th><th>风险等级</th><th>应对策略</th><th>负责人</th></tr></thead><tbody><tr><td>数据迁移失败</td><td>中</td><td>高</td><td>🔴 高</td><td>增量迁移+校验+回滚</td><td>张三</td></tr><tr><td>性能不达标</td><td>低</td><td>高</td><td>🟡 中</td><td>压测验证+优化预案</td><td>李四</td></tr><tr><td>进度延期</td><td>中</td><td>中</td><td>🟡 中</td><td>预留缓冲时间+里程碑检查</td><td>王五</td></tr></tbody></table></section><section><h3>2.8 进度规划与资源<a href="#28-进度规划与资源"><span>#</span></a></h3><p><strong>核心目的</strong>：明确时间节点和资源需求。</p><p><strong>甘特图/里程碑</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>## 8. 进度规划</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### 8.1 里程碑</span></div></div><div><div><div>4</div></div><div><span>| 里程碑 | 时间 | 交付物 | 验收标准 |</span></div></div><div><div><div>5</div></div><div><span>|--------|------|--------|----------|</span></div></div><div><div><div>6</div></div><div><span>| M1: 方案评审 | 2026-04-15 | 详细设计文档 | 评审通过 |</span></div></div><div><div><div>7</div></div><div><span>| M2: 开发完成 | 2026-06-30 | 代码、测试用例 | 测试通过 |</span></div></div><div><div><div>8</div></div><div><span>| M3: 灰度发布 | 2026-07-15 | 灰度环境 | 监控正常 |</span></div></div><div><div><div>9</div></div><div><span>| M4: 全量上线 | 2026-07-30 | 生产环境 | 验收通过 |</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>### 8.2 资源需求</span></div></div><div><div><div>12</div></div><div><span>| 角色 | 人数 | 职责 |</span></div></div><div><div><div>13</div></div><div><span>|------|------|------|</span></div></div><div><div><div>14</div></div><div><span>| 架构师 | 1 | 架构设计、技术决策 |</span></div></div><div><div><div>15</div></div><div><span>| 后端开发 | 4 | 核心模块开发 |</span></div></div><div><div><div>16</div></div><div><span>| 测试工程师 | 2 | 测试用例、自动化测试 |</span></div></div><div><div><div>17</div></div><div><span>| DBA | 1 | 数据迁移、数据库优化 |</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.9 测试与验证<a href="#29-测试与验证"><span>#</span></a></h3><p><strong>核心目的</strong>：确保重构后系统质量达标。</p><p><strong>测试策略</strong>：</p>

<table><thead><tr><th>测试类型</th><th>覆盖范围</th><th>通过标准</th></tr></thead><tbody><tr><td>单元测试</td><td>核心业务逻辑</td><td>覆盖率≥80%</td></tr><tr><td>集成测试</td><td>模块间交互</td><td>全部通过</td></tr><tr><td>性能测试</td><td>关键接口</td><td>响应时间≤500ms</td></tr><tr><td>安全测试</td><td>认证授权、数据安全</td><td>无高危漏洞</td></tr><tr><td>回归测试</td><td>全量功能</td><td>无新增Bug</td></tr></tbody></table></section><section><h3>2.10 上线与验收<a href="#210-上线与验收"><span>#</span></a></h3><p><strong>核心目的</strong>：确保平滑上线、顺利验收。</p><p><strong>上线检查清单</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>## 10. 上线与验收</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### 10.1 上线前检查</span></div></div><div><div><div>4</div></div><div><span><span>-</span><span> [ ] 代码审查完成</span></span></div></div><div><div><div>5</div></div><div><span><span>-</span><span> [ ] 测试用例全部通过</span></span></div></div><div><div><div>6</div></div><div><span><span>-</span><span> [ ] 性能测试达标</span></span></div></div><div><div><div>7</div></div><div><span><span>-</span><span> [ ] 监控告警配置完成</span></span></div></div><div><div><div>8</div></div><div><span><span>-</span><span> [ ] 回滚预案准备就绪</span></span></div></div><div><div><div>9</div></div><div><span><span>-</span><span> [ ] 文档更新完成</span></span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>### 10.2 上线方案</span></div></div><div><div><div>12</div></div><div><span><span>-</span><span> 发布窗口：......</span></span></div></div><div><div><div>13</div></div><div><span><span>-</span><span> 发布顺序：......</span></span></div></div><div><div><div>14</div></div><div><span><span>-</span><span> 验证步骤：......</span></span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>### 10.3 回滚预案</span></div></div><div><div><div>17</div></div><div><span><span>-</span><span> 回滚触发条件：......</span></span></div></div><div><div><div>18</div></div><div><span><span>-</span><span> 回滚步骤：......</span></span></div></div><div><div><div>19</div></div><div><span><span>-</span><span> 回滚验证：......</span></span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>### 10.4 验收标准</span></div></div><div><div><div>22</div></div><div><span>| 验收项 | 标准 | 验收方式 |</span></div></div><div><div><div>23</div></div><div><span>|--------|------|----------|</span></div></div><div><div><div>24</div></div><div><span>| 功能完整性 | 全部功能正常 | 功能测试 |</span></div></div><div><div><div>25</div></div><div><span>| 性能指标 | 达成目标值 | 性能测试 |</span></div></div><div><div><div>26</div></div><div><span>| 稳定性 | 无P0/P1事故 | 线上监控 |</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.11 总结与展望<a href="#211-总结与展望"><span>#</span></a></h3><p><strong>核心目的</strong>：沉淀经验、规划未来。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>## 11. 总结与展望</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>### 11.1 项目成果</span></div></div><div><div><div>4</div></div><div><span><span>-</span><span> 完成情况：......</span></span></div></div><div><div><div>5</div></div><div><span><span>-</span><span> 目标达成：......</span></span></div></div><div><div><div>6</div></div><div><span><span>-</span><span> 收益评估：......</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>### 11.2 经验教训</span></div></div><div><div><div>9</div></div><div><span><span>-</span><span> 做得好的：......</span></span></div></div><div><div><div>10</div></div><div><span><span>-</span><span> 待改进的：......</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>### 11.3 后续规划</span></div></div><div><div><div>13</div></div><div><span><span>-</span><span> 短期：......</span></span></div></div><div><div><div>14</div></div><div><span><span>-</span><span> 中长期：......</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section>
<section><h2>💡 三、最佳实践<a href="#-三最佳实践"><span>#</span></a></h2><section><h3>3.1 文档编写原则<a href="#31-文档编写原则"><span>#</span></a></h3>

<table><thead><tr><th>原则</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>观点先行</td><td>开篇亮明核心观点</td><td>「本次重构的核心目标是提升性能3倍」</td></tr><tr><td>数据支撑</td><td>用数据说话</td><td>「当前响应时间2s，目标500ms」</td></tr><tr><td>结构清晰</td><td>层次分明、逻辑递进</td><td>使用目录、编号、表格</td></tr><tr><td>可执行性</td><td>方案具体、可落地</td><td>明确责任人、时间节点</td></tr></tbody></table></section><section><h3>3.2 常见误区<a href="#32-常见误区"><span>#</span></a></h3>

<table><thead><tr><th>误区</th><th>后果</th><th>正确做法</th></tr></thead><tbody><tr><td>目标模糊</td><td>方向不明确、难以验收</td><td>SMART原则设定目标</td></tr><tr><td>忽视风险</td><td>项目延期、失败</td><td>提前识别、制定预案</td></tr><tr><td>文档过时</td><td>信息不准确、失去价值</td><td>及时更新、版本管理</td></tr><tr><td>缺乏对齐</td><td>业务与技术脱节</td><td>与业务方充分沟通</td></tr></tbody></table></section><section><h3>3.3 文档模板<a href="#33-文档模板"><span>#</span></a></h3><p><strong>最小可用模板</strong>：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># XX系统架构重构方案</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>## 1. 引言</span></div></div><div><div><div>4</div></div><div><span><span>-</span><span> 背景：......</span></span></div></div><div><div><div>5</div></div><div><span><span>-</span><span> 问题：......</span></span></div></div><div><div><div>6</div></div><div><span><span>-</span><span> 目标：......</span></span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>## 2. 现状分析</span></div></div><div><div><div>9</div></div><div><span><span>-</span><span> 问题清单：......</span></span></div></div><div><div><div>10</div></div><div><span><span>-</span><span> 技术债务：......</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>## 3. 重构方案</span></div></div><div><div><div>13</div></div><div><span><span>-</span><span> 目标架构：......</span></span></div></div><div><div><div>14</div></div><div><span><span>-</span><span> 实施步骤：......</span></span></div></div><div><div><div>15</div></div><div><span><span>-</span><span> 时间规划：......</span></span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>## 4. 风险与应对</span></div></div><div><div><div>18</div></div><div><span><span>-</span><span> 风险清单：......</span></span></div></div><div><div><div>19</div></div><div><span><span>-</span><span> 应对策略：......</span></span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>## 5. 验收标准</span></div></div><div><div><div>22</div></div><div><span><span>-</span><span> 功能验收：......</span></span></div></div><div><div><div>23</div></div><div><span><span>-</span><span> 性能验收：......</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section>
<section><h2>📌 四、总结<a href="#-四总结"><span>#</span></a></h2><p>软件架构重构文档是项目成功的基石，一份优秀的文档应具备：</p>

<table><thead><tr><th>特征</th><th>说明</th></tr></thead><tbody><tr><td><strong>完整性</strong></td><td>覆盖从分析到上线的全流程</td></tr><tr><td><strong>清晰性</strong></td><td>结构清晰、逻辑递进</td></tr><tr><td><strong>可执行性</strong></td><td>方案具体、责任明确</td></tr><tr><td><strong>可追溯性</strong></td><td>版本管理、变更记录</td></tr></tbody></table><p><strong>核心要点</strong>：</p><ol>
<li>现状分析要全面，问题识别要准确</li>
<li>目标设定要SMART，与业务对齐</li>
<li>方案设计要具体，风险预案要充分</li>
<li>进度规划要合理，资源调配要到位</li>
<li>测试验证要严格，上线验收要标准</li>
</ol><hr /></section>
<section><h2>📚 参考资料<a href="#-参考资料"><span>#</span></a></h2><ul>
<li>《重构：改善既有代码的设计》—— Martin Fowler</li>
<li>《软件架构：架构模式》</li>
<li>《系统重构：方法论与实践》</li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-http-server-implementation/</id>
      <title type="text">基于libevent的http服务器实现</title>
      <published>2022-12-11T00:00:00.000Z</published>
      <updated>2022-12-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-http-server-implementation/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>基于libevent的http服务器实现<a href="#基于libevent的http服务器实现"><span>#</span></a></h1><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>//libevent的http服务器简单实现方式</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>#include</span><span> </span><span>&lt;stdio.h&gt;</span></div></div><div><div><div>4</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>5</div></div><div><span>#include</span><span> </span><span>&lt;unistd.h&gt;</span></div></div><div><div><div>6</div></div><div><span>#include</span><span> </span><span>&lt;string.h&gt;</span></div></div><div><div><div>7</div></div><div><span>//for struct evkeyvalq</span></div></div><div><div><div>8</div></div><div><span>#include</span><span> </span><span>&lt;sys/queue.h&gt;</span></div></div><div><div><div>9</div></div><div><span>#include</span><span> </span><span>&lt;event.h&gt;</span></div></div><div><div><div>10</div></div><div><span>#include</span><span> </span><span>&lt;event2/http.h&gt;</span></div></div><div><div><div>11</div></div><div><span>#include</span><span> </span><span>&lt;event2/http_struct.h&gt;</span></div></div><div><div><div>12</div></div><div><span>#include</span><span> </span><span>&lt;event2/http_compat.h&gt;</span></div></div><div><div><div>13</div></div><div><span>#include</span><span> </span><span>&lt;event2/util.h&gt;</span></div></div><div><div><div>14</div></div><div><span>#include</span><span> </span><span>&lt;signal.h&gt;</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>#define</span><span> </span><span>LOG_INFO</span><span>    </span><span>printf</span></div></div><div><div><div>17</div></div><div><span>#define</span><span> </span><span>LOG_DBG</span><span>    </span><span>printf</span></div></div><div><div><div>18</div></div><div><span>#define</span><span> </span><span>LOG_ERR</span><span>    </span><span>printf</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>#define</span><span> </span><span>MYHTTPD_SIGNATURE</span><span>   </span><span>"httpd v 0.0.1"</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>//处理模块</span></div></div><div><div><div>23</div></div><div><span>void</span><span> </span><span>httpd_handler</span><span>(</span><span>struct</span><span> </span><span>evhttp_request</span><span> </span><span>*</span><span><span>req</span><span>, </span></span><span>void</span><span> </span><span>*</span><span><span>arg</span><span>) {</span></span></div></div><div><div><div>24</div></div><div><span>    </span><span>char</span><span> </span><span>output</span><span>[</span><span>2048</span><span>] </span><span>=</span><span> </span><span>"</span><span>\0</span><span>"</span><span>;</span></div></div><div><div><div>25</div></div><div><span>    </span><span>char</span><span> </span><span>tmp</span><span>[</span><span>1024</span><span>];</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>//获取客户端请求的URI(使用evhttp_request_uri或直接req-&gt;uri)</span></div></div><div><div><div>28</div></div><div><span>    </span><span>const</span><span> </span><span>char</span><span> </span><span>*</span><span>uri</span><span>;</span></div></div><div><div><div>29</div></div><div><span>    </span><span>uri</span><span> </span><span>=</span><span> </span><span>evhttp_request_uri</span><span>(</span><span>req</span><span>);</span></div></div><div><div><div>30</div></div><div><span>    </span><span>sprintf</span><span>(</span><span>tmp</span><span>, </span><span>"uri=</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>uri</span><span>);</span></div></div><div><div><div>31</div></div><div><span>    </span><span>strcat</span><span>(</span><span>output</span><span>, </span><span>tmp</span><span>);</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>sprintf</span><span>(</span><span>tmp</span><span>, </span><span>"uri=</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>req</span><span>-&gt;</span><span>uri</span><span>);</span></div></div><div><div><div>34</div></div><div><span>    </span><span>strcat</span><span>(</span><span>output</span><span>, </span><span>tmp</span><span>);</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>//decoded uri</span></div></div><div><div><div>36</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>decoded_uri</span><span>;</span></div></div><div><div><div>37</div></div><div><span>    </span><span>decoded_uri</span><span> </span><span>=</span><span> </span><span>evhttp_decode_uri</span><span>(</span><span>uri</span><span>);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>sprintf</span><span>(</span><span>tmp</span><span>, </span><span>"decoded_uri=</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>decoded_uri</span><span>);</span></div></div><div><div><div>39</div></div><div><span>    </span><span>strcat</span><span>(</span><span>output</span><span>, </span><span>tmp</span><span>);</span></div></div><div><div><div>40</div></div><div>
</div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>//解析URI的参数(即GET方法的参数)</span></div></div><div><div><div>42</div></div><div><span>    </span><span>struct</span><span> </span><span>evkeyvalq</span><span> </span><span>params</span><span>;</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>//将URL数据封装成key-value格式,q=value1, s=value2</span></div></div><div><div><div>44</div></div><div><span>    </span><span>evhttp_parse_query</span><span>(</span><span>decoded_uri</span><span>, </span><span>&amp;</span><span>params</span><span>);</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>//得到q所对应的value</span></div></div><div><div><div>46</div></div><div><span>    </span><span>sprintf</span><span>(</span><span>tmp</span><span>, </span><span>"q=</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evhttp_find_header</span><span>(</span><span>&amp;</span><span>params</span><span>, </span><span>"q"</span><span>));</span></div></div><div><div><div>47</div></div><div><span>    </span><span>strcat</span><span>(</span><span>output</span><span>, </span><span>tmp</span><span>);</span></div></div><div><div><div>48</div></div><div><span><span>    </span></span><span>//得到s所对应的value</span></div></div><div><div><div>49</div></div><div><span>    </span><span>sprintf</span><span>(</span><span>tmp</span><span>, </span><span>"s=</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>evhttp_find_header</span><span>(</span><span>&amp;</span><span>params</span><span>, </span><span>"s"</span><span>));</span></div></div><div><div><div>50</div></div><div><span>    </span><span>strcat</span><span>(</span><span>output</span><span>, </span><span>tmp</span><span>);</span></div></div><div><div><div>51</div></div><div>
</div></div><div><div><div>52</div></div><div><span>    </span><span>free</span><span>(</span><span>decoded_uri</span><span>);</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>//获取POST方法的数据</span></div></div><div><div><div>55</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>post_data</span><span> </span><span>=</span><span> (</span><span>char</span><span> </span><span>*</span><span>) </span><span>EVBUFFER_DATA</span><span>(</span><span>req</span><span>-&gt;</span><span>input_buffer</span><span>);</span></div></div><div><div><div>56</div></div><div><span>    </span><span>sprintf</span><span>(</span><span>tmp</span><span>, </span><span>"post_data=</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>post_data</span><span>);</span></div></div><div><div><div>57</div></div><div><span>    </span><span>strcat</span><span>(</span><span>output</span><span>, </span><span>tmp</span><span>);</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>/* 输出到客户端 */</span></div></div><div><div><div>60</div></div><div>
</div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>//HTTP header</span></div></div><div><div><div>62</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>req</span><span>-&gt;</span><span>output_headers</span><span>, </span><span>"Server"</span><span>, </span><span>MYHTTPD_SIGNATURE</span><span>);</span></div></div><div><div><div>63</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>req</span><span>-&gt;</span><span>output_headers</span><span>, </span><span>"Content-Type"</span><span>, </span><span>"text/plain; charset=UTF-8"</span><span>);</span></div></div><div><div><div>64</div></div><div><span>    </span><span>evhttp_add_header</span><span>(</span><span>req</span><span>-&gt;</span><span>output_headers</span><span>, </span><span>"Connection"</span><span>, </span><span>"close"</span><span>);</span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>//输出的内容</span></div></div><div><div><div>66</div></div><div><span>    </span><span>struct</span><span> </span><span>evbuffer</span><span> </span><span>*</span><span>buf</span><span>;</span></div></div><div><div><div>67</div></div><div><span>    </span><span>buf</span><span> </span><span>=</span><span> </span><span>evbuffer_new</span><span>();</span></div></div><div><div><div>68</div></div><div><span>    </span><span>evbuffer_add_printf</span><span>(</span><span>buf</span><span>, </span><span>"It works!</span><span>\n</span><span>%s</span><span>\n</span><span>"</span><span>, </span><span>output</span><span>);</span></div></div><div><div><div>69</div></div><div><span>    </span><span>evhttp_send_reply</span><span>(</span><span>req</span><span>, </span><span>HTTP_OK</span><span>, </span><span>"OK"</span><span>, </span><span>buf</span><span>);</span></div></div><div><div><div>70</div></div><div><span>    </span><span>evbuffer_free</span><span>(</span><span>buf</span><span>);</span></div></div><div><div><div>71</div></div><div>
</div></div><div><div><div>72</div></div><div><span>}</span></div></div><div><div><div>73</div></div><div><span>void</span><span> </span><span>show_help</span><span>() {</span></div></div><div><div><div>74</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>help</span><span> </span><span>=</span><span> </span><span>"http://localhost:8080</span><span>\n</span><span>"</span></div></div><div><div><div>75</div></div><div><span>        </span><span>"-l &lt;ip_addr&gt; interface to listen on, default is 0.0.0.0</span><span>\n</span><span>"</span></div></div><div><div><div>76</div></div><div><span>        </span><span>"-p &lt;num&gt;     port number to listen on, default is 1984</span><span>\n</span><span>"</span></div></div><div><div><div>77</div></div><div><span>        </span><span>"-d           run as a deamon</span><span>\n</span><span>"</span></div></div><div><div><div>78</div></div><div><span>        </span><span>"-t &lt;second&gt;  timeout for a http request, default is 120 seconds</span><span>\n</span><span>"</span></div></div><div><div><div>79</div></div><div><span>        </span><span>"-h           print this help and exit</span><span>\n</span><span>"</span></div></div><div><div><div>80</div></div><div><span>        </span><span>"</span><span>\n</span><span>"</span><span>;</span></div></div><div><div><div>81</div></div><div><span>    </span><span>fprintf</span><span>(</span><span>stderr</span><span>,</span><span>"</span><span>%s</span><span>"</span><span>,</span><span>help</span><span>);</span></div></div><div><div><div>82</div></div><div><span>}</span></div></div><div><div><div>83</div></div><div><span>//当向进程发出SIGTERM/SIGHUP/SIGINT/SIGQUIT的时候，终止event的事件侦听循环</span></div></div><div><div><div>84</div></div><div><span>void</span><span> </span><span>signal_handler</span><span>(</span><span>int</span><span><span> </span><span>sig</span><span>) {</span></span></div></div><div><div><div>85</div></div><div><span>    </span><span>switch</span><span> (</span><span>sig</span><span>) {</span></div></div><div><div><div>86</div></div><div><span>        </span><span>case</span><span> </span><span>SIGTERM</span><span>:</span></div></div><div><div><div>87</div></div><div><span>        </span><span>case</span><span> </span><span>SIGHUP</span><span>:</span></div></div><div><div><div>88</div></div><div><span>        </span><span>case</span><span> </span><span>SIGQUIT</span><span>:</span></div></div><div><div><div>89</div></div><div><span>        </span><span>case</span><span> </span><span>SIGINT</span><span>:</span></div></div><div><div><div>90</div></div><div><span>            </span><span>event_loopbreak</span><span>();</span><span>  //终止侦听event_dispatch()的事件侦听循环，执行之后的代码</span></div></div><div><div><div>91</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>92</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>93</div></div><div><span>}</span></div></div><div><div><div>94</div></div><div>
</div></div><div><div><div>95</div></div><div><span>int</span><span> </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>, </span></span><span>char</span><span> </span><span>*</span><span><span>argv</span><span>[]) {</span></span></div></div><div><div><div>96</div></div><div>
</div></div><div><div><div>97</div></div><div><span><span>    </span></span><span>//自定义信号处理函数</span></div></div><div><div><div>98</div></div><div><span>    </span><span>signal</span><span>(</span><span>SIGHUP</span><span>   , </span><span>signal_handler</span><span>);</span></div></div><div><div><div>99</div></div><div><span>    </span><span>signal</span><span>(</span><span>SIGTERM</span><span>  , </span><span>signal_handler</span><span>);</span></div></div><div><div><div>100</div></div><div><span>    </span><span>signal</span><span>(</span><span>SIGINT</span><span>   , </span><span>signal_handler</span><span>);</span></div></div><div><div><div>101</div></div><div><span>    </span><span>signal</span><span>(</span><span>SIGQUIT</span><span>  , </span><span>signal_handler</span><span>);</span></div></div><div><div><div>102</div></div><div>
</div></div><div><div><div>103</div></div><div><span><span>    </span></span><span>//默认参数</span></div></div><div><div><div>104</div></div><div><span>    </span><span>char</span><span> </span><span>*</span><span>httpd_option_listen</span><span> </span><span>=</span><span> </span><span>"0.0.0.0"</span><span>;</span></div></div><div><div><div>105</div></div><div><span>    </span><span>int</span><span> </span><span>httpd_option_port</span><span> </span><span>=</span><span> </span><span>8080</span><span>;</span></div></div><div><div><div>106</div></div><div><span>    </span><span>int</span><span> </span><span>httpd_option_daemon</span><span> </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>107</div></div><div><span>    </span><span>int</span><span> </span><span>httpd_option_timeout</span><span> </span><span>=</span><span> </span><span>120</span><span>;</span><span> //in seconds</span></div></div><div><div><div>108</div></div><div>
</div></div><div><div><div>109</div></div><div><span>    </span><span>LOG_INFO</span><span>(</span><span>"http server start </span><span>%s</span><span>:</span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>httpd_option_listen</span><span>, </span><span>httpd_option_port</span><span>);</span></div></div><div><div><div>110</div></div><div>
</div></div><div><div><div>111</div></div><div><span><span>    </span></span><span>//获取参数</span></div></div><div><div><div>112</div></div><div><span>    </span><span>int</span><span> </span><span>c</span><span>;</span></div></div><div><div><div>113</div></div><div><span>    </span><span>while</span><span> ((</span><span>c</span><span> </span><span>=</span><span> </span><span>getopt</span><span>(</span><span>argc</span><span>, </span><span>argv</span><span>, </span><span>"l:p:dt:h"</span><span>)) </span><span>!=</span><span> </span><span>-</span><span>1</span><span>) {</span></div></div><div><div><div>114</div></div><div><span>        </span><span>switch</span><span> (</span><span>c</span><span>) {</span></div></div><div><div><div>115</div></div><div><span>            </span><span>case</span><span> </span><span>'l'</span><span> :</span></div></div><div><div><div>116</div></div><div><span>                </span><span>httpd_option_listen</span><span> </span><span>=</span><span> </span><span>optarg</span><span>;</span></div></div><div><div><div>117</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>118</div></div><div><span>            </span><span>case</span><span> </span><span>'p'</span><span> :</span></div></div><div><div><div>119</div></div><div><span>                </span><span>httpd_option_port</span><span> </span><span>=</span><span> </span><span>atoi</span><span>(</span><span>optarg</span><span>);</span></div></div><div><div><div>120</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>121</div></div><div><span>            </span><span>case</span><span> </span><span>'d'</span><span> :</span></div></div><div><div><div>122</div></div><div><span>                </span><span>httpd_option_daemon</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>123</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>124</div></div><div><span>            </span><span>case</span><span> </span><span>'t'</span><span> :</span></div></div><div><div><div>125</div></div><div><span>                </span><span>httpd_option_timeout</span><span> </span><span>=</span><span> </span><span>atoi</span><span>(</span><span>optarg</span><span>);</span></div></div><div><div><div>126</div></div><div><span>                </span><span>break</span><span>;</span></div></div><div><div><div>127</div></div><div><span>            </span><span>case</span><span> </span><span>'h'</span><span> :</span></div></div><div><div><div>128</div></div><div><span>            </span><span>default</span><span> :</span></div></div><div><div><div>129</div></div><div><span>                </span><span>show_help</span><span>();</span></div></div><div><div><div>130</div></div><div><span>                </span><span>exit</span><span>(</span><span>EXIT_SUCCESS</span><span>);</span></div></div><div><div><div>131</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>132</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>133</div></div><div>
</div></div><div><div><div>134</div></div><div><span><span>    </span></span><span>//判断是否设置了-d，以daemon运行</span></div></div><div><div><div>135</div></div><div><span>    </span><span>if</span><span> (</span><span>httpd_option_daemon</span><span>) {</span></div></div><div><div><div>136</div></div><div><span>        </span><span>pid_t</span><span> </span><span>pid</span><span>;</span></div></div><div><div><div>137</div></div><div><span>        </span><span>pid</span><span> </span><span>=</span><span> </span><span>fork</span><span>();</span></div></div><div><div><div>138</div></div><div><span>        </span><span>if</span><span> (</span><span>pid</span><span> </span><span>&lt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>139</div></div><div><span>            </span><span>perror</span><span>(</span><span>"fork failed"</span><span>);</span></div></div><div><div><div>140</div></div><div><span>            </span><span>exit</span><span>(</span><span>EXIT_FAILURE</span><span>);</span></div></div><div><div><div>141</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>142</div></div><div><span>        </span><span>if</span><span> (</span><span>pid</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>143</div></div><div><span><span>            </span></span><span>//生成子进程成功，退出父进程</span></div></div><div><div><div>144</div></div><div><span>            </span><span>exit</span><span>(</span><span>EXIT_SUCCESS</span><span>);</span></div></div><div><div><div>145</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>146</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>147</div></div><div>
</div></div><div><div><div>148</div></div><div><span><span>    </span></span><span>/* 使用libevent创建HTTP Server */</span></div></div><div><div><div>149</div></div><div>
</div></div><div><div><div>150</div></div><div><span><span>    </span></span><span>//初始化event API</span></div></div><div><div><div>151</div></div><div><span>    </span><span>event_init</span><span>();</span></div></div><div><div><div>152</div></div><div>
</div></div><div><div><div>153</div></div><div><span><span>    </span></span><span>//创建一个http server</span></div></div><div><div><div>154</div></div><div><span>    </span><span>struct</span><span> </span><span>evhttp</span><span> </span><span>*</span><span>httpd</span><span>;</span></div></div><div><div><div>155</div></div><div><span>    </span><span>httpd</span><span> </span><span>=</span><span> </span><span>evhttp_start</span><span>(</span><span>httpd_option_listen</span><span>, </span><span>httpd_option_port</span><span>);</span></div></div><div><div><div>156</div></div><div><span>    </span><span>evhttp_set_timeout</span><span>(</span><span>httpd</span><span>, </span><span>httpd_option_timeout</span><span>);</span></div></div><div><div><div>157</div></div><div>
</div></div><div><div><div>158</div></div><div><span><span>    </span></span><span>//指定generic callback</span></div></div><div><div><div>159</div></div><div><span>    </span><span>evhttp_set_gencb</span><span>(</span><span>httpd</span><span>, </span><span>httpd_handler</span><span>, </span><span>NULL</span><span>);</span></div></div><div><div><div>160</div></div><div><span><span>    </span></span><span>//也可以为特定的URI指定callback</span></div></div><div><div><div>161</div></div><div><span><span>    </span></span><span>//evhttp_set_cb(httpd, "/", specific_handler, NULL);</span></div></div><div><div><div>162</div></div><div>
</div></div><div><div><div>163</div></div><div><span><span>    </span></span><span>//开始事件监听，分发</span></div></div><div><div><div>164</div></div><div><span>    </span><span>event_dispatch</span><span>();</span></div></div><div><div><div>165</div></div><div>
</div></div><div><div><div>166</div></div><div><span><span>    </span></span><span>//释放资源</span></div></div><div><div><div>167</div></div><div><span>    </span><span>evhttp_free</span><span>(</span><span>httpd</span><span>);</span></div></div><div><div><div>168</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>169</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://xutopia.top/blog/libevent-multi-thread-http-server/</id>
      <title type="text">🖥️ 基于libevent的多线程HTTP服务器实现</title>
      <published>2022-12-11T00:00:00.000Z</published>
      <updated>2022-12-11T00:00:00.000Z</updated>
      <author><name>xutopia77</name></author>
      <link rel="alternate" href="https://xutopia.top/blog/libevent-multi-thread-http-server/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>项目需要实现一个HTTP/1.0版本的Web服务器，支持GET、POST方法，运行在嵌入式Linux系统中，并且需要处理大量并发请求。综合考虑后，选择使用libevent实现多线程HTTP服务器。</p>
<p>本文基于libevent-2.1.12-stable版本，从多线程使用注意事项到完整实现，手把手教你搭建高性能HTTP服务器。</p>

<section><h2>📖 一、libevent简介<a href="#-一libevent简介"><span>#</span></a></h2><section><h3>🎯 1.1 什么是libevent<a href="#-11-什么是libevent"><span>#</span></a></h3><p>官方介绍：</p><blockquote><p>The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts.</p><p>libevent API提供了一种机制，可以在文件描述符上发生特定事件或超时后执行回调函数。此外，libevent还支持由于信号或常规超时而产生的回调。</p></blockquote><p>百度百科介绍：</p><blockquote><p>Libevent是一个用C语言编写的、轻量级的开源高性能事件通知库，主要有以下几个亮点：事件驱动（event-driven），高性能；轻量级，专注于网络；源代码相当精炼、易读；跨平台，支持Windows、Linux、*BSD和Mac OS；支持多种I/O多路复用技术，epoll、poll、dev/poll、select和kqueue等；支持I/O，定时器和信号等事件；注册事件优先级。</p></blockquote><p><strong>简单来说</strong>：libevent是一个网络库，能够帮我们处理大量的网络编程细节，降低网络编程门槛，并具备良好的跨平台能力。使用者只需要向它注册事件，它就会在合适的时机（可读、可写或定时事件触发时）调用对应的回调函数。</p></section><section><h3>🏗️ 1.2 libevent核心组件<a href="#️-12-libevent核心组件"><span>#</span></a></h3>

<table><thead><tr><th>组件</th><th>说明</th></tr></thead><tbody><tr><td>事件管理</td><td>各种IO（socket）、定时器、信号等事件</td></tr><tr><td>缓存管理</td><td>evbuffer功能</td></tr><tr><td>DNS</td><td>异步DNS查询功能</td></tr><tr><td>HTTP</td><td>轻量级HTTP实现，包括服务器和客户端</td></tr><tr><td>缓存事件</td><td>缓存事件处理</td></tr></tbody></table><p>本文要实现的HTTP服务器，就是使用HTTP组件。需要注意的是，libevent虽然支持SSL，但HTTP Server的实现并不完善支持SSL。</p></section><section><h3>📚 1.3 前置知识<a href="#-13-前置知识"><span>#</span></a></h3><p>阅读本文前，建议先了解：</p><ul>
<li>IO多路复用器（尤其是epoll）</li>
<li>Reactor和Proactor模式</li>
</ul><p>参考资料：<a href="https://zhuanlan.zhihu.com/p/457920283" target="_blank">select，poll，epoll的区别以及使用方法</a></p><p>单线程版本实现：<a href="https://zhuanlan.zhihu.com/p/458189076" target="_blank">基于libevent的http服务器实现</a></p><hr /></section></section>
<section><h2>⚠️ 二、libevent多线程使用注意事项<a href="#️-二libevent多线程使用注意事项"><span>#</span></a></h2><p>关于libevent是否支持多线程，很多资料有争议。关键在于<strong>多线程是怎么使用的，是怎么与libevent结合的</strong>。</p><section><h3>✅ 2.1 支持的使用方式<a href="#-21-支持的使用方式"><span>#</span></a></h3><p><strong>不同线程使用不同的base</strong>：每个线程拥有独立的event_base，事件注册到各自线程的base上。这是推荐的多线程使用方式。</p></section><section><h3>❌ 2.2 不支持的使用方式<a href="#-22-不支持的使用方式"><span>#</span></a></h3>

<table><thead><tr><th>场景</th><th>原因</th></tr></thead><tbody><tr><td>信号事件多线程</td><td>内部使用了全局变量，不支持多线程</td></tr><tr><td>不同线程共享同一个base</td><td>即使加锁也不行，事件触发后可能被自动删除，导致并发问题</td></tr></tbody></table></section><section><h3>💡 2.3 核心结论<a href="#-23-核心结论"><span>#</span></a></h3><blockquote><p><strong>如果想要使用多线程，就需要每个线程中对应一个base，将线程里的事件注册到线程的base上。</strong></p></blockquote><hr /></section></section>
<section><h2>🛠️ 三、多线程HTTP服务器实现<a href="#️-三多线程http服务器实现"><span>#</span></a></h2><section><h3>📝 3.1 启动多线程HTTP服务<a href="#-31-启动多线程http服务"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>nfd</span><span> </span><span>=</span><span> </span><span>BindSocket</span><span>(</span><span>port</span><span>);</span><span>  // 监听端口</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>for</span><span> (</span><span>int</span><span> </span><span>i</span><span> </span><span>=</span><span> </span><span>0</span><span>; </span><span>i</span><span> </span><span>&lt;</span><span> </span><span>nthreads</span><span>; </span><span>i</span><span>++</span><span>)</span></div></div><div><div><div>4</div></div><div><span>{</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>// 每个线程独有的环境参数，不使用局部变量，防止离开局部作用域后失效</span></div></div><div><div><div>6</div></div><div><span>    </span><span>WorkRoom</span><span>*</span><span> </span><span>w</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>WorkRoom</span><span>;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>// libevent使用多线程，每个线程中使用一个base</span></div></div><div><div><div>9</div></div><div><span>    </span><span>w</span><span>-&gt;</span><span>base</span><span> </span><span>=</span><span> </span><span>event_init</span><span>();</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>// 初始化http</span></div></div><div><div><div>12</div></div><div><span>    </span><span>w</span><span>-&gt;</span><span>httpd</span><span> </span><span>=</span><span> </span><span>evhttp_new</span><span>(</span><span>w</span><span>-&gt;</span><span>base</span><span>);</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>// 接受连接</span></div></div><div><div><div>15</div></div><div><span>    </span><span>r</span><span> </span><span>=</span><span> </span><span>evhttp_accept_socket</span><span>(</span><span>w</span><span>-&gt;</span><span>httpd</span><span>, </span><span>nfd</span><span>);</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>// 设置http路由 404</span></div></div><div><div><div>18</div></div><div><span>    </span><span>evhttp_set_gencb</span><span>(</span><span>w</span><span>-&gt;</span><span>httpd</span><span>, </span><span>GenericHandler</span><span>, </span><span>this</span><span>);</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>// 设置http路由</span></div></div><div><div><div>21</div></div><div><span>    </span><span>evhttp_set_cb</span><span>(</span><span>w</span><span>-&gt;</span><span>httpd</span><span>, </span><span>"/test"</span><span>, </span><span>ProcessRequest</span><span>, </span><span>this</span><span>);</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>// 每个base设置一个看门狗，可以做一些线程监控的工作</span></div></div><div><div><div>24</div></div><div><span>    </span><span>event</span><span>*</span><span> </span><span>watchdogEv</span><span> </span><span>=</span><span> </span><span>event_new</span><span>(</span><span>w</span><span>-&gt;</span><span>base</span><span>, </span><span>-</span><span>1</span><span>, </span><span>EV_PERSIST</span><span>|</span><span>EV_TIMEOUT</span><span>, </span><span>watchdog</span><span>, </span><span>w</span><span>);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>event_add</span><span>(</span><span>watchdogEv</span><span>, </span><span>&amp;</span><span>tv</span><span>);</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>🚀 3.2 开启事件分发线程<a href="#-32-开启事件分发线程"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event_base_dispatch</span><span>(</span><span>w</span><span>-&gt;</span><span>base</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>🛑 3.3 停止事件分发循环<a href="#-33-停止事件分发循环"><span>#</span></a></h3><p>在看门狗中关闭事件分发循环：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> </span><span>timeval</span><span> </span><span>delay</span><span> </span><span>=</span><span> { </span><span>0</span><span>, </span><span>1</span><span> };</span></div></div><div><div><div>2</div></div><div><span>event_base_loopexit</span><span>(</span><span>w</span><span>-&gt;</span><span>base</span><span>, </span><span>&amp;</span><span>delay</span><span>);</span><span>  // 可设置延迟，参数为空则立即退出</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>🧹 3.4 资源释放<a href="#-34-资源释放"><span>#</span></a></h3><p>HTTP服务器停止时，需要注意资源释放，避免内存泄漏：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>event_free</span><span>(</span><span>w</span><span>-&gt;</span><span>watchdogEv</span><span>);</span></div></div><div><div><div>2</div></div><div><span>evhttp_free</span><span>(</span><span>w</span><span>-&gt;</span><span>httpd</span><span>);</span></div></div><div><div><div>3</div></div><div><span>event_base_free</span><span>(</span><span>w</span><span>-&gt;</span><span>base</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section>
<section><h2>📋 四、完整使用示例<a href="#-四完整使用示例"><span>#</span></a></h2><section><h3>💻 4.1 程序Demo<a href="#-41-程序demo"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>servers::</span><span>HttpSrv</span><span> </span><span>s</span><span>;</span></div></div><div><div><div>2</div></div><div><span>s</span><span>.</span><span>start</span><span>(</span><span>8080</span><span>, </span><span>5</span><span>);</span><span>  // 监听8080端口，启动5个线程</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>sleep</span><span>(</span><span>10</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>LOG_INFO</span><span>(</span><span>"http to stop</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>7</div></div><div><span>s</span><span>.</span><span>stop</span><span>();</span><span>  // 停止服务，释放所有资源</span></div></div><div><div><div>8</div></div><div><span>LOG_INFO</span><span>(</span><span>"http stop</span><span>\n</span><span>"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>📊 4.2 运行结果<a href="#-42-运行结果"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>dispatch</span><span> </span><span>start</span></div></div><div><div><div>2</div></div><div><span>dispatch</span><span> </span><span>start</span></div></div><div><div><div>3</div></div><div><span>dispatch</span><span> </span><span>start</span></div></div><div><div><div>4</div></div><div><span>dispatch</span><span> </span><span>start</span></div></div><div><div><div>5</div></div><div><span>dispatch</span><span> </span><span>start</span></div></div><div><div><div>6</div></div><div><span>http</span><span> </span><span>start</span><span> </span><span>port:8080,</span><span> </span><span>thNum:5</span></div></div><div><div><div>7</div></div><div><span>http</span><span> </span><span>to</span><span> </span><span>stop</span></div></div><div><div><div>8</div></div><div><span>dispatch</span><span> </span><span>stop</span></div></div><div><div><div>9</div></div><div><span>dispatch</span><span> </span><span>stop</span></div></div><div><div><div>10</div></div><div><span>dispatch</span><span> </span><span>stop</span></div></div><div><div><div>11</div></div><div><span>dispatch</span><span> </span><span>stop</span></div></div><div><div><div>12</div></div><div><span>dispatch</span><span> </span><span>stop</span></div></div><div><div><div>13</div></div><div><span>http</span><span> </span><span>stop</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>🔧 4.3 Postman请求测试<a href="#-43-postman请求测试"><span>#</span></a></h3><p><strong>请求</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>GET</span><span> http://192.168.31.106:8080/test</span></div></div><div><div><div>2</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>PostmanRuntime/7.28.4</span></div></div><div><div><div>3</div></div><div><span>Accept</span><span>:</span><span> </span><span>*/*</span></div></div><div><div><div>4</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.31.106:8080</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>响应</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>HTTP</span><span>/</span><span>1.1</span><span> </span><span>200</span><span> </span><span>OK</span></div></div><div><div><div>2</div></div><div><span>Date</span><span>:</span><span> </span><span>Sat, 15 Jan 2022 10:16:26 GMT</span></div></div><div><div><div>3</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>17</span></div></div><div><div><div>4</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>text/html; charset=ISO-8859-1</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>Requested</span><span>:</span><span> /test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><hr /></section></section>
<section><h2>📌 五、总结<a href="#-五总结"><span>#</span></a></h2><p>本文介绍了基于libevent实现多线程HTTP服务器的完整流程：</p>

<table><thead><tr><th>要点</th><th>说明</th></tr></thead><tbody><tr><td>多线程核心</td><td>每个线程对应一个独立的event_base</td></tr><tr><td>启动流程</td><td>BindSocket → 创建WorkRoom → event_init → evhttp_new → 设置路由</td></tr><tr><td>停止流程</td><td>event_base_loopexit → 资源释放</td></tr><tr><td>注意事项</td><td>信号事件不支持多线程，禁止共享base</td></tr></tbody></table><p>libevent是一个非常优秀的跨平台网络库，通过学习它，可以深入理解网络库的设计思想。虽然短时间内难以掌握所有细节，但掌握基本的使用方法已经能够应对大多数场景。</p><blockquote><p><strong>源代码获取</strong>：关注公众号 <strong>xutopia77</strong>，回复「<strong>基于libevent的多线程http服务器</strong>」获取完整源码。</p></blockquote></section>]]></content>
    </entry>
</feed>
