我使用signalR编写了一些代码。主要思想是我们支持几组用户。每个组都使用令牌(某些字符串值)进行标识。此令牌通过查询字符串从客户端传递到服务器。然后具有相同令牌的所有客户端从服务器获取更新。 这是我的代码的一个非常简化的版本。
我使用HubPipelineModule来支持signalR中的组。假设令牌始终从客户端传递,因此我们可以从Referer头中获取其值。
public class RejoingGroupPipelineModule : HubPipelineModule
{
public override Func<HubDescriptor, IRequest, IList<string>, IList<string>> BuildRejoiningGroups(
Func<HubDescriptor, IRequest, IList<string>, IList<string>> rejoiningGroups)
{
return (hb, r, l) =>
{
return new[] { GetTaskToken(r) };
};
}
public static String GetGroupName(IRequest request)
{
var refererHeader = request.Headers.FirstOrDefault(h => h.Key == "Referer");
var uriString = refererHeader.Value;
if (String.IsNullOrWhiteSpace(uriString))
{
return String.Empty;
}
return "defaultToken";
}
}
RejoinGroupPipelineModule在Startup类中注册
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
GlobalHost.HubPipeline.AddModule(new RejoingGroupPipelineModule());
}
}
然后Hub类更新客户端。
public class BaseHub : Hub
{
public void Update()
{
context.Clients.Groups(new[]{"defaultToken"}).updateClient(DateTime.Now);
}
}
在计时器上重复调用BaseHub.Update方法。因此,所有客户端都会刷新服务器时间。 当signalR使用长轮询时,一切正常,客户端也会更新。但是当signalR使用网络套接字时,就会出现问题:
1)将具有uri http://localhost/signalr/CONNECT?transport=webSockets&...
的请求传递给方法BuildRejoiningGroups。请求不包含引用标头,因此我们无法确定正确的组。
2)然后将具有uri http://localhost/signalr/START?transport=webSockets&...
的请求传递给该方法。现在Referer标头出现在请求中。我们可以使用令牌并返回正确的功能。但客户不会更新。我建议重新加入的组实际上没有更新。
我使用了下一个解决方法:
public override Func<HubDescriptor, IRequest, IList<string>, IList<string>> BuildRejoiningGroups(
Func<HubDescriptor, IRequest, IList<string>, IList<string>> rejoiningGroups)
{
return (hb, r, l) =>
{
if (r.Headers.All(h => h.Key != "Referer"))
{
return null;
}
return new[] { GetTaskToken(r) };
};
}
现在客户端已更新,但正在抛出“System.ArgumentNullException:Value not not null”。
我的问题是:为什么不使用最后重新加入的群组?如何正确地从查询字符串中获取令牌?如何避免System.ArgumentNullException?
答案 0 :(得分:3)
为什么不使用上次重新加入的群组?
SignalR的设计使其可以在负载均衡器后面使用。这意味着可能会将不同的请求发送到不同的服务器以进行相同的SignalR“连接”。出于这个原因,SignalR尝试保留大多数状态,例如客户端以groupsToken
的形式加入的组在客户端本身上。
如何正确地从查询字符串中获取令牌?
SignalR已拥有自己的“groupsToken”,以确保客户端在重新连接时通过调用Groups.Add
安全地重新添加到已加入的群组中。为什么不使用SignalR的内置组重新加入功能?如果您希望始终根据Referer标头的存在将连接添加到默认组,则可以在OnConnected
中执行此操作。
public override Task OnConnected()
{
var refererHeader = Context.Request.Headers.FirstOrDefault(h => h.Key == "Referer");
var uriString = refererHeader.Value;
if (!String.IsNullOrWhiteSpace(uriString))
{
Groups.Add(Context.ConnectionId, GetTaskToken(Context.Request));
}
return base.OnConnected();
}
从SignalR 2.1.0开始,总是调用OnConnected以响应Ajax / start请求,这意味着与WebSocket请求不同,Referer头应该是可访问的。
如何避免System.ArgumentNullException?
不要从BuildRejoiningGroups
中返回的Func返回null。相反,您应该使用paremeter来获取SignalR通常会重新加入客户端的组。
public override Func<HubDescriptor, IRequest, IList<string>, IList<string>> BuildRejoiningGroups(
Func<HubDescriptor, IRequest, IList<string>, IList<string>> rejoiningGroups)
{
return (hb, r, l) =>
{
// Get the groups SignalR would rejoin the client to by default
var groupsToRejoin = rejoiningGroups(hb, r, l);
// I would ensure that groupsToRejoin doesn't already contain the group
// GetTaskToken would add, because SignalR will rejoin the group automatically
// if the client has already be added to the group.
if (r.Headers.Any(h => h.Key == "Referer"))
{
groupsToRejoin.Add(GetTaskToken(r));
}
return groupsToRejoin;
};
}
P.S。如您所述,“Referer”标头不会随WebSocket请求一起发送。但是,会发送类似的“Origin”标题。