• SignalR 开始聊天室之旅


    首先明确需求,我现在有很多个直播间,每个直播间内需要存在一个聊天室,每个聊天室内可以存在很多人聊天,当然,只有登陆系统的会员才能聊天,没有登陆的,干看着吧!

    根据以上需求,可以做出三个简单的页面:登陆页面、直播列表页面、直播和聊天室页面。

    一、登陆页面

    登陆页面如下所示:

    好简洁,有没有?

    当用户成功登录之后,将用户信息保存到Session中或其他缓存中,本案例用的是Session,简单异常啊!

    二、直播列表页面

    直播列表页面如下所示:

    本案例的侧重点在聊天室,至于直播,见他的大爷去,用图片代替!

    点击任一图片,可以跳转到直播和聊天的页面!

    三、直播和聊天室页面

    直播和聊天室页面如下:

    这个页面才是本次的重点!

    接下来,主讲的就是这个页面。

    我先把GroupChatHub类的代码全部贴出来,如下所示:

    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Threading.Tasks;
    using MyProject.Entity;
    using SignalRTest.BLL;
    
    namespace SignalRTest.Utils {
        [HubName("groupChatHub")]
        public class GroupChatHub:Hub {  
            [HubMethodName("joinRoom")]
            //创建聊天的房间
            public void JoinRoom(string liveId) {
                try {    
                    //查询出该房间是否开放
                    LiveChatRoomBLL roomBiz = new LiveChatRoomBLL();
                    LiveChatRoom room = roomBiz.Find(it => it.LiveID == Convert.ToInt32(liveId) && it.Status == 1);
                    //如果房间为空,则创建该聊天房间
                    if (room == null) {
                        room = new LiveChatRoom { LiveID = Convert.ToInt32(liveId), Status = 1 };
                        room.ID = roomBiz.Add(room);
                    }
    
                    if (room != null && room.ID > 0) {
                        //将ConnectionID发送给自己
                        Clients.Client(Context.ConnectionId).intoRoom(Context.ConnectionId);
                    }                
                }
                catch (Exception ex) {
                }
            }
         
            public override Task OnConnected() {            
                return base.OnConnected();
            }
    
            public override Task OnReconnected() {           
                return base.OnReconnected();
            }
    
            public override Task OnDisconnected(bool stopCalled) {         
                 LiveChatRoomMemberBLL biz = new LiveChatRoomMemberBLL();
                LiveChatRoomMember member = biz.Find(it => it.ConnectionID == Context.ConnectionId);
                if (member != null && member.ID > 0) {
                    //从该房间清除该人员
                    if (biz.Delete(member.ID)) {
                        //发送退出消息
                        Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg("系统消息", member.User.UserName + "  退出聊天", 0));
                        //从组中移除该ConnectionID
                        Groups.Remove(Context.ConnectionId, member.RoomID.ToString());
                    }
                }
                
                return base.OnDisconnected(stopCalled);
            }
    
            /// <summary>
            /// 发送消息,供客户端调用
            /// </summary>
            /// <param name="user">用户名,如果为0,则是发送给所有人</param>
            /// <param name="msg">消息</param>
            public void SendMsg(string user,string msg) {
                LiveChatRoomMember member = new LiveChatRoomMemberBLL().Find(it => it.ConnectionID == Context.ConnectionId);
                if (member != null && member.ID > 0) {
                    Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg(member.User.UserName, msg, 1, member.User.HeadPic));
                }           
            }
    
            
    
            //type 0:系统消息 1:用户消息
            public static dynamic FormatMsg(string name, string msg, int type=1,string pic="") {
                return new {IType=type, Name=name,Msg=HttpUtility.HtmlEncode(msg),Pic= HttpUtility.HtmlEncode(pic), Time=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")};
            }
        }
    }

    1、创建聊天室

    当每个用户进入该页面的时候,在客户端页面中调用GroupChatHub的JoinRoom方法,JoinRoom方法的作用是创建聊天室数据并将ConnectionID发送给自己当前的页面。为何需要这样做?因为在SignalR中,每一个新连接到来,程序就会自动创建一个ConnectionID,一般的做法是将ConnectionID和UserID绑定。可是Hub不支持Session,因此在GroupChatHub类中就无法获取Session,也就无法获取当前登陆用户的信息。那怎么办呢?哼哼,简单至极!直接获取不行,我们可以进行迂回呀!反正客户端页面可以通过js调用GroupChatHub的方法,那我就将ConnectionID回传到前端页面,然后再通过ajax调用自定义的IHttpHandler接口,将ConnectionID传到接口中去和UserID进行绑定!是不是非常简单!有人会说了,IHttpHandler接口中也无法获取Session呀,你不会自己再次继承IRequiresSessionState接口哇?

    上代码:

    (1) 前端页面连接hub

                // 链接hub            
                var ticker = $.connection.groupChatHub;          
                $.connection.hub.start().done(function () {
                    //调用GroupChatHub的JoinRoom方法,创建聊天房间
                    ticker.server.joinRoom(QueryString("sid")).done(function () {
                        
                    });
                });

    (2) 创建房间,并将ConnectionID回传到当前页面

            [HubMethodName("joinRoom")]
            //创建聊天的房间
            public void JoinRoom(string liveId) {
                try { //查询出该房间是否开放
                    LiveChatRoomBLL roomBiz = new LiveChatRoomBLL();
                    LiveChatRoom room = roomBiz.Find(it => it.LiveID == Convert.ToInt32(liveId) && it.Status == 1);
                    //如果房间为空,则创建该聊天房间
                    if (room == null) {
                        room = new LiveChatRoom { LiveID = Convert.ToInt32(liveId), Status = 1 };
                        room.ID = roomBiz.Add(room);
                    }
    
                    if (room != null && room.ID > 0) {
                        //将ConnectionID发送给自己
                        Clients.Client(Context.ConnectionId).intoRoom(Context.ConnectionId);
                    }                
                }
                catch (Exception ex) {
    
                }
            }

    (3) 当前页面接收回传的ConnectionID,并上送接口中

                // 接收服务端发送的消息
                $.extend(ticker.client, {               
                    intoRoom: function (data) {
                        //打印出当前连接的ConnectionID
                        //alert(data);
                        
                        //调用ajax接口,将当前用户的ID(Session中)与ConnectionID关联起来
                        var param = { action: 'joinroom', liveId: QueryString("sid"), connectionId: data };
                        $.ajax({
                            type: 'POST',
                            dataType: 'json',
                            url: 'Index.ashx',
                            data: JSON.stringify(param),
                            success: function (data) {
                                if (data && data.returnValue == 0) {
                                    console.log(data.returnMsg);
                                }
                                else alert(data.returnMsg);
                            }
                        });
                    }
                });

    (4) 在接口中将ConnectionID与UserID绑定

     public class JoinRoomHandler : LiveHandler<JoinRoomReq> {
            public JoinRoomHandler() : base("JoinRoomHandler") { }
    
            protected override BaseResponseResult DoWork(JoinRoomReq param) {
                BaseResponseResult rc = new BaseResponseResult((int)Code.OperationError, "操作失败!");
    
                if (Index.User != null) {
                    if (param.liveId > 0) {
                        //找到当前直播对应的房间号
                        LiveChatRoom room = new LiveChatRoomBLL().Find(it => it.LiveID == param.liveId && it.Status == 1);
                        //如果房间存在
                        if (room != null && room.ID > 0) {
                            //将ConnectionID与UserID绑定到当前直播的房间中
                            LiveChatRoomMember member = new LiveChatRoomMember
                            {
                                ConnectionID = param.connectionId,
                                RoomID = room.ID,
                                UserID = Index.User.UserID
                            };
    
                            member.ID = new LiveChatRoomMemberBLL().Add(member);
                            //如果当前登陆的人员 成功 加入到直播聊天室
                            if (member.ID > 0) {
                                //这里的代码很重要,这是在外部调用GroupChatHub
                                var context = GlobalHost.ConnectionManager.GetHubContext<GroupChatHub>();
                                //将当前的ConnectionID加入到 以房间ID为名称的组中
                                context.Groups.Add(param.connectionId, room.ID.ToString());
                                //向客户端发送新加入人员信息
                                context.Clients.Group(room.ID.ToString()).publishMsg(GroupChatHub.FormatMsg("系统消息", Index.User.UserName + "  加入聊天", 0,Index.User.HeadPic));
                                rc.SetResult(0,"成功加入聊天室!");
                            }
                            else
                                rc.SetResult(3, "加入聊天房间失败!");
                        }
                        else
                            rc.SetResult(1, "当前聊天房间不存在!");
                    }
                    else
                        rc.SetResult(1, "当前聊天房间不存在!");
                }
                else
                    rc.SetResult(2,"未登录!");
    
                return rc;
            }
        }

    至此,成功将当前页面的ConnectionID、登陆人员的UserID和房间号绑定了起来。

    2、发送消息

    发送消息就简单了,在客户端页面调用GroupChatHub类的SendMsg方法。调用如下所示:

    $("#btnSend").click(function () {
                    //获取文本框内容
                    var tbxInput = $(this).parent().children(".msgs");
                    if (tbxInput) {
                        var msg = tbxInput.val() || '';
                        if (msg.length > 0) {         
                            // 主动发送消息,传入直播ID,和发送的内容。
                            ticker.server.sendMsg(QueryString("sid"), msg);
                            tbxInput.val('');
                        }
                        else tbxInput.focus();
                    }                
                });
    
                $(".msgs").bind("keydown", event, function () {
                    if (event.keyCode == 13)
                        $("#btnSend").click();
                });

    SendMsg方法如下:

       public void SendMsg(string user,string msg) {
                //通过ConnectionID找到当前聊天室的信息
                LiveChatRoomMember member = new LiveChatRoomMemberBLL().Find(it => it.ConnectionID == Context.ConnectionId);
                if (member != null && member.ID > 0) {
                    //向当前聊天室发送消息
                    Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg(member.User.UserName, msg, 1, member.User.HeadPic));
                }           
            }

    客户端接收消息代码如下:

                // 接收服务端发送的消息
                $.extend(ticker.client, {
                    // 接收聊天消息
                    publishMsg: function (data) {                    
                        if (data) {
                            var html = '';
                            //系统消息
                            if (data.IType == 0) {
                                html = '<div class="im-systeminfo">'+
                                           '<p class="im-sititle">' + data.Name + '&emsp;' + data.Time + '</p>' +
                                           '<p class="im-sicontent">' + data.Msg + '</p>' +
                                        '</div>';
                            }
                            //群聊消息
                            else if (data.IType == 1) {
                                html = '<div class="im-contents">' +
                                          '<img class="im-headpic" src="' + data.Pic + '"/>' +
                                          '<div>' +
                                              '<p class="im-nickname">' + data.Name + '&emsp;' + data.Time + '</p>' +
                                              '<p class="im-msgs">' + data.Msg + '</p>' +
                                          '</div>' +
                                       '</div>';
                            }
                        }
                        
                        $(".im-list").append(html);                   
                        $(".im-list").scrollTop($(".im-list")[0].scrollHeight);                   
                    },                
                    intoRoom: function (data) {
                        //打印出当前连接的ConnectionID
                        //alert(data);
                        
                        //调用ajax接口,将当前用户的ID(Session中)与ConnectionID关联起来
                        var param = { action: 'joinroom', liveId: QueryString("sid"), connectionId: data };
                        $.ajax({
                            type: 'POST',
                            dataType: 'json',
                            url: 'Index.ashx',
                            data: JSON.stringify(param),
                            success: function (data) {
                                if (data && data.returnValue == 0) {
                                    console.log(data.returnMsg);
                                }
                                else alert(data.returnMsg);
                            }
                        });
                    }
                });

    消息收发界面如下:

    3、退出聊天室

    退出聊天室很简单!当用户关闭当前聊天室页面,系统就会自动调用GroupChatHub的OnDisconnected方法,我们只需在OnDisconnected方法中写入逻辑代码即可。如下所示:

     public override Task OnDisconnected(bool stopCalled) {    
                 LiveChatRoomMemberBLL biz = new LiveChatRoomMemberBLL();
                //根据ConnectionID找到当前的聊天室信息
                LiveChatRoomMember member = biz.Find(it => it.ConnectionID == Context.ConnectionId);
                if (member != null && member.ID > 0) {
                    //从该房间清除该人员
                    if (biz.Delete(member.ID)) {
                        //发送退出消息
                        Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg("系统消息", member.User.UserName + "  退出聊天", 0));
                        //从组中移除该ConnectionID
                        Groups.Remove(Context.ConnectionId, member.RoomID.ToString());
                    }
                }
                
                return base.OnDisconnected(stopCalled);
            }

    界面如下所示:

    至此,整个流程如上所述,一个简单的聊天室就搭建完毕啦!

    源代码:https://github.com/wsjun2016/SignalRTest

    演示地址:http://101.201.234.177:8090/

    备注:共预配了5个账号,每个账户名都是单个数字【1,2,3,4,5】,密码都为1

  • 相关阅读:
    正则表达式预:
    cookie 二:
    Javascript之运动框架2
    cookie预:
    Javascript之链式运动框架1
    基于Azure的软件部署和开发系列沙龙
    在Docker中安装.NET Core(使用命令行工具)
    Xshell 无法连接虚拟机中的ubuntu的问题
    springboot09-redis
    springboot08-jpa-mysql
  • 原文地址:https://www.cnblogs.com/williamwsj/p/8276600.html
Copyright © 2020-2023  润新知