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兩個項目。

總結

  給你們大致介紹了一下對接思路,其實有不少細節也沒有展現,畢竟貼的代碼已經夠多了。若是小夥伴們有興趣,能夠移步:源碼地址,今天就到這裏啦,再見,祝你們中秋快樂

相關文章
相關標籤/搜索