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 == );
//如果房间为空,则创建该聊天房间
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 + ' ' + 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 + ' ' + 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 开始聊天室之旅的更多相关文章
- [Asp.net 开发系列之SignalR篇]专题三:使用SignalR实现聊天室的功能
一.引言 在前一篇文章中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用SignalR实现群聊这样的功能. 二.实现思路 要想实现群聊的功能,首先我们需要创建一个房间,然 ...
- 史上最全面的SignalR系列教程-6、SignalR 实现聊天室
1.概述 通过前面几篇文章对SignalR的详细介绍.我们知道Asp.net SignalR是微软为实现实时通信的一个类库.一般情况下,SignalR会使用JavaScript的长轮询(long po ...
- Asp.Net SignalR - 简单聊天室实现
简单聊天室 使用持久链接类我们就可以做一些即时通讯的应用了,我使用Group做了一个简单的聊天室,先上图技术细节下面再讲 可以加入聊天室.创建聊天室.发送消息,下面就说说我是如何通过Group做出来的 ...
- 使用signalR创建聊天室。
浏览器支持Html5的情况下,SignalR使用WebSockets,当不支持时SignalR将使用其它技术来实现通讯. 界面如下:左侧包含三种聊天对象,不同的聊天对象会创建不同的对话框. 设计思路参 ...
- 用SignalR 2.0开发客服系统[系列2:实现聊天室]
前言 交流群:195866844 上周发表了 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 这篇文章,得到了很多帮助和鼓励,小弟在此真心的感谢大家的支持.. 这周继续系列2,实现聊天室 ...
- Asp.NET MVC 使用 SignalR 实现推送功能二(Hubs 在线聊天室 获取保存用户信息)
简单介绍 关于SignalR的简单实用 请参考 Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室) 在上一篇中,我们只是介绍了简单的消息推送,今天我们来修改一下,实现 ...
- Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室)
简介 ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端 ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室 实战系列
ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言 http://www.cnblogs.com/panzi/p/5742089.html ASP.NET S ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十二) 代码重构使用反射工厂解耦(一)缓存切换
前言 上一篇中,我们用了反射工厂来解除BLL和UI层耦合的问题.当然那是最简单的解决方法,再复杂一点的程序可能思路相同,但是在编程细节中需要考虑的就更多了,比如今天我在重构过程中遇到的问题.也是接下来 ...
随机推荐
- h5内容超出可以滑动展示的处理,iscroll的使用
第一步: 引入js 第二步:页面结构 第三步:使用 dome效果:http://cubiq.org/dropbox/iscroll4/examples/simple/ 文档地址:http://iscr ...
- spring cloud: zuul(四): 正则表达式匹配其他微服务(给其他微服务加版本号)
spring cloud: zuul(四): 正则表达式匹配其他微服务(给其他微服务加版本号) 比如我原来有,spring-boot-user微服务,后台进行迭代更新,另外其了一个微服务: sprin ...
- 单细胞数据高级分析之消除细胞周期因素 | Removal of cell cycle effect
The normalization method described above aims to reduce the effect of technical factors in scRNA-seq ...
- libavcodec是一款LGPL自由软件编解码库,用于视频和音频数据的编解码工作
http://zh.wikipedia.org/zh-cn/Libavcodec http://baike.baidu.com/view/856526.htm libavcodec是一款LGPL自由软 ...
- LeetCode--455--分发饼干
问题描述: 假设你是一位很棒的家长,想要给你的孩子们一些小饼干.但是,每个孩子最多只能给一块饼干.对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸:并且每块饼干 j , ...
- You Don't Know JS: Scope & Closures (第2章: Lexical Scope)
2种主要的models for how scope work. 最普遍的是Lexical Scope. 另一种 Dynamic Scope.(在Appendix a中介绍.和Lexical Scope ...
- 海康摄像头配置固定IP
前言 首先要海康设备连接好网线,电脑客户端跟海康设备在同一个局域网络. 1.直接在海康网站下载SADP工具软件,安装SADP工具,如图所示: 2.安装成功后,桌面的出现设备网络搜索, 面板介绍:这里将 ...
- 函数使用七:AUTHORITY_CHECK_RFC
此函数是用来检查用户使用RFC函数的权限 感觉是个废物,从来没遇到过这么蛋疼的权限设置,以及这么挫的检查... Import USERID 执行RFC函数的用 ...
- WDA基础七:TABStrip
这个组件很少用. 一般用这个,是为了页面好看,还有就是布局排版的问题,因为多个页签其实占的是一块地... 新建ELEMENT TABStrip,右键tabstrip,插入页签,需要几个加几个... 加 ...
- Homebrew 安装mysql
在mac上安装软件,无疑安装一个brew是个很好的选择,关于brew是什么,怎么安装建议去brew官网查看, 附上地址:brew官网 还有一篇博文 http://www.cnblogs.com/xd ...