<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>TouchSocket Blog</title>
        <link>https://touchsocket.net/blog</link>
        <description>TouchSocket Blog</description>
        <lastBuildDate>Sat, 04 Apr 2026 05:36:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh</language>
        <item>
            <title><![CDATA[创建一个高可用Tcp服务器]]></title>
            <link>https://touchsocket.net/blog/CreatehighlyavailableTcpService</link>
            <guid>https://touchsocket.net/blog/CreatehighlyavailableTcpService</guid>
            <pubDate>Sat, 04 Apr 2026 05:36:17 GMT</pubDate>
            <description><![CDATA[一、引言]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="一引言">一、引言<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E4%B8%80%E5%BC%95%E8%A8%80" class="hash-link" aria-label="一、引言的直接链接" title="一、引言的直接链接" translate="no">​</a></h2>
<p>在当今万物互联的时代，物联网（IoT）设备的数量正以一种让程序员们头皮发麻的速度增长。这些设备每天通过网络传输的数据量，大概可以把一个普通开发者的大脑硬盘塞到格式化重装的程度。</p>
<p>而在这个"一切皆联网"的洪流中，TCP 服务器扮演着承上启下的核心角色——它既要接受来自四面八方、操着各种稀奇古怪协议方言的设备连接，又要把这些数据可靠地处理并转发出去。说白了，它就是那个<strong>全能保姆</strong>，还不能休假。</p>
<p>作为 TouchSocket 网络通信框架的作者，我经常在各种群里看到这样的场景：</p>
<blockquote>
<p>某开发者：为什么我的 TCP 服务器运行一段时间就崩了？</p>
<p>我：你的服务器是怎么创建的？</p>
<p>某开发者：<code>new TcpService()</code> 啊，哪里有问题？</p>
</blockquote>
<p>这就好比你找了一个世界顶级主厨，然后让他站在路边摆摊煎饼果子——不是不行，但总感觉哪里不对劲。</p>
<p>TCP 服务器不是一个随手创建、用完即弃的玩具。它需要精心设计的架构、合理的配置管理、优雅的插件化扩展，以及——最重要的——<strong>要能活得久</strong>。</p>
<p>所以，我决定写这篇博客，手把手带你构建一个真正<strong>高可用</strong>的 TCP 服务器。用通俗的话说——就是那种即使凌晨 3 点宕机了，你的手机也不会被运维的电话打爆的服务器。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="二技术细节">二、技术细节<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E4%BA%8C%E6%8A%80%E6%9C%AF%E7%BB%86%E8%8A%82" class="hash-link" aria-label="二、技术细节的直接链接" title="二、技术细节的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="21-技术框架">2.1 技术框架<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#21-%E6%8A%80%E6%9C%AF%E6%A1%86%E6%9E%B6" class="hash-link" aria-label="2.1 技术框架的直接链接" title="2.1 技术框架的直接链接" translate="no">​</a></h3>
<p>本文使用微软提供的<strong>通用主机（Generic Host）</strong> 进行构建，这套体系就像是给你的应用装了一套 PM（进程管理）+ 配置中心 + IOC 容器的豪华套餐，支持：</p>
<ul>
<li class="">跨平台（Windows、Linux、macOS，就差运行在烤面包机上了）</li>
<li class="">Windows 服务（让服务器在后台默默干活，不打扰你工作摸鱼）</li>
<li class="">IIS 托管</li>
<li class="">Options 选项配置（告别硬编码端口，yyds）</li>
<li class="">插件化加载（像乐高一样搭积木）</li>
<li class="">IOC 容器（依赖注入，让代码松耦合不粘手）</li>
<li class="">Native AOT（让你的 exe 小到让同事以为你在传病毒）</li>
</ul>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="22-框架版本">2.2 框架版本<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#22-%E6%A1%86%E6%9E%B6%E7%89%88%E6%9C%AC" class="hash-link" aria-label="2.2 框架版本的直接链接" title="2.2 框架版本的直接链接" translate="no">​</a></h3>
<p>支持：</p>
<ul>
<li class="">.NET Framework 4.6.2 及以上（是的，古老的 462 也没被抛弃）</li>
<li class="">.NET 6 及以上</li>
</ul>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="23-实现功能">2.3 实现功能<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#23-%E5%AE%9E%E7%8E%B0%E5%8A%9F%E8%83%BD" class="hash-link" aria-label="2.3 实现功能的直接链接" title="2.3 实现功能的直接链接" translate="no">​</a></h3>
<p>本示例将实现以下功能（画的饼，本文全部会兑现）：</p>
<ul>
<li class="">接收、发送数据</li>
<li class="">消息单次响应</li>
<li class="">消息广播（群发，比你微信群艾特所有人还快）</li>
<li class="">数据库持久化（把数据存起来，不然断电就白干了）</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="三项目结构设计">三、项目结构设计<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E4%B8%89%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="三、项目结构设计的直接链接" title="三、项目结构设计的直接链接" translate="no">​</a></h2>
<p>本文示例使用 .NET 10.0 构建，拆分为 <strong>5 个项目</strong>，如果你觉得这样太麻烦，可以先把所有代码塞在一个文件里……不过等你的代码行数超过 3000 行时，你会来感谢我的。</p>
<p>也支持完整的 Native AOT。</p>
<p>项目结构如下：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">HighlyAvailableTcpService.App                  ← 主程序，启动入口</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">HighlyAvailableTcpService.Core                 ← 核心库，定义接口和 Options</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">HighlyAvailableTcpService.Plugins              ← 插件层，业务逻辑的家</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">HighlyAvailableTcpService.DataHandlingAdapters ← 数据适配器，数据解包的地方</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">HighlyAvailableTcpService.Db                   ← 数据库层，数据的最终归宿</span><br></div></code></pre></div></div>
<p>项目引用关系：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">App → Core, Plugins, DataHandlingAdapters, Db</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Plugins → Core, DataHandlingAdapters, Db</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">DataHandlingAdapters → Core</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">Db（无额外引用）</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="四创建项目">四、创建项目<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%9B%9B%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE" class="hash-link" aria-label="四、创建项目的直接链接" title="四、创建项目的直接链接" translate="no">​</a></h2>
<p>首先，使用 <code>辅助角色服务</code> 模板创建一个 .NET 10.0 项目，名称为 <code>HighlyAvailableTcpService.App</code>。这是我们的主程序，也是最终启动的入口。</p>
<p>如果你仍在坚守 .NET Framework 4.6.2，也可以先按 net10 创建，再把 <code>.csproj</code> 里的 <code>&lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;</code> 改成 <code>&lt;TargetFramework&gt;net462&lt;/TargetFramework&gt;</code>。记得<strong>勾选不使用顶级语句</strong>和<strong>取消勾选 AOT</strong>，否则编译时会给你一个"惊喜"。</p>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgsAAABgCAYAAACT8zF+AAAAAXNSR0IArs4c6QAAFxNJREFUeF7tnX+sHlWZx899772ld28LUiWyuvYXaQwJIdG/jBqQLGQb2UAw6SZLVg0SZcElSkLUoJvF6HYTQgIESpYYJKLRKMluQsQtaQ3YUPEfJRKSLmkoLW4UqJpYxdpte+/m+8r33ec+98ycM+877/vOnflOQuh958yZ83zOmfN85znPzMycPLW0HEIIc72z+J82ERABERABERABEVhBYEZiQSNCBERABERABESgjIDEgsaHCIiACIiACIhAKQGJBQ0QERABERABERABiQWNAREQAREQAREQgeEJrIgsvHr8xPA16UgREAEREAEREIE1S+DCC84tbLvEwprtVjVcBERABERABOojILFQH0vVJAIiIAIiIAKtJCCx0MpulVEiIAIiIAIiUB8BiYX6WKomERABERABEWglAYmFVnarjBIBERABERCB+ghILNTHUjWJgAiIgAiIQCsJSCy0sltllAiIgAiIgAjUR2BNiIWFc+bDhsVzwuxsL8tyvRMiC5MKiYAIiIAIiEAWgcaLhfM2rg8L69cNjDl58o2wsLCYNE6CIYlIBURABERABEQgi0CjxQIiCueduxAgEO49cE/4r//+wcCoA5/5cf/fl933/oB/8/8s0CSx8K9f/ZfwoQ/9dfjABy/L6pSq5bMqVSEREAEREAERGJJAo8XC287fEObmeuHfnvxqXyi8/dwLw+XbLg+Hjx8O9+16oC8Q/EYRkSsW4Jg/+rEbwubNWwdVvfLK0fDNRx8JLx0+HG785D8OnPwnPn59ePHFQ4Nyd919X39frA4WOvjMgfC52z+zqp0f/ttrwhe/9OVVv+Pcf/931636/dP/9Nlw/T98fMXvH3jfe1a05emnfxh+8P3HVx178CfPrTruO9/7zxU2Dzl+dJgIiIAIiEAHCDRaLLBxFAVPfmrfqiWIUSMLEABf/8a3V3Q1xQKcud1v//3tb30jbNm6LSkW4NCts6YY8A6cDYi1B3XEnDvLsi0QC174+PogbGKCgueXiOjAVS8TRUAERKAigamJhbITexsgCN7zjvf2owl2GyWy4KME7373xSuiBvY82AdBERMLsagBHa4/R6xvWDf2xYQCnPu2rdtXRRVs+TKxYMUK6sIGseDFCvfF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">
<p>然后用 nuget 安装 <code>TouchSocket.Hosting</code>。如果不熟悉 nuget，可以参考<a href="https://touchsocket.net/docs/current/startguide" target="_blank" rel="noopener noreferrer" class="">入门指南</a>。</p>
<p>接下来，再分别新建以下类库项目：</p>
<ul>
<li class=""><code>HighlyAvailableTcpService.Core</code></li>
</ul>
<img src="data:image/png;base64,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">
<ul>
<li class=""><code>HighlyAvailableTcpService.Plugins</code></li>
<li class=""><code>HighlyAvailableTcpService.DataHandlingAdapters</code></li>
<li class=""><code>HighlyAvailableTcpService.Db</code></li>
</ul>
<p>按照第三章的项目引用关系配置好后，我们就可以愉快地开始码字了。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="五完善-core-核心库">五、完善 Core 核心库<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E4%BA%94%E5%AE%8C%E5%96%84-core-%E6%A0%B8%E5%BF%83%E5%BA%93" class="hash-link" aria-label="五、完善 Core 核心库的直接链接" title="五、完善 Core 核心库的直接链接" translate="no">​</a></h2>
<p><code>HighlyAvailableTcpService.Core</code> 是整个项目的基础设施层，里面住着各种接口、配置类——用一句流行语来说，它是整个系统的"灵魂"。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="51-创建自定义-tcp-会话客户端接口">5.1 创建自定义 Tcp 会话客户端接口<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#51-%E5%88%9B%E5%BB%BA%E8%87%AA%E5%AE%9A%E4%B9%89-tcp-%E4%BC%9A%E8%AF%9D%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="5.1 创建自定义 Tcp 会话客户端接口的直接链接" title="5.1 创建自定义 Tcp 会话客户端接口的直接链接" translate="no">​</a></h3>
<p>首先定义 <code>IHighlyAvailableSessionClient</code> 接口，继承 <code>ITcpSessionClient</code>。这个接口就像是给每个连进来的设备颁发的"身份证"，让我们统一管理：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>接口里定义了 <code>SetHighlyAvailableAdapter</code> 方法，用于为会话单独设置数据处理适配器（别把适配器搞混了，它不是充电头）。详情见<a href="https://touchsocket.net/docs/current/adapterdescription" target="_blank" rel="noopener noreferrer" class="">适配器介绍与使用</a>。</p>
<p>接下来，定义实现类 <code>HighlyAvailableSessionClient</code>，继承 <code>TcpSessionClient</code> 并实现上面的接口：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="52-创建自定义-tcp-服务接口与实现">5.2 创建自定义 Tcp 服务接口与实现<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#52-%E5%88%9B%E5%BB%BA%E8%87%AA%E5%AE%9A%E4%B9%89-tcp-%E6%9C%8D%E5%8A%A1%E6%8E%A5%E5%8F%A3%E4%B8%8E%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="5.2 创建自定义 Tcp 服务接口与实现的直接链接" title="5.2 创建自定义 Tcp 服务接口与实现的直接链接" translate="no">​</a></h3>
<p>再定义服务接口 <code>IHighlyAvailableTcpService</code>，继承 <code>ITcpService&lt;HighlyAvailableSessionClient&gt;</code>。有了这个接口，以后想扩展功能就直接往接口里加，不用到处乱找实现类：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>然后是内部实现类 <code>HighlyAvailableTcpService</code>，注意这里用了 <code>internal</code>——外面的世界看不到它，就让它安安静静地在内部干活：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="53-定义-options-配置类">5.3 定义 Options 配置类<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#53-%E5%AE%9A%E4%B9%89-options-%E9%85%8D%E7%BD%AE%E7%B1%BB" class="hash-link" aria-label="5.3 定义 Options 配置类的直接链接" title="5.3 定义 Options 配置类的直接链接" translate="no">​</a></h3>
<p>接下来定义配置选项类 <code>HighlyAvailableTcpServiceOption</code>，用于映射配置文件中的每条监听配置：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>因为服务器可能需要同时监听多个端口（比如同时对外提供 HTTP 和私有协议的服务），所以还需要一个集合配置类。顺带考虑了 Native AOT 的兼容性：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="54-注册扩展方法">5.4 注册扩展方法<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#54-%E6%B3%A8%E5%86%8C%E6%89%A9%E5%B1%95%E6%96%B9%E6%B3%95" class="hash-link" aria-label="5.4 注册扩展方法的直接链接" title="5.4 注册扩展方法的直接链接" translate="no">​</a></h3>
<p>因为 <code>HighlyAvailableTcpService</code> 是 <code>internal</code> 的，外部项目不能直接 <code>new</code> 它出来，所以我们提供一个扩展方法来暴露注册能力。这样外部只需要调用一个方法，内部的脏活我们全包了：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="55-主项目中应用">5.5 主项目中应用<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#55-%E4%B8%BB%E9%A1%B9%E7%9B%AE%E4%B8%AD%E5%BA%94%E7%94%A8" class="hash-link" aria-label="5.5 主项目中应用的直接链接" title="5.5 主项目中应用的直接链接" translate="no">​</a></h3>
<p>首先，在 <code>Program.cs</code> 中绑定配置文件：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>对应的开发环境配置文件如下：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">appsettings.Development.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"HighlyAvailableTcpServiceOptions"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token property">"Options"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"Name"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"HighlyAvailableTcpService"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"Port"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">7789</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"Ip"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"127.0.0.1"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"Backlog"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">100</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"SslPath"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"SslKey"</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">""</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span></span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>备注</div><div class="admonitionContent_BuS1"><p>配置文件有两个：生产环境（<code>appsettings.json</code>）和开发环境（<code>appsettings.Development.json</code>）。开发时只改开发配置文件，上线时再参考配置生产配置文件，这样可以避免线上线下配置打架的惨剧。</p></div></div>
<p>然后注册 TCP 服务。此刻先不配置监听端口——我们计划用插件机制动态添加监听，避免在启动逻辑里硬编码一堆端口号，顺带利用<a href="https://touchsocket.net/docs/current/pluginsmanager" target="_blank" rel="noopener noreferrer" class="">插件机制</a> 和<a href="https://touchsocket.net/docs/current/tcpservice" target="_blank" rel="noopener noreferrer" class="">动态监听</a> 来完成：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>完成以上步骤后，可以尝试启动项目，控制台应该会出现类似这样的日志——说明服务器已经成功站岗了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token plain">info: TouchSocket.Sockets.CheckClearPlugin begin polling</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">info: TouchSocket.Hosting.Sockets.HostService.ServiceHost[0]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      服务器已启动。</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">info: Microsoft.Hosting.Lifetime[0]</span><br></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">      Application started. Press Ctrl+C to shut down.</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="六完善数据适配器项目">六、完善数据适配器项目<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%85%AD%E5%AE%8C%E5%96%84%E6%95%B0%E6%8D%AE%E9%80%82%E9%85%8D%E5%99%A8%E9%A1%B9%E7%9B%AE" class="hash-link" aria-label="六、完善数据适配器项目的直接链接" title="六、完善数据适配器项目的直接链接" translate="no">​</a></h2>
<p>数据适配器项目 <code>HighlyAvailableTcpService.DataHandlingAdapters</code> 的职责是：把从 TCP 流里读出来的一坨字节，翻译成程序能理解的结构化数据。关于适配器的详细介绍，可以参考<a href="https://touchsocket.net/docs/current/adapterdescription" target="_blank" rel="noopener noreferrer" class="">适配器文档</a>。</p>
<p>本示例的数据解析规则很简单：</p>
<ul>
<li class="">接收字符串数据，遇到 <code>\r\n</code> 就认为一条消息结束了</li>
</ul>
<p>（就像古代的竹简，遇到空白就代表一句话说完了。）</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="61-解析数据">6.1 解析数据<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#61-%E8%A7%A3%E6%9E%90%E6%95%B0%E6%8D%AE" class="hash-link" aria-label="6.1 解析数据的直接链接" title="6.1 解析数据的直接链接" translate="no">​</a></h3>
<p>首先定义承载解析结果的类 <code>HighlyAvailableRequestInfo</code>，以及继承自 <code>CustomBetweenAndDataHandlingAdapter</code> 的适配器类 <code>HighlyAvailableDataAdapter</code>：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p>关于 <code>CustomBetweenAndDataHandlingAdapter</code> 的详细介绍，可以参考<a href="https://touchsocket.net/docs/current/custombetweenanddatahandlingadapter" target="_blank" rel="noopener noreferrer" class="">文档</a>。</p></div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="62-关于适配器的应用">6.2 关于适配器的应用<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#62-%E5%85%B3%E4%BA%8E%E9%80%82%E9%85%8D%E5%99%A8%E7%9A%84%E5%BA%94%E7%94%A8" class="hash-link" aria-label="6.2 关于适配器的应用的直接链接" title="6.2 关于适配器的应用的直接链接" translate="no">​</a></h3>
<p>适配器可以在 <code>TouchSocketConfig</code> 中进行全局统一配置，但在本示例中，我们把适配器的设置交给了插件层来管理——这样可以为不同类型的设备连接单独设置不同的适配器，具体实现见下一章的 <code>AdapterPlugin</code>。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="七完善插件项目">七、完善插件项目<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E4%B8%83%E5%AE%8C%E5%96%84%E6%8F%92%E4%BB%B6%E9%A1%B9%E7%9B%AE" class="hash-link" aria-label="七、完善插件项目的直接链接" title="七、完善插件项目的直接链接" translate="no">​</a></h2>
<p>插件项目 <code>HighlyAvailableTcpService.Plugins</code> 是业务逻辑的主战场。我们遵循<strong>单一职责原则</strong>：每个插件只做一件事，处理不了的就传给下一个插件。这套机制就像流水线作业，每个工人只负责自己这道工序。</p>
<p>关于插件系统的介绍，可以参考<a href="https://touchsocket.net/docs/current/pluginsmanager" target="_blank" rel="noopener noreferrer" class="">插件系统文档</a>。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="71-监听配置插件">7.1 监听配置插件<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#71-%E7%9B%91%E5%90%AC%E9%85%8D%E7%BD%AE%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="7.1 监听配置插件的直接链接" title="7.1 监听配置插件的直接链接" translate="no">​</a></h3>
<p>监听配置插件负责在服务器启动时，从配置文件读取端口信息并动态添加监听。</p>
<p>这样做的好处是：改个端口只需要改配置文件，不用重新编译代码。运维同学会感谢你的：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="72-适配器插件">7.2 适配器插件<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#72-%E9%80%82%E9%85%8D%E5%99%A8%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="7.2 适配器插件的直接链接" title="7.2 适配器插件的直接链接" translate="no">​</a></h3>
<p>当一个新设备连接进来时，这个插件立刻为它分配专属的数据适配器。你可以在这里根据 IP、端口、连接时间……甚至月相来决定给它用什么适配器：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="73-日志插件">7.3 日志插件<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#73-%E6%97%A5%E5%BF%97%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="7.3 日志插件的直接链接" title="7.3 日志插件的直接链接" translate="no">​</a></h3>
<p>日志是程序员最好的朋友，尤其是在凌晨 3 点被电话叫醒排查问题的时候。</p>
<p>这里使用了 <code>PluginOption(FromIoc = true)</code>，让日志插件从 IOC 容器中取实例，支持 Scoped 依赖注入：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p><code>PluginOption(FromIoc = true)</code> 特性可以让插件从依赖注入容器中获取实例，实现 Scoped 生命周期——也就是说每个会话有自己独立的插件实例。别小看这个细节，它在处理有状态业务时能救你的命。</p></div></div>
<p>配置 NLog 日志组件。首先 nuget 安装 <code>NLog.Extensions.Logging</code>，然后在 <code>Program.cs</code> 中配置：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>并且把 TouchSocket 的内部日志也接入 AspNetCore 日志体系：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="74-hello-数据处理插件">7.4 Hello 数据处理插件<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#74-hello-%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="7.4 Hello 数据处理插件的直接链接" title="7.4 Hello 数据处理插件的直接链接" translate="no">​</a></h3>
<p>这是一个最简单的业务插件示例：收到 <code>"hello"</code> 就回复 <code>"hi"</code>。虽然逻辑简单，但它展示了插件链式处理数据的核心思路——如果数据不归我处理，就往后传：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p>注意 <code>return</code> 和 <code>await e.InvokeNext()</code> 的区别：</p><ul>
<li class=""><code>return</code>：我处理完了，后续插件不用再看这条消息了（像个霸道的门卫）。</li>
<li class=""><code>await e.InvokeNext()</code>：我处理完了，但后面的插件也可以接着看看有没有它们要做的事（像个礼貌的接待员）。</li>
</ul></div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="75-登录插件与会话状态暂存">7.5 登录插件与会话状态暂存<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#75-%E7%99%BB%E5%BD%95%E6%8F%92%E4%BB%B6%E4%B8%8E%E4%BC%9A%E8%AF%9D%E7%8A%B6%E6%80%81%E6%9A%82%E5%AD%98" class="hash-link" aria-label="7.5 登录插件与会话状态暂存的直接链接" title="7.5 登录插件与会话状态暂存的直接链接" translate="no">​</a></h3>
<p>在实际业务中，我们经常需要记录某个会话的状态，比如"这个设备登录了吗？用户名叫什么？"。</p>
<p>TouchSocket 提供了基于依赖属性的扩展机制。我们先定义扩展属性——注意 <code>SetUserName</code> 是 <code>internal</code> 的，只有在我们自己的插件里才能设置用户名，外部只能读取，防止外部乱改：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>然后定义登录处理插件：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="76-广播插件">7.6 广播插件<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#76-%E5%B9%BF%E6%92%AD%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="7.6 广播插件的直接链接" title="7.6 广播插件的直接链接" translate="no">​</a></h3>
<p>广播功能是很多物联网平台的刚需——比如把某个传感器的告警消息推送给所有连接中的监控终端。</p>
<p>这里使用了 <code>Channel&lt;string&gt;</code> 来做异步消息队列，避免在接收数据的线程上直接遍历所有会话导致性能抖动：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>当收到 <code>"broadcast"</code> 消息时，就把数据塞进 Channel，由后台任务负责挨个发送给所有在线会话。即使某个会话发送失败，也不会影响其他会话的发送——这才叫"高可用"。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="77-注册插件到容器scoped-支持">7.7 注册插件到容器（Scoped 支持）<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#77-%E6%B3%A8%E5%86%8C%E6%8F%92%E4%BB%B6%E5%88%B0%E5%AE%B9%E5%99%A8scoped-%E6%94%AF%E6%8C%81" class="hash-link" aria-label="7.7 注册插件到容器（Scoped 支持）的直接链接" title="7.7 注册插件到容器（Scoped 支持）的直接链接" translate="no">​</a></h3>
<p>需要使用 IOC 注入的插件（即标注了 <code>[PluginOption(FromIoc = true)]</code> 的），需要先在服务容器中注册：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="78-插件注册扩展">7.8 插件注册扩展<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#78-%E6%8F%92%E4%BB%B6%E6%B3%A8%E5%86%8C%E6%89%A9%E5%B1%95" class="hash-link" aria-label="7.8 插件注册扩展的直接链接" title="7.8 插件注册扩展的直接链接" translate="no">​</a></h3>
<p>和 Core 层一样，插件层也提供一个统一的扩展方法，让外部只需一行代码就能启用所有插件：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>最终在主项目中调用：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="八完善-db-数据库层">八、完善 Db 数据库层<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%85%AB%E5%AE%8C%E5%96%84-db-%E6%95%B0%E6%8D%AE%E5%BA%93%E5%B1%82" class="hash-link" aria-label="八、完善 Db 数据库层的直接链接" title="八、完善 Db 数据库层的直接链接" translate="no">​</a></h2>
<p>数据库层 <code>HighlyAvailableTcpService.Db</code> 负责把重要数据持久化到磁盘中。毕竟，内存是易忘症患者，断电就失忆；数据库才是靠谱的日记本。</p>
<p>本项目使用 <strong>SQLite + SqlSugar</strong> 的组合：</p>
<ul>
<li class=""><strong>SQLite</strong>：轻量级嵌入式数据库，零配置，随项目走，适合中小规模的 IoT 数据存储</li>
<li class=""><strong>SqlSugar</strong>：国产 ORM 框架，API 简洁，性能优秀（而且 Native AOT 支持做得很好）</li>
</ul>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="81-定义数据库实体">8.1 定义数据库实体<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#81-%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E4%BD%93" class="hash-link" aria-label="8.1 定义数据库实体的直接链接" title="8.1 定义数据库实体的直接链接" translate="no">​</a></h3>
<p>定义用户实体类，这里只是一个示例结构：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="82-注册数据库服务">8.2 注册数据库服务<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#82-%E6%B3%A8%E5%86%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E6%9C%8D%E5%8A%A1" class="hash-link" aria-label="8.2 注册数据库服务的直接链接" title="8.2 注册数据库服务的直接链接" translate="no">​</a></h3>
<p>注册 SQLite 连接和仓储模式。这里通过 CodeFirst 方式在首次运行时自动建表，省去了手动执行 SQL 脚本的麻烦：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>在 <code>Program.cs</code> 中调用：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="83-dbplugin-数据库插件">8.3 DbPlugin 数据库插件<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#83-dbplugin-%E6%95%B0%E6%8D%AE%E5%BA%93%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="8.3 DbPlugin 数据库插件的直接链接" title="8.3 DbPlugin 数据库插件的直接链接" translate="no">​</a></h3>
<p>当收到 <code>"Add"</code> 消息时，自动往数据库里插入一条用户记录。需要注意的是，AOT 环境下 ORM 的反射功能受限，所以这里用了条件编译符来区分：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="九native-aot">九、Native AOT<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E4%B9%9Dnative-aot" class="hash-link" aria-label="九、Native AOT的直接链接" title="九、Native AOT的直接链接" translate="no">​</a></h2>
<p>如果你追求极致的启动速度和极小的发布体积，.NET 10.0 及以上版本可以使用 <strong>Native AOT</strong> 发布。</p>
<p>只需在 <code>.csproj</code> 中添加：</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#bfc7d5"><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;</span><span class="token tag" style="color:rgb(255, 85, 114)">PublishAot</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><span class="token plain">true</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&lt;/</span><span class="token tag" style="color:rgb(255, 85, 114)">PublishAot</span><span class="token tag punctuation" style="color:rgb(199, 146, 234)">&gt;</span><br></div></code></pre></div></div>
<p>然后右击项目 → 发布 → 新建配置，最终发布配置如下：</p>
<img src="https://touchsocket.net/assets/images/CreatehighlyavailableTcpService-3-19dc0680f3c4650e07c113b1bee0094c.png">
<p>发布后，可执行文件仅 <strong>6.6 MB</strong>，是的你没看错，6.6 MB，比你手机上随手下载的一个表情包 App 还小：</p>
<img src="https://touchsocket.net/assets/images/CreatehighlyavailableTcpService-4-7f33d75a2328ede5cfe31d3eec067ded.png">
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>提示</div><div class="admonitionContent_BuS1"><p>TouchSocket 已完整支持 Native AOT。但如果你引入了其他第三方库，请确认它们也支持 AOT——否则编译时会给你一些意想不到的"惊喜警告"。</p></div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="十设为-windows-服务">十、设为 Windows 服务<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%8D%81%E8%AE%BE%E4%B8%BA-windows-%E6%9C%8D%E5%8A%A1" class="hash-link" aria-label="十、设为 Windows 服务的直接链接" title="十、设为 Windows 服务的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="101-配置-windows-服务">10.1 配置 Windows 服务<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#101-%E9%85%8D%E7%BD%AE-windows-%E6%9C%8D%E5%8A%A1" class="hash-link" aria-label="10.1 配置 Windows 服务的直接链接" title="10.1 配置 Windows 服务的直接链接" translate="no">​</a></h3>
<p>通用主机天然支持作为 Windows 服务运行。详细介绍，请参考：<a href="https://learn.microsoft.com/zh-cn/dotnet/core/extensions/windows-service" target="_blank" rel="noopener noreferrer" class="">使用 BackgroundService 创建 Windows 服务</a>。</p>
<p>首先 nuget 安装 <code>Microsoft.Extensions.Hosting.WindowsServices</code>，然后在 Program.cs 中加入：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>这段代码我们已经在主程序里写好了——而且加了跨平台判断，在 Linux 上运行时不会出错，毕竟我们承诺过跨平台。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="102-安装启动服务">10.2 安装、启动服务<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#102-%E5%AE%89%E8%A3%85%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1" class="hash-link" aria-label="10.2 安装、启动服务的直接链接" title="10.2 安装、启动服务的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">cmd（以管理员身份运行）</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">sc create HighlyAvailableTcpService binPath= %~dp0HighlyAvailableTcpService.App.exe start= auto</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">sc description HighlyAvailableTcpService "高可用Tcp服务"</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Net Start HighlyAvailableTcpService</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">pause</span></span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="103-卸载停止服务">10.3 卸载、停止服务<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#103-%E5%8D%B8%E8%BD%BD%E5%81%9C%E6%AD%A2%E6%9C%8D%E5%8A%A1" class="hash-link" aria-label="10.3 卸载、停止服务的直接链接" title="10.3 卸载、停止服务的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_OeMC">cmd（以管理员身份运行）</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">net stop HighlyAvailableTcpService</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">sc delete HighlyAvailableTcpService</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#bfc7d5"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">pause</span></span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="十一最佳实践">十一、最佳实践<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%8D%81%E4%B8%80%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5" class="hash-link" aria-label="十一、最佳实践的直接链接" title="十一、最佳实践的直接链接" translate="no">​</a></h2>
<p>积累了无数次被用户"温柔鞭打"的经验后，给大家整理以下最佳实践——希望你少踩一点坑。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="111-日志分级别输出">11.1 日志分级别输出<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#111-%E6%97%A5%E5%BF%97%E5%88%86%E7%BA%A7%E5%88%AB%E8%BE%93%E5%87%BA" class="hash-link" aria-label="11.1 日志分级别输出的直接链接" title="11.1 日志分级别输出的直接链接" translate="no">​</a></h3>
<p>TouchSocket 内部会把网络异常（比如对端突然断开）记录为日志，这些日志在生产环境中往往是"已知噪音"。建议在 NLog 配置中：</p>
<ul>
<li class="">通信组件的日志输出到单独的文件（比如 <code>touchsocket.log</code>）</li>
<li class="">业务日志输出到主日志文件</li>
</ul>
<p>这样排查问题时就不会被一堆 "连接已断开" 日志淹没。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="112-使用依赖属性暂存会话状态">11.2 使用依赖属性暂存会话状态<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#112-%E4%BD%BF%E7%94%A8%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E6%9A%82%E5%AD%98%E4%BC%9A%E8%AF%9D%E7%8A%B6%E6%80%81" class="hash-link" aria-label="11.2 使用依赖属性暂存会话状态的直接链接" title="11.2 使用依赖属性暂存会话状态的直接链接" translate="no">​</a></h3>
<p>如果需要为每个连接的设备暂存状态（比如认证信息、设备类型、最后活跃时间），推荐使用 TouchSocket 提供的 <code>DependencyProperty</code> 机制，而不是用 <code>ConcurrentDictionary</code> 满天飞。</p>
<p>好处：</p>
<ul>
<li class="">随会话生命周期自动管理，不用担心内存泄漏</li>
<li class="">访问接口统一，代码整洁</li>
<li class=""><code>SetUserName</code> 设为 <code>internal</code> 后，外部只能读不能写，天然防止误操作</li>
</ul>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="113-插件顺序很重要">11.3 插件顺序很重要<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#113-%E6%8F%92%E4%BB%B6%E9%A1%BA%E5%BA%8F%E5%BE%88%E9%87%8D%E8%A6%81" class="hash-link" aria-label="11.3 插件顺序很重要的直接链接" title="11.3 插件顺序很重要的直接链接" translate="no">​</a></h3>
<p>插件的执行顺序就是注册顺序。日志插件要放在业务数据处理插件<strong>前面</strong>，这样才能记录到完整的处理耗时。否则日志只记录到"数据到了"，不知道后续花了多少时间处理。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="114-广播消息用-channel-解耦">11.4 广播消息用 Channel 解耦<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#114-%E5%B9%BF%E6%92%AD%E6%B6%88%E6%81%AF%E7%94%A8-channel-%E8%A7%A3%E8%80%A6" class="hash-link" aria-label="11.4 广播消息用 Channel 解耦的直接链接" title="11.4 广播消息用 Channel 解耦的直接链接" translate="no">​</a></h3>
<p>不要在接收数据的回调里直接遍历所有会话发送消息！这会导致发送耗时阻塞接收线程。正确姿势是用 <code>Channel&lt;T&gt;</code> 或其他异步队列，把消息投递交给专属后台任务处理。</p>
<p><code>BroadcastPlugin</code> 就是这个思路的最佳实践。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="115-native-aot-兼容性注意">11.5 Native AOT 兼容性注意<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#115-native-aot-%E5%85%BC%E5%AE%B9%E6%80%A7%E6%B3%A8%E6%84%8F" class="hash-link" aria-label="11.5 Native AOT 兼容性注意的直接链接" title="11.5 Native AOT 兼容性注意的直接链接" translate="no">​</a></h3>
<p>如果使用 Native AOT 发布，需要注意：</p>
<ul>
<li class="">反射受限，某些 ORM 的动态查询功能不可用（如示例中的 <code>DbPlugin</code> 用了 <code>#if !AOT</code>）</li>
<li class="">检查所有第三方库是否支持 AOT（SqlSugar 需要手动开启 <code>StaticConfig.EnableAot = true</code>）</li>
</ul>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="十二总结">十二、总结<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%8D%81%E4%BA%8C%E6%80%BB%E7%BB%93" class="hash-link" aria-label="十二、总结的直接链接" title="十二、总结的直接链接" translate="no">​</a></h2>
<p>好了，我们用一整篇博客，把一个"高可用 TCP 服务器"的各个器官全部安装完毕：</p>
<table><thead><tr><th>项目</th><th>职责</th></tr></thead><tbody><tr><td><code>Core</code></td><td>定义接口和配置类，是整个系统的骨架</td></tr><tr><td><code>DataHandlingAdapters</code></td><td>数据解包，把字节流翻译成业务数据</td></tr><tr><td><code>Plugins</code></td><td>业务逻辑，插件化、可扩展、可复用</td></tr><tr><td><code>Db</code></td><td>数据持久化，让数据不会随断电而消逝</td></tr><tr><td><code>App</code></td><td>主程序入口，把所有模块串联起来</td></tr></tbody></table>
<p>核心设计理念：</p>
<ul>
<li class=""><strong>插件化</strong>：每个业务逻辑一个插件，互不干扰，随时可插拔</li>
<li class=""><strong>配置驱动</strong>：端口、IP、证书全部走配置文件，零硬编码</li>
<li class=""><strong>分层解耦</strong>：Core / Adapters / Plugins / Db 各司其职，改一层不影响另一层</li>
<li class=""><strong>高可用</strong>：健康清理插件 + 异步广播 + AOT 支持 = 稳定运行，不让运维老哥半夜起床</li>
</ul>
<p>希望这篇博客能帮你少走弯路，早日构建出那个不会让手机被叫爆的 TCP 服务器。祝编译一次通过，Bug 从不出现在生产！</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="十三参考资料">十三、参考资料<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%8D%81%E4%B8%89%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99" class="hash-link" aria-label="十三、参考资料的直接链接" title="十三、参考资料的直接链接" translate="no">​</a></h2>
<ol>
<li class=""><a href="https://touchsocket.net/" target="_blank" rel="noopener noreferrer" class="">TouchSocket 官网</a></li>
<li class=""><a href="https://learn.microsoft.com/zh-cn/dotnet/core/extensions/generic-host" target="_blank" rel="noopener noreferrer" class="">.NET 通用主机</a></li>
<li class=""><a href="https://learn.microsoft.com/zh-cn/dotnet/core/extensions/windows-service" target="_blank" rel="noopener noreferrer" class="">使用 BackgroundService 创建 Windows 服务</a></li>
<li class=""><a href="https://touchsocket.net/docs/current/adapterdescription" target="_blank" rel="noopener noreferrer" class="">适配器介绍与使用</a></li>
<li class=""><a href="https://touchsocket.net/docs/current/pluginsmanager" target="_blank" rel="noopener noreferrer" class="">插件系统文档</a></li>
</ol>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="十四本文示例-demo">十四、本文示例 Demo<a href="https://touchsocket.net/blog/CreatehighlyavailableTcpService#%E5%8D%81%E5%9B%9B%E6%9C%AC%E6%96%87%E7%A4%BA%E4%BE%8B-demo" class="hash-link" aria-label="十四、本文示例 Demo的直接链接" title="十四、本文示例 Demo的直接链接" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>信息</div><div class="admonitionContent_BuS1"><p>本文所涉及所有技术均是开源的，大家可以直接按照教程一步步搭建。</p><p>但作为 Pro 用户的专属福利，完整的成品示例工程仅对企业版 Pro 用户开放。</p><p>欢迎通过 Pro 的<a href="https://touchsocket.net/docs/current/enterprise" target="_blank" rel="noopener noreferrer" class="">购买</a>来支持我们的开源工作，让这个项目越来越好！</p></div></div>
<div class="card-link"><div class="card-content"><svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" class="icon" viewBox="0 0 1031 1024"><path fill="#E3E3E3" d="M946.9 765.9H84.7C38.1 765.9 0 727.8 0 681.2V127.4c0-46.6 38.1-84.7 84.7-84.7h862.2c46.6 0 84.7 38.1 84.7 84.7v553.8c0 46.6-38.1 84.7-84.7 84.7"></path><path fill="#475762" d="M946.9 991.5H84.7C38.1 991.5 0 953.4 0 906.8V268.4h1031.5v638.5c.1 46.5-38 84.6-84.6 84.6"></path><path fill="#CACACA" d="M163.304 234.063a22.17 22.17 0 1 0 44.34 0 22.17 22.17 0 1 0-44.34 0m63.343 0a22.17 22.17 0 1 0 44.34 0 22.17 22.17 0 1 0-44.34 0m63.343 0a22.17 22.17 0 1 0 44.34 0 22.17 22.17 0 1 0-44.34 0m-56.236 175.816H200.46c-10.042 0-18.23-8.189-18.23-18.23 0-10.043 8.188-18.231 18.23-18.231h33.294c10.042 0 18.23 8.188 18.23 18.23s-8.188 18.23-18.23 18.23z"></path><path fill="#F26397" d="M309.7 379.8H239c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h70.7c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m22.6 89.2H298c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h34.3c13 0 23.6 10.6 23.6 23.6S345.2 469 332.3 469m52.3 89.2h-32.8c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h32.8c13 0 23.6 10.6 23.6 23.6s-10.7 23.6-23.6 23.6"></path><path fill="#6ACEB1" d="M597.359 547.688H480.097c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h117.262c10.042 0 18.23 8.188 18.23 18.23-.077 10.042-8.265 18.23-18.23 18.23"></path><path fill="#5F7786" d="M785.2 558.2h-63.6c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h63.6c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m139.5 82H777c-13 0-23.6-10.6-23.6-23.6S764 593 777 593h147.7c13 0 23.6 10.6 23.6 23.6 0 12.9-10.7 23.6-23.6 23.6M775 736.6h-70.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6H775c13 0 23.6 10.6 23.6 23.6s-10.7 23.6-23.6 23.6m-81.8 89.2h-70.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h70.7c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m231.5-267.6h-63.6c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h63.6c13 0 23.6 10.6 23.6 23.6s-10.7 23.6-23.6 23.6M917 736.6h-20.5c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6H917c13 0 23.6 10.6 23.6 23.6S930 736.6 917 736.6"></path><path fill="#6ACEB1" d="M575.3 469H423.5c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h151.8c13 0 23.6 10.6 23.6 23.6S588.3 469 575.3 469m47.2 171.2H470.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h151.8c13 0 23.6 10.6 23.6 23.6-.1 12.9-10.7 23.6-23.6 23.6m0 96.4H470.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h151.8c13 0 23.6 10.6 23.6 23.6-.1 13-10.7 23.6-23.6 23.6m-114.9 89.2h-36.9c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h36.9c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6"></path><path fill="#F26397" d="M418.685 616.593h-26.496c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h26.496c10.042 0 18.23 8.188 18.23 18.23s-8.188 18.23-18.23 18.23m0 68.905h-26.496c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h26.496c10.042 0 18.23 8.188 18.23 18.23s-8.188 18.23-18.23 18.23m-39.937 68.905H346.69c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h32.058c10.042 0 18.23 8.187 18.23 18.23-.077 10.042-8.265 18.23-18.23 18.23m-26.96 64.965h-54.614c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h54.614c10.043 0 18.23 8.188 18.23 18.23s-8.187 18.23-18.23 18.23"></path><path fill="#CACACA" d="M151.8 469h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6S164.8 469 151.8 469m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6S95.7 511 108.7 511h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6S164.8 915 151.8 915"></path></svg><h3>HighlyAvailableTcpService</h3></div><div class="card-links"><a href="https://github.com/RRQM/PrivateTouchSocketPro/tree/main/examples-pro/HighlyAvailableTcpService" class="link-button github" target="_blank" rel="noopener noreferrer"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" class="link-icon"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>GitHub</a></div><div class="pro-badge"><a href="https://touchsocket.net/docs/current/enterprise">Pro</a></div></div>]]></content:encoded>
            <category>TcpService</category>
        </item>
        <item>
            <title><![CDATA[Tcp实现限流服务——优雅地拒绝"贪婪"的客户端]]></title>
            <link>https://touchsocket.net/blog/TcpServiceLimiting</link>
            <guid>https://touchsocket.net/blog/TcpServiceLimiting</guid>
            <pubDate>Sat, 04 Apr 2026 05:36:17 GMT</pubDate>
            <description><![CDATA[一、引言：当"好客"变成"受难"]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="一引言当好客变成受难">一、引言：当"好客"变成"受难"<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E4%B8%80%E5%BC%95%E8%A8%80%E5%BD%93%E5%A5%BD%E5%AE%A2%E5%8F%98%E6%88%90%E5%8F%97%E9%9A%BE" class="hash-link" aria-label="一、引言：当&quot;好客&quot;变成&quot;受难&quot;的直接链接" title="一、引言：当&quot;好客&quot;变成&quot;受难&quot;的直接链接" translate="no">​</a></h2>
<p>在一个阳光明媚的下午，我正悠哉悠哉地更新博客。忽然，一条来自群友的私信打破了宁静——这位朋友是一名对网络编程充满热情的大学生，最近正在研究 TCP 连接数量限流和接收流量限流。他噼里啪啦连续发来一堆问题，我的屏幕差点没装下……</p>
<p>其实，限流这个话题，本质上就是 <strong>"服务器学会说不"</strong> 的艺术。</p>
<p>你想想：一个热门餐厅，如果不限号，来多少人接多少，厨师当场崩溃，菜没法做，最终所有人都饿肚子。但如果合理地限制每桌用餐人数、控制上菜速度，反而能让大家都享受美食体验。TCP 限流的道理如出一辙。</p>
<p>以前写过一篇<a href="https://blog.csdn.net/qq_40374647/article/details/125496769" target="_blank" rel="noopener noreferrer" class="">博客</a>聊过相关话题，但那会儿写得太简单，点到即止。这次要升级一下方案，把原理和代码都聊清楚，让你彻底搞明白——如何用最优雅的方式，礼貌而坚定地"限制"你的客户端。</p>
<!-- -->
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="二技术选型站在巨人的肩膀上">二、技术选型：站在巨人的肩膀上<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E4%BA%8C%E6%8A%80%E6%9C%AF%E9%80%89%E5%9E%8B%E7%AB%99%E5%9C%A8%E5%B7%A8%E4%BA%BA%E7%9A%84%E8%82%A9%E8%86%80%E4%B8%8A" class="hash-link" aria-label="二、技术选型：站在巨人的肩膀上的直接链接" title="二、技术选型：站在巨人的肩膀上的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="21-tcp-服务器touchsocket">2.1 TCP 服务器：TouchSocket<a href="https://touchsocket.net/blog/TcpServiceLimiting#21-tcp-%E6%9C%8D%E5%8A%A1%E5%99%A8touchsocket" class="hash-link" aria-label="2.1 TCP 服务器：TouchSocket的直接链接" title="2.1 TCP 服务器：TouchSocket的直接链接" translate="no">​</a></h3>
<p>这里使用 <a href="https://gitee.com/RRQM_Home/TouchSocket" target="_blank" rel="noopener noreferrer" class="">TouchSocket-TcpService</a> 作为 TCP 服务器。</p>
<p>为什么选它？因为 TouchSocket 支持<strong>插件化</strong>的开发方式，我们可以把限流逻辑封装进插件里，做到：</p>
<ul>
<li class="">逻辑清晰，一眼看出这个插件是干嘛的</li>
<li class="">可插拔，不需要限流了直接卸载插件即可</li>
<li class="">可复用，写一次，到处用</li>
</ul>
<p>就好比给餐厅门口配了一个"礼貌但强硬"的门卫，而不是让每个厨师自己去拦客——专业分工，效率翻倍。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="22-限流算法微软官方出品">2.2 限流算法：微软官方出品<a href="https://touchsocket.net/blog/TcpServiceLimiting#22-%E9%99%90%E6%B5%81%E7%AE%97%E6%B3%95%E5%BE%AE%E8%BD%AF%E5%AE%98%E6%96%B9%E5%87%BA%E5%93%81" class="hash-link" aria-label="2.2 限流算法：微软官方出品的直接链接" title="2.2 限流算法：微软官方出品的直接链接" translate="no">​</a></h3>
<p>限流算法使用微软提供的 <a href="https://www.nuget.org/packages/System.Threading.RateLimiting" target="_blank" rel="noopener noreferrer" class="">System.Threading.RateLimiting</a> 库。这个库是 .NET 官方在 .NET 7 中引入的，专门用来实现流量控制，可靠性拉满。</p>
<p>它内置了 4 种限流算法，各有特点：</p>
<table><thead><tr><th>算法</th><th>特点</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>并发限制</strong>（Concurrency）</td><td>限制同时进行的请求数量</td><td>限制并发连接数</td></tr><tr><td><strong>令牌桶</strong>（Token Bucket）</td><td>令牌匀速补充，消费灵活</td><td>允许一定突发流量</td></tr><tr><td><strong>固定时间窗口</strong>（Fixed Window）</td><td>固定时间内限制请求数</td><td>简单的 QPS 限制</td></tr><tr><td><strong>滑动时间窗口</strong>（Sliding Window）</td><td>更平滑的时间窗口</td><td>精确的速率控制</td></tr></tbody></table>
<p>本文示例以<strong>固定时间窗口</strong>为例。其他算法的用法大同小异，具体参数可参考<a href="https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.ratelimiting" target="_blank" rel="noopener noreferrer" class="">微软官方文档</a>，举一反三即可。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="三实践效果眼见为实">三、实践效果：眼见为实<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E4%B8%89%E5%AE%9E%E8%B7%B5%E6%95%88%E6%9E%9C%E7%9C%BC%E8%A7%81%E4%B8%BA%E5%AE%9E" class="hash-link" aria-label="三、实践效果：眼见为实的直接链接" title="三、实践效果：眼见为实的直接链接" translate="no">​</a></h2>
<p>话不多说，先看效果。下图演示了连接数量限流和流量限流的实际效果：</p>
<ul>
<li class="">当连接数超过上限时，新连接会被<strong>礼貌拒绝</strong>（服务器内心：对不起，我已经满了）</li>
<li class="">当某个客户端发送数据过快时，服务器会<strong>优雅减速</strong>（不是不收，是慢慢收）</li>
</ul>
<img src="https://touchsocket.net/assets/images/tcplimiting-1-18eac99f610a022758d1aaac16316ef5.gif">
<p>是不是很丝滑？接下来我们就来看看这背后的代码实现。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="四代码实现插件化的艺术">四、代码实现：插件化的艺术<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E5%9B%9B%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0%E6%8F%92%E4%BB%B6%E5%8C%96%E7%9A%84%E8%89%BA%E6%9C%AF" class="hash-link" aria-label="四、代码实现：插件化的艺术的直接链接" title="四、代码实现：插件化的艺术的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="41-整体架构">4.1 整体架构<a href="https://touchsocket.net/blog/TcpServiceLimiting#41-%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="4.1 整体架构的直接链接" title="4.1 整体架构的直接链接" translate="no">​</a></h3>
<p>整个限流方案基于 TouchSocket 的插件体系，我们只需要在服务器启动时注册好两个插件即可——连接限流插件和接收流量限流插件。</p>
<p>服务器启动代码如下：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>就这么简单！接下来分别看这两个插件的内部实现。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="42-连接限流插件">4.2 连接限流插件<a href="https://touchsocket.net/blog/TcpServiceLimiting#42-%E8%BF%9E%E6%8E%A5%E9%99%90%E6%B5%81%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="4.2 连接限流插件的直接链接" title="4.2 连接限流插件的直接链接" translate="no">​</a></h3>
<p>连接限流的核心思路：<strong>在握手阶段就拦截</strong>。</p>
<p>TouchSocket 提供了 <code>ITcpConnectingPlugin</code> 接口，它会在 TCP 连接建立之前触发。我们在这里检查是否超过连接数限制，超了就拒之门外——就像夜店门口的保镖，超员了直接让你回家。</p>
<p>完整插件代码如下：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h4 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="关键点解析">关键点解析<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E5%85%B3%E9%94%AE%E7%82%B9%E8%A7%A3%E6%9E%90" class="hash-link" aria-label="关键点解析的直接链接" title="关键点解析的直接链接" translate="no">​</a></h4>
<p><strong>限流器初始化</strong>：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>这里的参数含义：</p>
<ul>
<li class=""><code>PermitLimit = 2</code>：每个时间窗口内最多允许 2 个新连接（演示用，生产环境请适当调大）</li>
<li class=""><code>QueueLimit = 4</code>：允许最多 4 个请求在队列中等待（这里我们选择直接拒绝，所以这个参数不太关键）</li>
<li class=""><code>Window = TimeSpan.FromSeconds(5)</code>：时间窗口为 5 秒</li>
</ul>
<p><strong>连接拒绝逻辑</strong>：</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>代码非常直接：<code>AttemptAcquire(1)</code> 尝试获取一个令牌，如果没拿到（<code>IsAcquired == false</code>），就设置 <code>e.IsPermitOperation = false</code>，直接拒绝连接。顺便记录一条警告日志，让你知道是谁在"碰壁"。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>小技巧</div><div class="admonitionContent_BuS1"><p><code>client.IsClient</code> 的判断是为了在服务器插件中区分"服务端角色"和"客户端角色"，避免在客户端模式下也触发连接限制。如果你的服务器纯粹作为服务端使用，可以省略这个判断。</p></div></div>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="43-接收流量限流插件">4.3 接收流量限流插件<a href="https://touchsocket.net/blog/TcpServiceLimiting#43-%E6%8E%A5%E6%94%B6%E6%B5%81%E9%87%8F%E9%99%90%E6%B5%81%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="4.3 接收流量限流插件的直接链接" title="4.3 接收流量限流插件的直接链接" translate="no">​</a></h3>
<p>流量限流和连接限流有一个本质区别：连接限流是<strong>全局的</strong>（针对所有连接），而流量限流是<strong>per-client 的</strong>（每个客户端独立计算自己的配额）。</p>
<p>这就像：餐厅的座位总数是固定的（全局），但每桌的上菜速度要单独控制（per-client），不能因为某桌狼吞虎咽就让其他桌也饿着。</p>
<h4 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="431-per-client-的限流器绑定">4.3.1 per-client 的限流器绑定<a href="https://touchsocket.net/blog/TcpServiceLimiting#431-per-client-%E7%9A%84%E9%99%90%E6%B5%81%E5%99%A8%E7%BB%91%E5%AE%9A" class="hash-link" aria-label="4.3.1 per-client 的限流器绑定的直接链接" title="4.3.1 per-client 的限流器绑定的直接链接" translate="no">​</a></h4>
<p>为了给每个客户端绑定独立的限流器，这里巧妙地使用了 TouchSocket 的 <code>DependencyProperty</code>（扩展属性）机制，而不是靠字典维护——这样可以随着客户端的生命周期自动管理，免去了手动清理的麻烦。</p>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>这段代码声明了一个懒加载的扩展属性：当某个 <code>ITcpSession</code> 第一次调用 <code>GetValue(RateLimiterProperty)</code> 时，才会触发 <code>OnCreateRateLimiter</code> 工厂方法，为这个客户端创建专属的限流器并缓存起来。</p>
<p>这就是典型的"按需创建，随用随取"的设计思路，既优雅又高效。</p>
<h4 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="432-流量消费逻辑">4.3.2 流量消费逻辑<a href="https://touchsocket.net/blog/TcpServiceLimiting#432-%E6%B5%81%E9%87%8F%E6%B6%88%E8%B4%B9%E9%80%BB%E8%BE%91" class="hash-link" aria-label="4.3.2 流量消费逻辑的直接链接" title="4.3.2 流量消费逻辑的直接链接" translate="no">​</a></h4>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<p>这里有一个细节需要注意：每次 <code>AcquireAsync(step)</code> 请求的 <code>step</code> 不能超过限流器的 <code>PermitLimit</code>。当一次性接收的数据量很大时，需要用循环分批"消费"令牌，直到所有数据都被处理完毕。</p>
<p>这类似于自助餐的取菜逻辑——盘子只有那么大，超过盘子容量的食物需要分两盘装。</p>
<h4 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="433-完整插件代码">4.3.3 完整插件代码<a href="https://touchsocket.net/blog/TcpServiceLimiting#433-%E5%AE%8C%E6%95%B4%E6%8F%92%E4%BB%B6%E4%BB%A3%E7%A0%81" class="hash-link" aria-label="4.3.3 完整插件代码的直接链接" title="4.3.3 完整插件代码的直接链接" translate="no">​</a></h4>
<div style="padding:20px;text-align:center;background-color:#f5f5f5;border-radius:4px;font-family:monospace"><div>🔄 正在加载代码...</div></div>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="五进阶思考限流的背后">五、进阶思考：限流的背后<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E4%BA%94%E8%BF%9B%E9%98%B6%E6%80%9D%E8%80%83%E9%99%90%E6%B5%81%E7%9A%84%E8%83%8C%E5%90%8E" class="hash-link" aria-label="五、进阶思考：限流的背后的直接链接" title="五、进阶思考：限流的背后的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="51-为什么要限流">5.1 为什么要限流？<a href="https://touchsocket.net/blog/TcpServiceLimiting#51-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E9%99%90%E6%B5%81" class="hash-link" aria-label="5.1 为什么要限流？的直接链接" title="5.1 为什么要限流？的直接链接" translate="no">​</a></h3>
<p>不限流会发生什么？想象一下：</p>
<ol>
<li class=""><strong>连接爆炸</strong>：某个脑回路不正常的客户端（或者被攻击了）疯狂建立连接，服务器的文件描述符耗尽，新连接一个都接不了——这就是经典的连接耗尽攻击。</li>
<li class=""><strong>带宽打爆</strong>：某个客户端疯狂发数据，独占了所有带宽，其他客户端的数据淤积在缓冲区，体验极差。</li>
<li class=""><strong>内存溢出</strong>：接收缓冲区无限增长，最终 OOM——这比上面两个更惨烈。</li>
</ol>
<p>限流就是在这些悲剧发生之前，给系统加一道"自我保护"的防线。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="52-四种算法怎么选">5.2 四种算法怎么选？<a href="https://touchsocket.net/blog/TcpServiceLimiting#52-%E5%9B%9B%E7%A7%8D%E7%AE%97%E6%B3%95%E6%80%8E%E4%B9%88%E9%80%89" class="hash-link" aria-label="5.2 四种算法怎么选？的直接链接" title="5.2 四种算法怎么选？的直接链接" translate="no">​</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>我要限制同时在处理的请求数</td><td>并发限制</td></tr></tbody></table>
<p>本文的示例使用了固定时间窗口，简单直接，适合入门理解。</p>
<h3 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="53-touchsocket-插件体系的优势">5.3 TouchSocket 插件体系的优势<a href="https://touchsocket.net/blog/TcpServiceLimiting#53-touchsocket-%E6%8F%92%E4%BB%B6%E4%BD%93%E7%B3%BB%E7%9A%84%E4%BC%98%E5%8A%BF" class="hash-link" aria-label="5.3 TouchSocket 插件体系的优势的直接链接" title="5.3 TouchSocket 插件体系的优势的直接链接" translate="no">​</a></h3>
<p>插件体系最大的好处是<strong>关注点分离</strong>。业务逻辑和限流逻辑完全解耦：</p>
<ul>
<li class="">想换限流算法？改插件内部，不动业务代码</li>
<li class="">想临时关闭限流？注释掉 <code>a.Add&lt;LimitNumberOfConnectionsPlugin&gt;()</code> 这一行</li>
<li class="">想针对不同端口用不同限流策略？注册多个不同配置的插件实例</li>
</ul>
<p>这种灵活性，如果用传统的"在业务代码里硬编码限流逻辑"的方式，根本实现不了。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="六总结">六、总结<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E5%85%AD%E6%80%BB%E7%BB%93" class="hash-link" aria-label="六、总结的直接链接" title="六、总结的直接链接" translate="no">​</a></h2>
<p>本文用一种"能吃饭就不饿肚子"的思路，介绍了如何用 <strong>TouchSocket + System.Threading.RateLimiting</strong> 给 TCP 服务器加上限流保护：</p>
<ol>
<li class=""><strong>连接限流</strong>：在 <code>ITcpConnectingPlugin</code> 中，通过 <code>FixedWindowRateLimiter</code> 实现全局连接数控制——超员直接拒绝，没有商量余地。</li>
<li class=""><strong>流量限流</strong>：在 <code>ITcpReceivedPlugin</code> 中，通过 <code>DependencyProperty</code> 给每个客户端绑定独立的限流器——流量超标不断开，优雅地慢下来。</li>
</ol>
<p>两个插件，两段代码，轻松搞定"贪婪"客户端的问题。</p>
<p>当然别忘了，<code>PermitLimit</code>、<code>Window</code> 等参数需要根据实际业务场景调整。演示里设置得很小，是为了方便看到效果，实际生产环境中需要结合压测数据来设定合理的阈值。</p>
<p>代码写好了，服务器就不再是那个来者不拒的"烂好人"，而是一个懂得保护自己的"成熟的服务器"。</p>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="七参考资料">七、参考资料<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E4%B8%83%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99" class="hash-link" aria-label="七、参考资料的直接链接" title="七、参考资料的直接链接" translate="no">​</a></h2>
<ol>
<li class=""><a href="https://touchsocket.net/" target="_blank" rel="noopener noreferrer" class="">TouchSocket 官网</a></li>
<li class=""><a href="https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.ratelimiting" target="_blank" rel="noopener noreferrer" class="">System.Threading.RateLimiting 库</a></li>
<li class=""><a href="https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/" target="_blank" rel="noopener noreferrer" class="">Announcing Rate Limiting for .NET</a></li>
<li class=""><a href="https://blog.csdn.net/qq_40374647/article/details/125496769" target="_blank" rel="noopener noreferrer" class="">以前的限流博客文章</a></li>
</ol>
<h2 class="anchor anchorTargetHideOnScrollNavbar_vjPI" id="八本文示例-demo">八、本文示例 Demo<a href="https://touchsocket.net/blog/TcpServiceLimiting#%E5%85%AB%E6%9C%AC%E6%96%87%E7%A4%BA%E4%BE%8B-demo" class="hash-link" aria-label="八、本文示例 Demo的直接链接" title="八、本文示例 Demo的直接链接" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>信息</div><div class="admonitionContent_BuS1"><p>本文所涉及所有技术均是开源的，大家可以直接按照教程搭建项目。</p><p>但是长期以往，我们对于 Pro 用户的福利实在是遗憾。所以成品示例，我们仅为企业版 Pro 用户提供。</p><p>我们也欢迎大家通过 Pro 的<a href="https://touchsocket.net/docs/current/enterprise" target="_blank" rel="noopener noreferrer" class="">购买</a>，来支持我们和我们开源的项目。</p></div></div>
<div class="card-link"><div class="card-content"><svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" class="icon" viewBox="0 0 1031 1024"><path fill="#E3E3E3" d="M946.9 765.9H84.7C38.1 765.9 0 727.8 0 681.2V127.4c0-46.6 38.1-84.7 84.7-84.7h862.2c46.6 0 84.7 38.1 84.7 84.7v553.8c0 46.6-38.1 84.7-84.7 84.7"></path><path fill="#475762" d="M946.9 991.5H84.7C38.1 991.5 0 953.4 0 906.8V268.4h1031.5v638.5c.1 46.5-38 84.6-84.6 84.6"></path><path fill="#CACACA" d="M163.304 234.063a22.17 22.17 0 1 0 44.34 0 22.17 22.17 0 1 0-44.34 0m63.343 0a22.17 22.17 0 1 0 44.34 0 22.17 22.17 0 1 0-44.34 0m63.343 0a22.17 22.17 0 1 0 44.34 0 22.17 22.17 0 1 0-44.34 0m-56.236 175.816H200.46c-10.042 0-18.23-8.189-18.23-18.23 0-10.043 8.188-18.231 18.23-18.231h33.294c10.042 0 18.23 8.188 18.23 18.23s-8.188 18.23-18.23 18.23z"></path><path fill="#F26397" d="M309.7 379.8H239c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h70.7c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m22.6 89.2H298c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h34.3c13 0 23.6 10.6 23.6 23.6S345.2 469 332.3 469m52.3 89.2h-32.8c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h32.8c13 0 23.6 10.6 23.6 23.6s-10.7 23.6-23.6 23.6"></path><path fill="#6ACEB1" d="M597.359 547.688H480.097c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h117.262c10.042 0 18.23 8.188 18.23 18.23-.077 10.042-8.265 18.23-18.23 18.23"></path><path fill="#5F7786" d="M785.2 558.2h-63.6c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h63.6c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m139.5 82H777c-13 0-23.6-10.6-23.6-23.6S764 593 777 593h147.7c13 0 23.6 10.6 23.6 23.6 0 12.9-10.7 23.6-23.6 23.6M775 736.6h-70.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6H775c13 0 23.6 10.6 23.6 23.6s-10.7 23.6-23.6 23.6m-81.8 89.2h-70.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h70.7c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m231.5-267.6h-63.6c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h63.6c13 0 23.6 10.6 23.6 23.6s-10.7 23.6-23.6 23.6M917 736.6h-20.5c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6H917c13 0 23.6 10.6 23.6 23.6S930 736.6 917 736.6"></path><path fill="#6ACEB1" d="M575.3 469H423.5c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h151.8c13 0 23.6 10.6 23.6 23.6S588.3 469 575.3 469m47.2 171.2H470.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h151.8c13 0 23.6 10.6 23.6 23.6-.1 12.9-10.7 23.6-23.6 23.6m0 96.4H470.7c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h151.8c13 0 23.6 10.6 23.6 23.6-.1 13-10.7 23.6-23.6 23.6m-114.9 89.2h-36.9c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h36.9c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6"></path><path fill="#F26397" d="M418.685 616.593h-26.496c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h26.496c10.042 0 18.23 8.188 18.23 18.23s-8.188 18.23-18.23 18.23m0 68.905h-26.496c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h26.496c10.042 0 18.23 8.188 18.23 18.23s-8.188 18.23-18.23 18.23m-39.937 68.905H346.69c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h32.058c10.042 0 18.23 8.187 18.23 18.23-.077 10.042-8.265 18.23-18.23 18.23m-26.96 64.965h-54.614c-10.042 0-18.23-8.188-18.23-18.23s8.188-18.23 18.23-18.23h54.614c10.043 0 18.23 8.188 18.23 18.23s-8.187 18.23-18.23 18.23"></path><path fill="#CACACA" d="M151.8 469h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6S164.8 469 151.8 469m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6S95.7 511 108.7 511h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6s-10.6 23.6-23.6 23.6m0 89.2h-43.1c-13 0-23.6-10.6-23.6-23.6s10.6-23.6 23.6-23.6h43.1c13 0 23.6 10.6 23.6 23.6S164.8 915 151.8 915"></path></svg><h3>LimitConsoleApp</h3></div><div class="card-links"><a href="https://github.com/RRQM/PrivateTouchSocketPro/tree/main/examples-pro/LimitConsoleApp" class="link-button github" target="_blank" rel="noopener noreferrer"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" class="link-icon"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>GitHub</a></div><div class="pro-badge"><a href="https://touchsocket.net/docs/current/enterprise">Pro</a></div></div>]]></content:encoded>
            <category>TcpClient</category>
            <category>TcpService</category>
        </item>
    </channel>
</rss>