Fork me on GitHub
tinylit

SignalR 设计理念(二)

SignalR 设计理念(二)

实现客户端和服务器端的实时通讯.

前言:

客户端方法忽略大小写,主要原因基于是URL对大小写不敏感的问题,开发者之间为了更好的协同开发,定下的开发者协议。

问题阐述
  1. 客户端数量不确定!
  2. 同一个用户的客户端数量不确定(一个用户可以多处登陆)!
  3. 客户端连接的渠道不确定(应用程序连接、Web普通连接、WebSocket连接等)!
  4. 同一个用户的连接渠道不一定!

针对以上问题,你会如何设计服务器架构?

SignalR 采用 管道通讯 作为连接消息通道,使用 分配器 对消息适配(基于持久化连接抽象类PersistentConnection实现)。

问:

  1. 管道数如何区分连接渠道? [1]

举一反三:

  1. 客户端和服务器之间,消息是如何传递的?

核心代码(一):

public abstract class PersistentConnection 
{ //持久化连接 【部分代码】
 private ITransportManager _transportManager;
 public virtual void Initialize(IDependencyResolver resolver)
 {
     _transportManager = resolver.Resolve<ITransportManager>();
 }
	public virtual Task ProcessRequest(HostContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}

		if (!_initialized)
		{
			throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_ConnectionNotInitialized));
		}

		if (IsNegotiationRequest(context.Request))
		{
			return ProcessNegotiationRequest(context);
		}
		else if (IsPingRequest(context.Request))
		{
			return ProcessPingRequest(context);
		}

		Transport = GetTransport(context); // 建立运输方式

		if (Transport == null)
		{
			return FailResponse(context.Response, String.Format(CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorUnknownTransport));
		}

		string connectionToken = context.Request.QueryString["connectionToken"];

		if (String.IsNullOrEmpty(connectionToken))
		{
			return FailResponse(context.Response, String.Format(CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorMissingConnectionToken));
		}

		string connectionId;
		string message;
		int statusCode;

		if (!TryGetConnectionId(context, connectionToken, out connectionId, out message, out statusCode))
		{
			return FailResponse(context.Response, message, statusCode);
		}

		Transport.ConnectionId = connectionId;

		// Get the groups token from the request
		return Transport.GetGroupsToken()
						.Then((g, pc, c) => pc.ProcessRequestPostGroupRead(c, g), this, context)
						.FastUnwrap();
	}
 private ITransport GetTransport(HostContext context)
 {
     return _transportManager.GetTransport(context);
 }
}

核心代码(二):

public class TransportManager : ITransportManager
{ //运输机管理器 【部分代码】
	private readonly ConcurrentDictionary<string, Func<HostContext, ITransport>> _transports = new ConcurrentDictionary<string, Func<HostContext, ITransport>>(StringComparer.OrdinalIgnoreCase);
	[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Those are factory methods")]
	public TransportManager(IDependencyResolver resolver)
	{
		if (resolver == null)
		{
			throw new ArgumentNullException("resolver");
		}
		Register("foreverFrame", context => new ForeverFrameTransport(context, resolver));
		Register("serverSentEvents", context => new ServerSentEventsTransport(context, resolver));
		Register("longPolling", context => new LongPollingTransport(context, resolver));
		Register("webSockets", context => new WebSocketTransport(context, resolver));
	}
	// 注册运行机 (可自定义运输方式)
	public void Register(string transportName, Func<HostContext, ITransport> transportFactory)
	{
		if (String.IsNullOrEmpty(transportName))
		{
			throw new ArgumentNullException("transportName");
		}

		if (transportFactory == null)
		{
			throw new ArgumentNullException("transportFactory");
		}

		_transports.TryAdd(transportName, transportFactory);
	}
	// 分配运输机
	public ITransport GetTransport(HostContext hostContext)
	{
		if (hostContext == null)
		{
			throw new ArgumentNullException("hostContext");
		}

		string transportName = hostContext.Request.QueryString["transport"];

		if (String.IsNullOrEmpty(transportName))
		{
			return null;
		}

		Func<HostContext, ITransport> factory;
		if (_transports.TryGetValue(transportName, out factory))
		{
			return factory(hostContext);
		}

		return null;
	}
}
解析:

  1. 由连接中的transport参数区分,有兴趣的朋友可以下载源码深度学习 Microsoft.AspNet.SignalR↩︎

posted @ 2018-11-24 17:18  影子和树  阅读(293)  评论(0编辑  收藏  举报