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

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

一、登陆页面

登陆页面如下所示:

好简洁,有没有?

当用户成功登录之后,将用户信息保存到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

 posted on 2018-01-12 16:44  F风  阅读(297)  评论(0编辑  收藏  举报