SignalR 疑难解答

作者 :Patrick Fletcher

警告

本文档不适用于最新版本的 SignalR。 查看 ASP.NET Core SignalR

本文档介绍 SignalR 的常见故障排除问题。

本主题中使用的软件版本

本主题的早期版本

有关 SignalR 早期版本的信息,请参阅 SignalR 旧版本

问题和评论

请留下反馈,说明你如何喜欢本教程,以及我们可以在页面底部的评论中改进的内容。 如果你有与本教程不直接相关的问题,可以将其发布到 ASP.NET SignalR 论坛StackOverflow.com

本文档包含以下部分。

在客户端和服务器之间调用方法以无提示方式失败

本部分介绍客户端和服务器之间方法调用失败而不显示有意义的错误消息的可能原因。 在 SignalR 应用程序中,服务器没有有关客户端实现的方法的信息;当服务器调用客户端方法时,方法名称和参数数据将发送到客户端,并且仅当该方法以服务器指定的格式存在时才执行。 如果在客户端上找不到匹配的方法,则不会发生任何操作,并且不会在服务器上引发错误消息。

若要进一步调查未调用的客户端方法,可以在中心上调用 start 方法之前打开日志记录,以查看来自服务器的调用。 若要在 JavaScript 应用程序中启用日志记录,请参阅 如何 (JavaScript 客户端版本) 启用客户端日志记录 。 若要在 .NET 客户端应用程序中启用日志记录,请参阅 如何启用客户端日志记录 (.NET 客户端版本)

方法拼写错误、方法签名错误或中心名称不正确

如果被调用方法的名称或签名与客户端上的相应方法不完全匹配,则调用将失败。 验证服务器调用的方法名称是否与客户端上方法的名称匹配。 此外,SignalR 使用适用于 JavaScript 的 camel 大小写方法创建中心代理,因此将在客户端代理中调用sendMessage服务器上调用的方法SendMessage。 如果在服务器端代码中使用 HubName 属性,请验证使用的名称是否与用于在客户端上创建中心的名称匹配。 如果不使用 HubName 属性,请验证 JavaScript 客户端中中心的名称是否为 camel 大小写,例如 chatHub 而不是 ChatHub。

客户端上的方法名称重复

验证客户端上是否有仅因大小写而异的重复方法。 如果客户端应用程序有一个名为 sendMessage的方法,请验证是否也没有名为 SendMessage 的方法。

客户端上缺少 JSON 分析程序

SignalR 要求存在 JSON 分析程序来序列化服务器和客户端之间的调用。 如果客户端没有内置的 JSON 分析程序 ((例如 Internet Explorer 7) ),则需要在应用程序中包括一个。 可以 在此处下载 JSON 分析程序。

混合中心和 PersistentConnection 语法

SignalR 使用两种通信模型:Hubs 和 PersistentConnections。 调用这两个通信模型的语法在客户端代码中有所不同。 如果在服务器代码中添加了中心,请验证所有客户端代码是否都使用正确的中心语法。

在 JavaScript 客户端中创建 PersistentConnection 的 JavaScript 客户端代码

var myConnection = $.connection('/echo');

在 Javascript 客户端中创建中心代理的 JavaScript 客户端代码

var myHub = $.connection.MyHub;

将路由映射到 PersistentConnection 的 C# 服务器代码

RouteTable.Routes.MapConnection<MyConnection>("my", "/echo");

将路由映射到中心或多个中心的 C# 服务器代码(如果有多个应用程序)

App.MapSignalR();

添加订阅之前启动的连接

如果在将可从服务器调用的方法添加到代理之前启动中心连接,则不会收到消息。 以下 JavaScript 代码无法正确启动中心:

不允许接收中心消息的 JavaScript 客户端代码不正确

var chat = $.connection.chatHub;
$.connection.hub.start().done(function () {
    chat.client.broadcastMessage = function (name, message) {...};
});

而是在调用 Start 之前添加方法订阅:

将订阅正确添加到中心的 JavaScript 客户端代码

var chat = $.connection.chatHub;
chat.client.broadcastMessage = function (name, message) {...};
    $.connection.hub.start().done(function () {
        ...
    });

中心代理上缺少方法名称

验证是否在客户端上订阅了服务器上定义的方法。 即使服务器定义了 方法,它仍必须添加到客户端代理。 可以通过以下方式将方法添加到客户端代理 (请注意,该方法将添加到 client 中心的成员,而不是直接) 中心:

将方法添加到中心代理的 JavaScript 客户端代码

// Method added to proxy in JavaScript:
myHubProxy.server.method1 = function (param1, param2) {...};
//Multiple methods added to proxy in JavaScript using jQuery:
$.extend(myHubProxy.server, {
    method1: function (param1, param2) {...},
    method2: function (param3, param4) {...}
});

未声明为 Public 的中心或中心方法

若要在客户端上可见,必须将中心实现和方法声明为 public

从其他应用程序访问中心

只能通过实现 SignalR 客户端的应用程序访问 SignalR 中心。 SignalR 无法与其他通信库(如 SOAP 或 WCF Web services) (互操作。) 如果没有可用于目标平台的 SignalR 客户端,则无法直接访问服务器的终结点。

手动序列化数据

SignalR 将自动使用 JSON 来序列化方法参数 - 无需自行执行。

远程中心方法未在 OnDisconnected 函数中的客户端上执行

此行为是设计使然。 调用 时 OnDisconnected ,中心已进入 Disconnected 状态,不允许调用其他中心方法。

正确执行 OnDisconnected 事件中的代码的 C# 服务器代码

public class MyHub : Hub
{
    public override Task OnDisconnected()
    {
        // Do what you want here
        return base.OnDisconnected();
    }
}

OnDisconnect 未在一致时间触发

此行为是设计使然。 当用户尝试离开具有活动 SignalR 连接的页面时,SignalR 客户端将尽最大努力尝试通知服务器客户端连接将停止。 如果 SignalR 客户端的尽力尝试未能到达服务器,则服务器将在可配置 DisconnectTimeout 后释放连接,届时将 OnDisconnected 触发事件。 如果 SignalR 客户端的尽力尝试成功,事件 OnDisconnected 将立即触发。

有关设置 DisconnectTimeout 设置的信息,请参阅 处理连接生存期事件:DisconnectTimeout

已达到连接限制

在 Windows 7 等客户端操作系统上使用完整版本的 IIS 时,将施加 10 个连接限制。 使用客户端 OS 时,请改用 IIS Express 以避免此限制。

跨域连接未正确设置

如果跨域连接 (SignalR URL 与托管页面不在同一域中的连接,) 未正确设置,则连接可能会失败,且不会显示错误消息。 有关如何启用跨域通信的信息,请参阅 如何建立跨域连接

使用 NTLM (Active Directory 的连接) 在 .NET 客户端中不起作用

如果未正确配置连接,则使用域安全性的 .NET 客户端应用程序中的连接可能会失败。 若要在域环境中使用 SignalR,请设置所需的连接属性,如下所示:

实现连接凭据的 C# 客户端代码

connection.Credentials = CredentialCache.DefaultCredentials;

将 IIS websocket 配置为 ping/pong 以检测死客户端

SignalR 服务器不知道客户端是否死机,它们依赖于来自基础 Websocket 的连接失败通知, OnClose 即回调。 此问题的一种解决方案是将 IIS websocket 配置为为你执行 ping/pong。 这可确保连接在意外中断时关闭。 有关详细信息,请参阅 此 stackoverflow 文章

其他连接问题

本部分介绍连接期间发生的特定症状或错误消息的原因和解决方案。

“必须先调用 Start 才能发送数据”错误

如果代码在启动连接之前引用 SignalR 对象,则通常会看到此错误。 连接完成后,必须添加处理程序等将调用服务器上定义的方法的连接。 请注意,对 Start 的调用是异步的,因此调用后的代码可能会在调用完成之前执行。 在连接完全启动后添加处理程序的最佳方法是将它们放入回调函数中,该回调函数作为参数传递给 start 方法:

正确添加引用 SignalR 对象的事件处理程序的 JavaScript 客户端代码

$.connection.hub.start().done(function () {
    // Wire up Send button to call NewContosoChatMessage on the server.
    $('#newContosoChatMessage').click(function () {
        contosoChatHubProxy.server.newContosoChatMessage(
            $('#displayname').val(), $('#message').val());
            $('#message').val('').focus();
    });

如果在仍然引用 SignalR 对象时连接停止,也会出现此错误。

“301 已永久移动”或“302 暂时移动”错误

如果项目包含名为 SignalR 的文件夹,则可能会出现此错误,该文件夹会干扰自动创建的代理。 若要避免此错误,请不要在应用程序中使用名为 SignalR 的文件夹,或关闭自动代理生成。 有关更多详细信息 ,请参阅生成的代理及其用途

.NET 或 Silverlight 客户端中的“403 禁止访问”错误

此错误可能发生在未正确启用跨域通信的跨域环境中。 有关如何启用跨域通信的信息,请参阅 如何建立跨域连接。 若要在 Silverlight 客户端中建立跨域连接,请参阅 从 Silverlight 客户端建立跨域连接

“404 未找到”错误

此问题有多种原因。 验证以下所有内容:

  • 中心代理地址引用的格式不正确: 如果对生成的中心代理地址的引用格式不正确,则通常会看到此错误。 验证是否正确引用了中心地址。 有关详细信息 ,请参阅如何引用动态生成的代理

  • 在添加中心路由之前将路由添加到应用程序: 如果应用程序使用其他路由,请验证添加的第一个路由是否是对 的 MapSignalR调用。

  • 使用 IIS 7 或 7.5,而不更新无扩展 URL: 使用 IIS 7 或 7.5 需要更新无扩展 URL,以便服务器可以提供对 中 /signalr/hubs中心定义的访问。 可 在此处找到更新。

  • IIS 缓存过期或损坏: 若要验证缓存内容是否未过期,请在 PowerShell 窗口中输入以下命令以清除缓存:

    net stop w3svc
    Remove-Item -Path "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\*" -Force -Recurse
    net start w3svc
    

“500 内部服务器错误”

这是一个非常通用的错误,原因可能有多种。 错误的详细信息应显示在服务器的事件日志中,或者可以通过调试服务器找到。 可以通过在服务器上启用详细错误来获取更详细的错误信息。 有关详细信息,请参阅 如何处理中心类中的错误

如果未正确配置防火墙或代理,则通常也会看到此错误,从而导致重写请求标头。 解决方案是确保在防火墙或代理上启用端口 80。

“意外响应代码:500”

如果应用程序中使用的 .NET Framework 版本与 Web.Config 中指定的版本不匹配,则可能会发生此错误。解决方案是验证应用程序设置和 Web.Config 文件中是否使用了 .NET 4.5。

“TypeError: <hubType> is undefined”错误

如果未正确调用 , MapSignalR 则会导致此错误。 有关详细信息 ,请参阅如何注册 SignalR 中间件和配置 SignalR 选项

JsonSerializationException 未由用户代码处理

验证发送到方法的参数是否不包含不可序列化的类型, (如文件句柄或数据库连接) 。 如果需要在服务器端对象上使用不希望发送到客户端的成员, (出于安全性或序列化) 的原因,请使用 JSONIgnore 属性。

“协议错误:未知传输”错误

如果客户端不支持 SignalR 使用的传输,则可能会发生此错误。 有关哪些浏览器可用于 SignalR 的信息,请参阅 传输和回退

“JavaScript 中心代理生成已禁用。”

如果 DisableJavaScriptProxies 设置了 ,同时在 中也包括对动态生成的代理 signalr/hubs的引用,则会发生此错误。 有关手动创建代理的详细信息,请参阅 生成的代理及其用途

“连接 ID 的格式不正确”或“在活动 SignalR 连接期间用户标识无法更改”错误

如果使用身份验证,并且客户端在连接停止之前已注销,则可能会看到此错误。 解决方法是在注销客户端之前停止 SignalR 连接。

“未捕获错误:SignalR: jQuery 未找到。 请确保在SignalR.js文件“错误之前引用 jQuery

SignalR JavaScript 客户端要求运行 jQuery。 验证对 jQuery 的引用是否正确、使用的路径是否有效,以及对 jQuery 的引用是否在对 SignalR 的引用之前。

“未捕获的 TypeError: 无法读取未定义的属性'<property>'”错误

此错误是由于未正确引用 jQuery 或中心代理。 验证对 jQuery 和中心代理的引用是否正确、使用的路径是否有效,以及对 jQuery 的引用是否在对中心代理的引用之前。 对中心代理的默认引用应如下所示:

正确引用中心代理的 HTML 客户端代码

<script src="/signalr/hubs"></script>

“RuntimeBinderException 未由用户代码处理”错误

使用 不正确的重载 Hub.On 时,可能会发生此错误。 如果方法具有返回值,则必须将返回类型指定为泛型类型参数:

在客户端 (上定义的方法,没有生成的代理)

MyHub.On<ReturnType>("MethodName", LocalMethod);

连接 ID 不一致或页面加载之间的连接中断

此行为是设计使然。 由于中心对象托管在页面对象中,因此在页面刷新时会销毁中心。 多页应用程序需要维护用户和连接 ID 之间的关联,以便它们在页面加载之间保持一致。 连接 ID 可以存储在服务器上 ConcurrentDictionary 对象或数据库中。

“值不能为 null”错误

当前不支持具有可选参数的服务器端方法;如果省略可选参数,该方法将失败。 有关详细信息,请参阅可选参数

Firebug 中的“Firefox 无法在地址>处与服务器<建立连接”错误

如果 WebSocket 传输协商失败,而是使用另一个传输,则可以在 Firebug 中看到此错误消息。 此行为是设计使然。

.NET 客户端应用程序中的“远程证书根据验证过程无效”错误

如果服务器需要自定义客户端证书,则可以在发出请求之前将 x509certificate 添加到连接。 使用 Connection.AddClientCertificate将证书添加到连接。

身份验证超时后连接断开

此行为是设计使然。 连接处于活动状态时,无法修改身份验证凭据;若要刷新凭据,必须停止并重启连接。

使用 jQuery Mobile 时,将调用 OnConnected 两次

jQuery Mobile 的 initializePage 函数强制重新执行每个页面中的脚本,从而创建第二个连接。 此问题的解决方案包括:

  • 在 JavaScript 文件之前包括对 jQuery Mobile 的引用。
  • initializePage通过设置 $.mobile.autoInitializePage = false禁用函数。
  • 等待页面完成初始化,然后再启动连接。

使用服务器发送事件在 Silverlight 应用程序中延迟消息

在 Silverlight 上使用服务器发送的事件时,消息会延迟。 若要强制改用长轮询,请在启动连接时使用以下方法:

connection.Start(new LongPollingTransport());

使用 Forever Frame 协议的“权限被拒绝”

这是一个已知问题, 如此处所述。 可以使用最新的 JQuery 库看到此症状;解决方法是将应用程序降级到 JQuery 1.8.2。

“InvalidOperationException:不是有效的 Web 套接字请求。

如果使用 WebSocket 协议,但网络代理正在修改请求标头,则可能会发生此错误。 解决方案是将代理配置为允许端口 80 上的 WebSocket。

客户端在服务器上调用方法时出现“异常: <无法解析方法名称> 方法”

此错误可能是由于使用在 JSON 有效负载中无法发现的数据类型(如 Array)导致的。 解决方法是使用 JSON 可发现的数据类型,例如 IList。 有关详细信息,请参阅 .NET 客户端无法使用数组参数调用中心方法

编译和服务器端错误

以下部分包含编译器和服务器端运行时错误的可能解决方案。

对中心实例的引用为 null

由于为每个连接创建中心实例,因此无法自行在代码中创建中心实例。 若要从中心本身外部调用中心上的方法,请参阅 如何从中心类外部调用客户端方法和管理组 ,了解如何获取对中心上下文的引用。

HTTPContext.Current.Session 为 null

此行为是设计使然。 SignalR 不支持 ASP.NET 会话状态,因为启用会话状态会中断双工消息传送。

没有合适的方法来替代

如果使用旧版文档或博客中的代码,可能会看到此错误。 验证是否未引用已更改或弃用 ((如) ) OnConnectedAsync 的方法的名称。

HostContextExtensions.WebSocketServerUrl 为 null

此行为是设计使然。 此成员已弃用,不应使用。

“名为'signalr.hubs'的路由已在路由集合中”错误

如果 MapSignalR 应用程序调用了两次,则会出现此错误。 一些示例应用程序直接在 Startup 类中调用 MapSignalR ;其他示例应用程序在包装类中调用 。 确保应用程序不会同时执行这两项操作。

未使用 WebSocket

如果已验证服务器和客户端满足支持 的平台 文档) 中列出的 WebSocket (的要求,则需要在服务器上启用 WebSocket。 可 在此处找到执行此操作的说明。

$.connection 未定义

此错误表示页面上的脚本未正确加载,或者中心代理无法访问或访问不正确。 验证页面上的脚本引用是否对应于项目中加载的脚本,以及当服务器正在运行时,可以在浏览器中访问 /signalr/hub。

找不到编译动态表达式所需的一个或多个类型

此错误表示缺少库 Microsoft.CSharp 。 将其添加到“Assemblies-Framework>”选项卡中。

无法从 Visual Basic 或强类型中心中的 Clients.Caller 访问调用方状态;“从类型”Object) 的任务 (“转换为类型”String“无效”错误

若要在 Visual Basic 或强类型中心访问调用方状态,请使用 Clients.CallerState SignalR 2.1) 中引入的属性 Clients.Caller (而不是 。

Visual Studio 问题

本部分介绍 Visual Studio 中遇到的问题。

“脚本文档”节点未显示在解决方案资源管理器

调试时,我们的一些教程会将你定向到 解决方案资源管理器 中的“脚本文档”节点。 此节点由 JavaScript 调试器生成,仅在 Internet Explorer 中调试浏览器客户端时显示;如果使用 Chrome 或 Firefox,则不会显示节点。 如果另一个客户端调试器(如 Silverlight 调试器)正在运行,JavaScript 调试器也不会运行。

SignalR 不适用于 Visual Studio 2008 或更早版本

此行为是设计使然。 SignalR 需要.NET Framework 4 或更高版本;这要求在 Visual Studio 2010 或更高版本中开发 SignalR 应用程序。 SignalR 的服务器组件需要 .NET Framework 4.5。

IIS 问题

本部分包含 Internet 信息服务的问题。

SignalR 在 Visual Studio 开发服务器上工作,但在 IIS 中不可用

IIS 7.0 和 7.5 支持 SignalR,但必须添加对无扩展 URL 的支持。 若要添加对无扩展 URL 的支持,请参阅 https://support.microsoft.com/kb/980368

SignalR 要求在服务器上安装 ASP.NET, (ASP.NET 默认情况下未在 IIS 上安装) 。 若要安装 ASP.NET,请参阅 ASP.NET 下载

Microsoft Azure 问题

本部分包含 Microsoft Azure 的问题。

在 Azure 辅助角色中托管 SignalR 时的 FileLoadException

在 Azure 辅助角色中托管 SignalR 可能会导致异常“无法加载文件或程序集'Microsoft.Owin, Version=2.0.0.0”。 这是 NuGet 的已知问题;绑定重定向不会在 Azure 辅助角色项目中自动添加。 若要解决此问题,可以手动添加绑定重定向。 将以下行添加到 app.config 辅助角色项目的 文件中。

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

更改主题名称后,不会通过 Azure 背板接收消息

Azure 底板使用的主题在内部维护;它们不是用户可配置的。