在 Linux 上运行 .NET 网站,通过
HttpContext.Connection.RemoteIpAddress
获取客户端的 IP 地址,结果是
::ffff:127.0.0.1
解决方法:
打开 Program.cs 文件,在 var app = builder.Build(); 之前(尽量往前)添加以下代码:
if (OperatingSystem.IsLinux())
{
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor
| ForwardedHeaders.XForwardedProto
| ForwardedHeaders.XForwardedHost;
// 清除 KnownNetworks 和 KnownProxies,表示信任来自本机的代理(如 Nginx)
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Console.WriteLine("ForwardedHeaders enabled (Running on Linux)");
}
然后在 app.UseRouting(); 之前添加以下代码:
if (OperatingSystem.IsLinux())
{
app.UseForwardedHeaders();
Console.WriteLine("UseForwardedHeaders() applied.");
}
其中,OperatingSystem.IsLinux() 用于判断只在 Linux 环境中生效,你可以视自身情况作判断。

早前记录过在 CentOS 中部署 ASP.NET Core 网站,现在宝塔面板已经直接支持创建 .NET 网站了,再次记录一下。
本文环境:VS2022、.NET 9、宝塔面板v11。
一、在 VS 中创建一个 .NET 9 网站,项目名称例:WebApplication1
发布选项:
关于“生成单个文件”选项,若勾选,发布后的项目启动文件不带后缀名(.dll),尝试在宝塔面板 v11.0.0 中无法顺利启动。
二、在 Linux 服务器上创建目录:/www/wwwroot/WebApplication1/
将发布后的文件上传到这个目录上,最终的文件结构如下:
三、进入宝塔面板,在 网站-Net项目 中安装 .Net环境管理,这里以 9.0.201 为例:
四、添加项目
项目名称:随意
运行路径:项目所在目录,本例中为:
/www/wwwroot/WebApplication1
启动命令:使用 dotnet 命令,dotnet 命令可以不使用全路径(/www/server/dotnet/9.0.201/dotnet),第一个参数是项目启动文件,--urls 是反向代理的服务器和端口。服务器名可以是*也可以是localhost。例:
dotnet WebApplication1.dll --urls=http://*:5000
项目端口:与启动命令中的端口号一致
开机启动:一般会勾选
启动用户:尽量用 www,最小权限原则。
正常情况下,网站已启动,如果未启动,检查配置,根据报错内容排查问题。
五、配置网站
添加域名、SSL 证书等。
友情提示:
网站版本更新重新发布后,需要手动重启网站才能生效,习惯于发布到 IIS(会自动生效)的同学要特别注意。
新手尽量以空项目或默认项目来部署,避免因特殊原因导致一系列其它问题。
nginx 反向代理并不会转发所有 Header,需要手动配置。

AutoUpdater.NET 是一个开源库,专为 .NET 桌面应用程序设计,支持 Windows Forms 和 WPF 应用。它通过从服务器获取 XML 文件来检测新版本信息,当发现新版本时向用户显示更新对话框。
相对于 ClickOnce,AutoUpdater.NET 的配置更简单一些。
首先通过 NuGet 包管理器安装 Autoupdater.NET.Official,然后在应用程序入口点添加以下代码:
AutoUpdater.Start("http://yourserver.com/path/to/update.xml");
XML 文件结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>2.0.0.0</version> <!--必填:最新版本号-->
<url>http://yourserver.com/path/to/updatefile.zip</url> <!--必填:更新文件下载地址-->
<changelog>http://yourserver.com/path/to/changelog.txt</changelog> <!--可选:更新日志-->
<mandatory>False</mandatory> <!--可选:是否强制更新-->
</item>
Windows Forms 在 Program.cs 文件的 Main() 方法中,WPF 在 App.xaml.cs 的 OnStartup() 中添加:
AutoUpdater.Start("http://yourserver.com/path/to/update.xml");
AutoUpdater.CheckForUpdateEvent += (e) =>
{
if (e.Error != null)
{
MessageBox.Show($"检查版本更新失败:{e.Error.Message}");
}
if (e.IsUpdateAvailable)
{
if (e.Mandatory.Value)
{
// 强制更新
AutoUpdater.DownloadUpdate(e);
}
else
{
// 可选更新
if (MessageBox.Show("发现新版本,是否立即更新?", "更新提示", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
AutoUpdater.DownloadUpdate(e);
}
}
}
};
这样就能简单实现打开应用时判断是否有更新。具体用法参:GitHub
对于控制台应用程序,AutoUpdater.NET 并不直接支持。可以使用 GeneralUpdate 等轻量级自动更新库。
其它:
若希望通过标准安装流程(如添加到开始菜单),优先选择 ClickOnce,适合长期维护的内部工具。
若追求快速集成和无感更新,AutoUpdater.NET 更灵活。
无论哪种方案,务必对程序进行代码签名(如购买企业证书或生成测试证书),否则系统可能拦截安装。(你要允许来自未知发布者的此应用对你的设备进行更改吗)
可以购买“OV 代码签名”证书或“EV 代码签名”证书,注意:“代码签名证书”与“域名证书”互不通用。

目标: 在运行 HarmonyOS Next 的华为 MateBook Pro 上侧载安装第三方应用程序。
注:本教程同样适用于在运行 HarmonyOS Next 的华为手机上侧载安装第三方应用程序。
所需准备
一台运行 HarmonyOS Next 的华为 MateBook Pro。
一台 Windows 电脑。
一根数据线:
USB-A to Type-C
或 Type-C to Type-C (确保能将两台电脑连接)。
网络连接:两台电脑需在同一局域网下(用于无线调试连接)。
步骤详解
第一步:鸿蒙 PC (MateBook Pro) 端设置 (启用开发者模式与调试)
连接设备: 使用数据线将鸿蒙 MateBook Pro 连接到 Windows 电脑。
打开设置: 在鸿蒙 PC 上,进入
设置
。进入关于本机: 在设置中,找到并点击
关于本机
或您的电脑型号
。启用开发者模式:
在“关于本机”界面,找到
软件版本
或HarmonyOS 版本
。连续点击
软件版本/HarmonyOS 版本
文字 5 次以上。系统会提示“您已处于开发者模式”或提示重启进入开发者模式。按提示重启电脑(如果需要)。
打开开发者选项:
重启后,回到
设置
->系统
->开发者选项
(路径可能略有不同,在“系统和更新”或“隐私与安全”下查找)。启用 USB 调试:
在开发者选项中,找到
USB 调试
,将其开启。首次开启时,可能会弹出提示(连接线后或稍后操作时),点击
允许
。注意提示:系统可能提示“Matebook Pro 上的右上角的 Type-C 口可以进行调试”,确保使用正确的接口连接调试线。如果弹出“始终允许本机调试”选项,勾选并允许。
启用并记录无线调试信息:
在开发者选项中,找到
无线调试
,将其开启。开启后,系统会显示一个 IP 地址和端口号(例如
192.168.x.x:xxxxx
)。务必准确记下这个 IP 地址和端口号,后续在 Windows 端连接时必须用到。至此,鸿蒙 PC 端的准备工作完成。
第二步:Windows 电脑端操作 (使用小白调试助手)
获取工具和 HAP 包:
访问包含 HarmonyOS Next 可用 HAP 包的资源(如 GitHub 上的相关仓库,搜索 “HarmonyOS Next HAP”)。
下载小白调试助手:
在工具仓库中找到
小白调试助手
或类似名称。点击
Latest
(最新版本)。点击
Download
下载其 Windows 版压缩包(.zip
文件)。下载目标应用的 HAP 包(以 ClashBox 为例,你也可以使用自己已有的 .hap 文件):
在应用仓库中找到
ClashBox
。同样点击
Latest
->Download
下载其 HAP 包(.hap
文件)。准备小白调试助手:
将下载的小白调试助手压缩包解压到一个非中文且无空格路径的文件夹。
进入解压后的文件夹,找到可执行文件。双击运行。根据 Windows SmartScreen 或安全软件提示(若有),选择
更多信息
->仍要运行
。登录华为开发者账号 (强烈推荐):
在小白调试助手界面,找到登录入口(通常在界面顶部或设置中),点击
登录
。使用您的华为开发者账号登录(登录时,您的华为手机可能会收到验证码,或在鸿蒙PC上验证)。
输入验证码,并在授权请求页面点击
允许
。登录成功后,界面通常会显示您的账号昵称或ID。重要: 开发者账号签发的应用证书有效期通常为 6 个月,普通账号仅 14 天。为方便使用,建议申请华为开发者账号。
连接鸿蒙设备:
在小白调试助手主界面,找到
连接设备
、添加设备
或类似功能的按钮并点击。在弹出的连接窗口(通常是输入IP和端口的对话框)中:
输入您在鸿蒙 PC 上
无线调试
功能中记录的 IP 地址。输入记录的 端口号。
点击
确定
、连接
或OK
。连接成功:如果地址和端口输入正确,且鸿蒙 PC 的开发者选项已开启无线调试:
鸿蒙 PC 可能弹出调试请求(“允许调试?”),勾选“始终允许”并点击
允许
。小白调试助手界面会显示“连接成功”或目标设备信息。
(若未弹出提示框但连接成功,也属正常)
第三步:安装第三方 HAP 包
选择 HAP 包:
确保设备已连接成功。
在小白调试助手界面,找到
选择 HAP
、加载 HAP
、安装应用
或类似按钮(通常在文件菜单或主功能区)。点击该按钮,浏览文件系统,找到您下载好的目标 HAP 文件(如
ClashBox-xxx.hap
),选中并点击打开
。开始安装:
选择好 HAP 文件后,小白调试助手界面通常会激活
开始调试
、安装
或运行
按钮。点击
开始调试
或安装
。等待安装完成:
小白调试助手会开始处理 HAP 包:进行签名、推送到设备并安装。
观察进度条或日志输出,耐心等待直至提示“安装成功”。
第四步:在鸿蒙 PC 上使用安装的应用
安装完成后,返回您的鸿蒙 MateBook Pro。
在桌面或
开始菜单
中,查找您刚刚安装的应用图标(如ClashBox
)。点击图标即可启动使用该应用。
常见问题处理
签名错误/证书问题:在小白调试助手内寻找 **
清理缓存
、重置证书
、重新登录
** 或类似选项。执行后,重新登录开发者账号,再尝试安装。确保登录的是开发者账号。Java 环境报错:如果小白调试助手提示需要 Java 环境(如
java
命令未找到),按照其提示点击安装,它会引导下载并安装所需的 Java Runtime Environment (JRE)。连接失败:
检查鸿蒙 PC 的
无线调试
IP 和端口号是否变化(息屏、重启、网络切换可能导致变化),在开发者选项里重新确认并输入。确保两台电脑在同一局域网。
检查鸿蒙 PC 的 **
USB 调试
和无线调试
是否已开启**。尝试在鸿蒙 PC 的开发者选项中关闭再重新开启
无线调试
,获取新的端口号。其他报错:查阅小白调试助手官方的使用说明或 GitHub 仓库的 Issue 区寻求解决方案。

安装“腾讯手机管家”或其它支持按关键词设置黑名单的垃圾信息过滤工具
打开手机设置 - App - 信息 - 未知与过滤信息 - 选择“腾讯手机管家”
打开“腾讯手机管家”- 垃圾短信过滤 - 自定义短信拦截 - 黑名单 - 添加关键词
(每次添加一个词就确定,如果填写多个表示同一条短信中同时包含这些关键词)

在代码中添加“不跟踪”(No-Tracking)功能,以提高查询性能(避免实体状态跟踪)。
确保后续操作无需更新返回的实体(如没有 SaveChanges 操作)。
在条件(Where)、排序(OrderBy、OrderByDescending)、分页(Skip、Take)等之前添加 .AsNoTracking(),如:db.Table.AsNoTracking().Where(...).ToList()。
如果查询中包含导航属性,它们也会因主查询的不跟踪而保持不跟踪状态。
AsNoTracking() 适用于查询简单无嵌套关系,若查询包含 Include/ThenInclude,建议用 AsNoTrackingWithIdentityResolution() 代替,后者更适合处理树形结构或循环引用数据。
添加(Add/AddRange)、修改、删除(Remove/RemoveRange)等有 SaveChanges 操作的场景不能加 .AsNoTracking()。
查询时是否需要加 .AsNoTracking() 参考下表:
数据查询操作体系
├── 限定符 (Any, All) ✔️ 推荐
├── 聚合函数 (Count, Sum, Min, Max, Average) ❌ 不需要(但在分页逻辑中 Count 与 ToList 使用共同条件筛选时建议加)
├── 集合操作 (Distinct, Union) ⚠️ 推荐用于只读
├── 元素操作 (First, Single) ⚠️ 推荐用于只读
└── 即时执行操作 (ToList, ToArray, ToDictionary) ⚠️ 推荐用于只读

临界区与 lock 关键字
核心作用:
通过将多线程访问串行化,保护共享资源或代码段。lock 关键字是 Monitor 类的语法糖,提供异常安全的临界区实现。
实现示例:
// 创建私有静态只读对象 // private static readonly object _lockObj = new object(); private static readonly System.Threading.Lock _locker = new(); // .NET 9+ 推荐使用 Lock 类型,避免传统 object 的性能损耗 public void ThreadSafeMethod() { lock (_lockObj) { // 临界区代码(每次仅一个线程可进入) } }
超时机制:
高并发场景可结合 Monitor.TryEnter 设置超时,避免无限等待:
if (Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(1))) { try { /* 操作 */ } finally { Monitor.Exit(lockObject); } }
关键特性:
用户态锁(无内核切换开销)
自动调用Monitor.Enter和Monitor.Exit
必须使用专用私有对象作为锁标识
注意事项:
❌ 避免锁定this、Type实例或字符串(易引发死锁)
❌ 避免嵌套锁(需严格按顺序释放)
✅ 推荐readonly修饰锁对象
❌ lock 不适用于异步代码(async/await),需使用 SemaphoreSlim 实现异步锁
互斥锁(Mutex)
核心作用:
系统级内核锁,支持跨进程同步,但性能开销较高(用户态/内核态切换)。
实现示例:
using var mutex = new Mutex(false, "Global\\MyAppMutex"); try { // 等待锁(最大等待时间500ms) if (mutex.WaitOne(500)) { // 临界区代码 } } finally { if (mutex != null) { mutex.ReleaseMutex(); } }
关键特性:
支持跨应用程序域同步
线程终止时自动释放锁
支持命名互斥体(系统全局可见)
适用场景:
单实例应用程序控制
进程间共享文件访问
硬件设备独占访问
信号量(Semaphore)
核心作用:
通过许可计数器控制并发线程数,SemaphoreSlim为轻量级版本(用户态实现)。
实现对比:
类型 跨进程 性能 最大许可数 Semaphore ✔️ 低 系统限制 SemaphoreSlim ❌ 高 Int32.Max 代码示例:
// 创建初始3许可、最大5许可的信号量 var semaphore = new SemaphoreSlim(3, 5); semaphore.Wait(); // 获取许可 try { // 资源访问代码 } finally { semaphore.Release(); }
异步编程
private readonly SemaphoreSlim _asyncLock = new(1, 1); public async Task UpdateAsync() { await _asyncLock.WaitAsync(); try { /* 异步操作 */ } finally { _asyncLock.Release(); } }
典型应用:
数据库连接池(限制最大连接数)
API 请求限流
批量任务并发控制
事件(Event)
核心机制:
通过信号机制实现线程间通知,分为两种类型:
类型 信号重置方式 唤醒线程数 AutoResetEvent 自动 单个 ManualResetEvent 手动 所有 使用示例:
var autoEvent = new AutoResetEvent(false); // 等待线程 Task.Run(() => { autoEvent.WaitOne(); // 收到信号后执行 }); // 信号发送线程 autoEvent.Set(); // 唤醒一个等待线程
高级用法:
配合WaitHandle.WaitAll实现多事件等待
使用ManualResetEventSlim提升性能
读写锁(ReaderWriterLockSlim)
核心优势:
实现读写分离的并发策略,适合读多写少场景(如缓存系统)。
锁模式对比:
模式 并发性 升级支持 读模式(EnterReadLock) 多线程并发读 ❌ 写模式(EnterWriteLock) 独占访问 ❌ 可升级模式 单线程读→写 ✔️ 代码示例:
var rwLock = new ReaderWriterLockSlim(); // 读操作 rwLock.EnterReadLock(); try { // 只读访问 } finally { rwLock.ExitReadLock(); } // 写操作 rwLock.EnterWriteLock(); try { // 排他写入 } finally { rwLock.ExitWriteLock(); }
最佳实践:
优先使用ReaderWriterLockSlim(旧版有死锁风险)
避免长时间持有读锁(可能饿死写线程)
原子操作(Interlocked)
原理:
通过CPU指令实现无锁线程安全操作。
常用方法:
int counter = 0; Interlocked.Increment(ref counter); // 原子递增 Interlocked.Decrement(ref counter); // 原子递减 Interlocked.CompareExchange(ref value, newVal, oldVal); // CAS操作
适用场景:
简单计数器
标志位状态切换
无锁数据结构实现
自旋锁(SpinLock)
核心特点:
通过忙等待(busy-wait)避免上下文切换,适用极短临界区(<1微秒)。
实现示例:
private SpinLock _spinLock = new SpinLock(); public void CriticalOperation() { bool lockTaken = false; try { _spinLock.Enter(ref lockTaken); // 极短临界区代码 } finally { if (lockTaken) _spinLock.Exit(); } }
优化技巧:
单核CPU需调用Thread.SpinWait或Thread.Yield
配合SpinWait结构实现自适应等待
同步机制对比指南
机制 跨进程 开销级别 最佳适用场景 lock ❌ 低 通用临界区保护 Mutex ✔️ 高 进程间资源独占 Semaphore ✔️ 中 并发数限制(跨进程) SemaphoreSlim ❌ 低 并发数限制(进程内) ReaderWriterLockSlim ❌ 中 读多写少场景 SpinLock ❌ 极低 纳秒级临界区 Interlocked - 无锁 简单原子操作
选择原则:
优先考虑用户态锁(lock/SpinLock/SemaphoreSlim)
跨进程需求必须使用内核对象(Mutex/Semaphore)
读写比例超过10:1时考虑读写锁
自旋锁仅用于高频短操作(如链表指针修改)
通过以上结构化的分类和对比,开发者可以更精准地选择适合特定场景的线程同步方案。建议在实际使用中配合性能分析工具(如BenchmarkDotNet)进行量化验证。
💡 ASP.NET 的异步编程(async/await)本质是单进程内的线程调度,不算“跨进程”。每个 IIS 应用程序池对应一个独立的工作进程(w3wp.exe),不同用户访问同一应用程序池下的 ASP.NET 网站,两者的请求均由同一个 w3wp.exe 进程处理。可能跨进程的场景有:Web Garden 配置、多应用程序池部署等。
在 C# 中,除了常规锁机制(如 lock、Mutex、Semaphore 等),还有一些内置类型通过内部锁或无锁设计实现线程安全。以下是常见的几类:
线程安全集合(System.Collections.Concurrent)这些集合通过细粒度锁或无锁算法(如 CAS)实现线程安全,适合高并发场景。
ConcurrentDictionary:分段锁机制,将数据分片存储,每个分片独立加锁,减少锁竞争。
ConcurrentQueue / ConcurrentStack:基于原子操作(Interlocked)保证线程安全。
ConcurrentBag:每个线程维护本地存储,减少争用,适合频繁添加和移除的场景。
BlockingCollection:基于 ConcurrentQueue 和信号量(SemaphoreSlim)实现生产-消费者模式,支持阻塞和超时。
不可变集合(System.Collections.Immutable) 通过数据不可变性实现线程安全(无需锁),每次修改返回新对象。
Lazy 的线程安全初始化(Lazy<T>) 通过锁或 Interlocked 确保延迟初始化的线程安全。
通道(System.Threading.Channels)用于异步生产-消费者模型,内部通过锁和信号量管理容量限制。
内存缓存(System.Runtime.Caching.MemoryCache)内部使用锁保护共享状态,确保线程安全。
原子操作类型(Interlocked 类、Volatile 关键字、Unsafe 类)通过 CPU 指令实现无锁线程安全。
其他同步工具(Barrier、CountdownEvent)虽然不是严格意义上的锁,但用于协调线程。

打开 .csproj 项目文件,在 <PropertyGroup> 标签内添加:
<Version>
1.0.$([System.Math]::Floor($([System.DateTime]::Now.Subtract($([System.DateTime]::Parse('2000-01-01T00:00:00Z'))).TotalDays))).$([MSBuild]::Divide($([System.Math]::Floor($([System.DateTime]::Now.TimeOfDay.TotalSeconds))), 2))
</Version>
最终生成的版本号示例: 1.0.9238.28518
其中,Major 与 Minor 是固定的,Build 是2000年1月1日至今的天数,Revision 是今天的秒数 / 2 所得的值。(为了防止数值超过 65535)
程序中获取版本号:
var version = Assembly.GetExecutingAssembly().GetName().Version!; // 当前类库
var version = Assembly.GetEntryAssembly()?.GetName().Version!; // 入口项目
从版本号获取发布时间:
DateTime versionTime = new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);

在解决方案资源管理器中找到 Properties/AssemblyInfo.cs 文件。该文件存放程序集版本信息。
修改版本号格式
将以下代码片段中的 AssemblyVersion 改为使用星号通配符(建议保留主版本和次版本号):
[assembly: AssemblyVersion("1.0.*")] // 自动生成构建号和修订号 // [assembly: AssemblyFileVersion("1.0.0.0")] // 注释或删除此行
关闭确定性构建
用文本编辑器打开 .csproj 项目文件,在 <PropertyGroup> 标签内添加:
<Deterministic>false</Deterministic>
此设置允许 MSBuild 生成动态版本号。
最终生成的版本号示例: 1.0.9238.28518
其中,Major 与 Minor 是固定的,Build 是2000年1月1日至今的天数,Revision 是今天的秒数 / 2 所得的值。(为了防止数值超过 65535)
程序中获取版本号:
var version = Assembly.GetExecutingAssembly().GetName().Version;
从版本号获取发布时间:
DateTime versionTime = new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);
查看 .NET Core / .NET 5+ 实现自动版本号的方法

Ollama 默认是仅本地访问,接口地址是:http://localhost:11434
开放外部访问需要设置两个环境变量:
OLLAMA_HOST=0.0.0.0
OLLAMA_ORIGINS=*
第一步,设置环境变量
macOS:
在终端中运行命令
launchctl setenv OLLAMA_HOST "0.0.0.0"
launchctl setenv OLLAMA_ORIGINS "*"
Windows:
打开“环境变量”配置框
添加两个用户变量:
Linux:
使用 systemctl 设置环境变量
调用 systemctl edit ollama.service 编辑 systemd 服务配置
在 [Service] 下方添加:
[Service]
Environment="OLLAMA_HOST=0.0.0.0"
Environment="OLLAMA_ORIGINS=*"
第二步,重启 Ollama
环境变量配置完成后,重启 Ollama。
第三步,开放防火墙端口
Windows:
进入“高级安全 Windows Defender 防火墙” → “入站规则” → 新建规则 → 允许 TCP 端口 11434。
第四步,验证
获取本机局域网 IP。
Windows:ipconfig
Linux/macOS:ifconfig
访问网址:http://<ip>:11434
若返回 Ollama is running 则表示连接正常。
