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

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

一、登陆页面

登陆页面如下所示:

好简洁,有没有?

当用户成功登录之后,将用户信息保存到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 == );
//如果房间为空,则创建该聊天房间
if (room == null) {
room = new LiveChatRoom { LiveID = Convert.ToInt32(liveId), Status = };
room.ID = roomBiz.Add(room);
} if (room != null && room.ID > ) {
//将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 > ) {
//从该房间清除该人员
if (biz.Delete(member.ID)) {
//发送退出消息
Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg("系统消息", member.User.UserName + " 退出聊天", ));
//从组中移除该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 > ) {
Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg(member.User.UserName, msg, , member.User.HeadPic));
}
} //type 0:系统消息 1:用户消息
public static dynamic FormatMsg(string name, string msg, int type=,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 == );
//如果房间为空,则创建该聊天房间
if (room == null) {
room = new LiveChatRoom { LiveID = Convert.ToInt32(liveId), Status = };
room.ID = roomBiz.Add(room);
} if (room != null && room.ID > ) {
//将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 == ) {
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 > ) {
//找到当前直播对应的房间号
LiveChatRoom room = new LiveChatRoomBLL().Find(it => it.LiveID == param.liveId && it.Status == );
//如果房间存在
if (room != null && room.ID > ) {
//将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 > ) {
//这里的代码很重要,这是在外部调用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 + " 加入聊天", ,Index.User.HeadPic));
rc.SetResult(,"成功加入聊天室!");
}
else
rc.SetResult(, "加入聊天房间失败!");
}
else
rc.SetResult(, "当前聊天房间不存在!");
}
else
rc.SetResult(, "当前聊天房间不存在!");
}
else
rc.SetResult(,"未登录!"); 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 > ) {
// 主动发送消息,传入直播ID,和发送的内容。
ticker.server.sendMsg(QueryString("sid"), msg);
tbxInput.val('');
}
else tbxInput.focus();
}
}); $(".msgs").bind("keydown", event, function () {
if (event.keyCode == )
$("#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 > ) {
//向当前聊天室发送消息
Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg(member.User.UserName, msg, , member.User.HeadPic));
}
}

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

            // 接收服务端发送的消息
$.extend(ticker.client, {
// 接收聊天消息
publishMsg: function (data) {
if (data) {
var html = '';
//系统消息
if (data.IType == ) {
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 == ) {
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")[].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 == ) {
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 > ) {
//从该房间清除该人员
if (biz.Delete(member.ID)) {
//发送退出消息
Clients.Groups(new List<string> { member.RoomID.ToString() }).publishMsg(FormatMsg("系统消息", member.User.UserName + " 退出聊天", ));
//从组中移除该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

SignalR 开始聊天室之旅的更多相关文章

  1. [Asp.net 开发系列之SignalR篇]专题三:使用SignalR实现聊天室的功能

    一.引言 在前一篇文章中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用SignalR实现群聊这样的功能. 二.实现思路 要想实现群聊的功能,首先我们需要创建一个房间,然 ...

  2. 史上最全面的SignalR系列教程-6、SignalR 实现聊天室

    1.概述 通过前面几篇文章对SignalR的详细介绍.我们知道Asp.net SignalR是微软为实现实时通信的一个类库.一般情况下,SignalR会使用JavaScript的长轮询(long po ...

  3. Asp.Net SignalR - 简单聊天室实现

    简单聊天室 使用持久链接类我们就可以做一些即时通讯的应用了,我使用Group做了一个简单的聊天室,先上图技术细节下面再讲 可以加入聊天室.创建聊天室.发送消息,下面就说说我是如何通过Group做出来的 ...

  4. 使用signalR创建聊天室。

    浏览器支持Html5的情况下,SignalR使用WebSockets,当不支持时SignalR将使用其它技术来实现通讯. 界面如下:左侧包含三种聊天对象,不同的聊天对象会创建不同的对话框. 设计思路参 ...

  5. 用SignalR 2.0开发客服系统[系列2:实现聊天室]

    前言 交流群:195866844 上周发表了 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 这篇文章,得到了很多帮助和鼓励,小弟在此真心的感谢大家的支持.. 这周继续系列2,实现聊天室 ...

  6. Asp.NET MVC 使用 SignalR 实现推送功能二(Hubs 在线聊天室 获取保存用户信息)

    简单介绍 关于SignalR的简单实用 请参考 Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室) 在上一篇中,我们只是介绍了简单的消息推送,今天我们来修改一下,实现 ...

  7. Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室)

    简介       ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端 ...

  8. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室 实战系列

    ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言  http://www.cnblogs.com/panzi/p/5742089.html ASP.NET S ...

  9. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十二) 代码重构使用反射工厂解耦(一)缓存切换

    前言 上一篇中,我们用了反射工厂来解除BLL和UI层耦合的问题.当然那是最简单的解决方法,再复杂一点的程序可能思路相同,但是在编程细节中需要考虑的就更多了,比如今天我在重构过程中遇到的问题.也是接下来 ...

随机推荐

  1. Mac python 环境配置

    问题:mac 只带了python2.7,要想使用高版本的Python,如python3.x,只能再次安装了,这样就会遇到 两个版本的切换问题了 如下图 : 执行 which python 如下图,查看 ...

  2. h5屏幕旋转的时间和样式的设置

    好几天没更新博客了,今天写写小感悟和一个小东西吧! 随着前端的前端的越来越火,对前端的要求也越来越高,从之前的切图到开发网站再到现在移动端开发,微信开发,手机app混合开发,不得不说现在前端在开发行业 ...

  3. Docker用途 & 和tomcat的区别

    两者不是同一种类型.1.docker 是容器,tomcat是jsp应用服务器2.tomcat可以安装在物理机上,虚拟机上,也可以安装在Docker上.所以从这个角度讲,Docker也可以看做是一种超轻 ...

  4. spring cloud: zuul(四): 正则表达式匹配其他微服务(给其他微服务加版本号)

    spring cloud: zuul(四): 正则表达式匹配其他微服务(给其他微服务加版本号) 比如我原来有,spring-boot-user微服务,后台进行迭代更新,另外其了一个微服务: sprin ...

  5. delphi 条形码

    Delphi中打印条码的方法 导读: 1 通过菜单”Component”下的”Import ActiveX”将“Microsoft Access Barcode Control 9.0”控件引入.这个 ...

  6. 20165327 2017-2018-2 《Java程序设计》第7周学习总结

    20165327 2017-2018-2 <Java程序设计>第7周学习总结 教材内容总结 第十一章 (一)MySQL数据库服务器 下载安装MySQL服务器 启动MySQL数据库服务器 在 ...

  7. MySQL数据库索引之B+树

    一.B+树是什么 B+ 树是一种树型数据结构,通常用于数据库和操作系统的文件系统中.B+ 树的特点是能够保持数据稳定有序,其插入与修改操作拥有较稳定的对数时间复杂度.B+ 树元素自底向上插入,这与二叉 ...

  8. LeetCode--367--有效的完全平方数

    问题描述: 给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False. 说明:不要使用任何内置的库函数,如  sqrt. 示例 1: 输入:16 输 ...

  9. (GoRails) 使用ActiveStorage给user添加上传头像功能。

    对activestorage的简单使用: 头像库:uifaces.co. 可以使用大量设置好的头像图片. 1.安装avatar rails active_storage:install 2.user ...

  10. Android Studio 一直卡在building解决办法

    1.随便找一个你能运行的as项目 2.打开gradle-wrapper.properties,文件目录:项目/gradle/wrapper/gradle-wrapper.properties 3.复制 ...