LayIM.AspNetCore Middleware 开发日记(七)Asp.Net.Core.SignalR闪亮登场

前言

  前几篇介绍了整个中间件的构成,路由,基本配置等等.基本上没有涉及到通信部分。不过已经实现了融云的通信功能,因为是第三方的就不在单独去写。正好.NET Core SignalR已经出来很久了,因而乎赶忙对接上。能够先看一下以前的文章:.Net Core SignalR初体验javascript

Hub设计

  Hub我采用了 Hub<T>,而后只定义了一个 Receive方法。html

namespace LayIM.AspNetCore.IM.SignalR
{
    public interface ILayIMClient
    {
        Task Receive(object message);
    }
}
// Hub端代码
  public Task SendMessage(string targetId, string message)
    {
           //这里就能够调用 Receive方法
            return Clients.Caller.Receive(message);
    }

  那么这里咱们要作的就是,先链接上服务器在实现详细业务。下面咱们要作两件事情:java

  • 修改Startup,注册SignalR
  • 增长Javascript客户端

因为是将SignalR拆分到LayIM.AspNetCore.IM.SignalR项目中,因此注册服务端代码作了小小封装。在SignalRServiceExtensions文件中:git

/// <summary>
        /// 使用SignalR通讯
        /// </summary>
        /// <param name="services"></param>
        /// <param name="setConfig"></param>
        public static IServiceCollection AddSignalR(this IServiceCollection services, Action<LayIMHubOptions> configure)
        {
            var options = new LayIMHubOptions();
            configure?.Invoke(options);
            var signalRServerBuilder = services.AddSignalR(options.HubConfigure);
            //增长Redis配置
            if (options.UseRedis)
            {
                signalRServerBuilder.AddRedis(options.RedisConfiguration, options.RedisConfigure);
            }
            //AddSignalR must be called before registering your custom SignalR services.
            services.AddSingleton<ILayIMAppBuilder, SignalRAppBuilder>();
            //获取用户ID
            services.AddSingleton<IUserIdProvider, LayIMUserIdProvider>();


            LayIMServiceLocator.SetServiceProvider(services.BuildServiceProvider());
            return services;
        }

那么在客户端 Startup 调用的时候就能够这么写了:github

//注册LayIM的默认服务
            services.AddLayIM(() =>
            {
                return new MyUserFactory();
            }).AddSignalR(options =>
                {
                    options.HubConfigure = hubOptions =>
                    {
                        hubOptions.EnableDetailedErrors = true;
                        hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(5);
                    };
                    //使用Redis
                    options.RedisConfiguration = "192.168.1.225:6379"
                })
                .AddSqlServer(connectionString);

而后Configure方法中:后端

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ....其余代码
            //使用LayIM,自定义配置
            app.UseLayIM(options =>
            {
                options.ServerType = ServerType.SignalR;
            });
            ....其余代码
        }

到这里可能你们有疑问,没有看到添加 AddSignalR方法。因为是封装了不少细节,因此,这一部分已经写到了UselayIM代码中。服务器

public class SignalRAppBuilder : ILayIMAppBuilder
    {
        public void Build(IApplicationBuilder builder)
        {
            builder.UseSignalR(route => {
                route.MapHub<LayIMHub>("/layimHub", connectionOptions =>
                {
                   
                });
            });
        }
    }

其实也就对应了上文中services.AddSingleton<ILayIMAppBuilder, SignalRAppBuilder>();这句代码。那么到这里呢,SignalR的服务该注册的也注册了,该添加的也添加了,下面就编写(JS)客户端代码。app

SignalR Javascript客户端

  这里咱们根据官方文档里写就能够。链接部分核心代码:socket

let hubRoute = "layimHub";
            let protocol = new signalR.JsonHubProtocol();
            var options = {};
            connection = new signalR.HubConnectionBuilder()
                .configureLogging(signalR.LogLevel.Trace)
                .withUrl(hubRoute, options)
                .withHubProtocol(protocol)
                .build();
            //receive message
            connection.on('Receive', im.handle);
            connection.onclose(function (e) {
                if (e) {
                  
                }
                log('链接已关闭' + e ? e : '');
            });
            connection.start()
                .then(function () {
                    //链接成功
                })
                .catch(function (err) {
                    log('服务器链接失败:' + err);
                });

  运行一下程序。没问题

  那么到这里,咱们就能够对接LayIM的实际业务了.这一段其实和融云思路差很少。首先,咱们要确保消息可以发送到后端,那么咱们修改一下监听LayIM发送消息部分的代码:ide

layim.on('sendMessage', function (data) {
        //调用socket方法,发送消息
       im.sendMsgWithQueue(data);
 });

调用服务端发送方法:

if (im.connected) {
            this.invoke(connection, 'SendMessage', targetId, msg);
      }

invoke方法

invoke: function () {
       if (!im.connected) {
           return;
       }
       var argsArray = Array.prototype.slice.call(arguments);
       connection.invoke.apply(connection, argsArray.slice(1))
           .then(function (result) {
               if (result) {
                   log(result);
               }
            }).catch(function (err) {
                    log(err);
           });
     },

能够看到,调用了服务端的 SendMessage方法,那么这里就要回到Hub代码部分了。咱们在Hub端新增方法SendMessage,而后定义好接收变量。以下:

public class LayIMMessage
    {
        [JsonProperty("id")]
        public long Id { get; set; }
        [JsonProperty("avatar")]
        public string Avatar { get; set; }
        [JsonProperty("type")]
        public string Type { get; set; }
        [JsonProperty("content")]
        public string Content { get; set; }
        [JsonProperty("username")]
        public string UserName { get; set; }
    }
public Task SendMessage(string targetId, LayIMMessage message)
        {
            if (string.IsNullOrEmpty(targetId) || message == null)
            {
                return Task.CompletedTask;
            }
            var toClientMessage = LayIMToClientMessage<LayIMMessage>.Create(message, LayIMMessageType.ClientToClient);
            //若是消息类型是群聊,调用OthersInGroup方法
            if (message.Type == LayIMConst.TYPE_GROUP)
            {
                return Clients.OthersInGroup(targetId).Receive(toClientMessage);
            }
            else
            {
                //若是消息类型是单聊,直接调用User
                //或者 Clients.Client([connectionId])
                return Clients.User(targetId).Receive(toClientMessage);
            }
        }

这里有两个细节要注意,第一:用户链接成功以后须要加入到Group,第二,自定义UserIdProvider 那么第一个,就是咱们要在用户链接成功以后调用一下加入群组的方法,一样,用户下线以后要移除掉。IGroupManager中定义了以下两个方法:

namespace Microsoft.AspNetCore.SignalR
{
    //
    // 摘要:
    //     A manager abstraction for adding and removing connections from groups.
    public interface IGroupManager
    {
        Task AddToGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default(CancellationToken));
       
        Task RemoveFromGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default(CancellationToken));
    }
}

至于自定义用户ID,很简单,咱们实现接口IUserIdProvider便可。细心的同窗可能在前文的代码中看到这一段了。为何要使用重写呢?由于SignalR默认使用ConnectionId。并且每次刷新页面以后,它都是会变化的,那么若是咱们改为使用绑定用户ID的话,对于直接定点推送,刷新页面是没有问题的,直接根据User对象推送便可。下面演示一下:

群聊的图就不贴了,同样的。那么至此SignalR的对接就结束了。是否是比Demo也难不了多少。

推送服务分离

  到这里呢,咱们就能够融云,SignalR自由切换了。具体细节能够查看 LayIM.AspNetCore.Demo.RongCloud,LayIM.AspNetCore.Demo.SignalR两个项目。

总结

  给你们大致介绍了一下对接思路,其实有不少细节也没有展现,毕竟贴的代码已经够多了。若是小伙伴们有兴趣,能够移步:源码地址,今天就到这里啦,再见,祝你们中秋快乐

相关文章
相关标签/搜索