Windows Server 2022上IIS部署Blazor Server的网络连接问题
Blazor Server部署与运行异常问题排查
问题描述
- 问题1(影响生产环境):在IIS、IIS Express和Kestrel环境中均出现错误:
Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message from the server.',但Blazor Server模板应用无此超时问题。 - 问题2:使用Kestrel开发时,浏览器访问HTTPS端口(
https://localhost:5101/my-tags)出现连接意外关闭错误,HTTP端口可正常访问;Blazor Server模板应用在Kestrel的HTTPS端口也存在此问题。
相关配置信息
启动设置(launch settings)
"profiles": { "JBFTags": { "commandName": "Project", "launchBrowser": true, "launchUrl": "my-tags?login=jMVt+ABJXf87QX52eeGLOGAnA6BPYUPMnoqaZUiVvoc=", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, "applicationUrl": "https://localhost:5101;http://localhost:5100" }, "JBFTagshttponly": { "commandName": "Project", "launchBrowser": true, "launchUrl": "my-tags?login=jMVt+ABJXf87QX52eeGLOGAnA6BPYUPMnoqaZUiVvoc=", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, "applicationUrl": "http://localhost:5100" }, "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "launchUrl": "my-tags?login=m57T6dYR2EjA==", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } }, "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:19716", "sslPort": 44396 } }
Program.cs代码
using NLog; var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("JBF Web App started!"); var builder = WebApplication.CreateBuilder(args); try { // file gets locked so close it in case it's open try { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Telerik Reporting", "WebReportDesignerSettings.json"); File.Create(path).Close(); } catch (Exception ex) { logger.Warn("Path error: {0}", ex.Message); } // Add services to the container. builder.Services.AddCors(c => { c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); }); builder.Services.AddScoped<SizeService>(); builder.Services.AddServerSideBlazor() .AddHubOptions(options => { options.ClientTimeoutInterval = TimeSpan.FromMinutes(2); // Client timeout options.HandshakeTimeout = TimeSpan.FromMinutes(2); // Handshake timeout options.KeepAliveInterval = TimeSpan.FromMinutes(1); // Keep-alive interval }); builder.Services.AddScoped<DialogService>(); builder.Services.AddScoped<NotificationService>(); builder.Services.AddScoped<TooltipService>(); builder.Services.AddScoped<ContextMenuService>(); builder.Services.AddScoped<IMyIdService, MyIdService>(); builder.Services.AddScoped<SelectedTagsService>(); builder.Services.AddScoped<JSServices>(); builder.Services.AddScoped<ICookieService, CookieService>(); builder.Services.AddHttpContextAccessor(); builder.Services.AddSingleton<UserAgentDetecter>(); builder.Services.AddSingleton<HostService>(); builder.Services.AddSingleton<ExportService>(); builder.Services.AddSingleton<TagsSqlService>(); builder.Services.AddScoped<IIdentitySQLService, IdentitySQLService>(); builder.Services.AddScoped<ToasterService>(); builder.Services.AddScoped<ITagsEFRepo, TagsEFRepo>(); builder.Services.AddScoped<IAspNetEfRepo, AspNetEfRepo>(); builder.Services.AddControllers(); builder.Services.AddRazorPages().AddNewtonsoftJson(); builder.Services.AddDbContext<TagDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("JBFSqlServer2005"))); builder.Services.AddDbContext<AspNetDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("JBFASPNETDB"))); var reportsPath = Path.Combine(builder.Environment.ContentRootPath, "Reports"); // Configure dependencies for ReportsController. builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp => new ReportServiceConfiguration { ReportingEngineConfiguration = sp.GetService<IConfiguration>(), HostAppId = "JBFTags", Storage = new FileStorage(), ReportSourceResolver = new TypeReportSourceResolver() .AddFallbackResolver(new UriReportSourceResolver(reportsPath)) }); builder.Services.AddCors(options => { options.AddPolicy("AllowBlazorOrigin", builder => { builder.WithOrigins("http://localhost:5000", "https://localhost:5001") .AllowAnyHeader() .AllowAnyMethod(); }); }); // Configure dependencies for ReportDesignerController. builder.Services.TryAddSingleton<IReportDesignerServiceConfiguration>(sp => new ReportDesignerServiceConfiguration { DefinitionStorage = new FileDefinitionStorage(reportsPath, new[] { "Resources", "Shared Data Sources" }), ResourceStorage = new ResourceStorage(Path.Combine(reportsPath, "Resources")), SharedDataSourceStorage = new FileSharedDataSourceStorage(Path.Combine(reportsPath, "Shared Data Sources")), SettingsStorage = new FileSettingsStorage(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Telerik Reporting")) }); // NLog: Setup NLog for Dependency injection builder.Logging.ClearProviders(); builder.Host.UseNLog(); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); app.UseCors("AllowBlazorOrigin"); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.MapControllers(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); app.UseWebSockets(); app.Run(); // Log the ports the application is listening on logger.Info("listing ports"); var serverAddresses = app.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>(); foreach (var address in serverAddresses.Addresses) { logger.Info("Listening on: {0}", address); } } catch (Exception e) { logger.Error(e, "Stopped program because of exception"); throw; } finally { NLog.LogManager.Shutdown(); }
Netstat输出及系统版本
TCP 127.0.0.1:5100 0.0.0.0:0 LISTENING *** HTTP TCP 127.0.0.1:5101 0.0.0.0:0 LISTENING *** HTTPS
OS Name: Microsoft Windows Server 2022 Standard OS Version: 10.0.20348 N/A Build 20348 OS Manufacturer: Microsoft Corporation OS Configuration: Standalone Server OS Build Type: Multiprocessor Free BIOS Version: Proxmox distribution of EDK II 4.2023.08-4, 2/15/2024
排查建议
针对问题1(连接超时)
- 匹配客户端与服务端Hub配置:确认Blazor客户端的超时参数是否与服务端
ClientTimeoutInterval、KeepAliveInterval一致,避免客户端提前判定连接超时。 - 排查主线程阻塞:检查Telerik Reporting文件操作、数据库查询等逻辑是否存在长时间阻塞,导致无法及时响应Hub心跳消息。
- 对比模板应用差异:逐一比对现有应用与Blazor Server模板的中间件顺序、服务注册项,排查自定义组件对Hub通信的干扰。
- 细化Hub日志:在Hub的
OnConnectedAsync、OnDisconnectedAsync事件中添加详细日志,追踪超时发生的具体阶段。
针对问题2(HTTPS端口连接关闭)
- 验证HTTPS证书:重新生成并信任开发环境证书(执行
dotnet dev-certs https --trust),检查证书是否有效。 - 调整中间件顺序:将
app.UseWebSockets();移至app.UseRouting();之前,确保WebSocket中间件在路由匹配前生效。 - 更换HTTPS端口测试:临时修改Kestrel的HTTPS端口,排除端口冲突或环境限制问题。
- 禁用HTTPS重定向验证:临时注释
app.UseHttpsRedirection();,确认是否因重定向逻辑导致连接异常。
内容的提问来源于stack exchange,提问作者Darryl Wagoner WA1GON




