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层耦合的问题.当然那是最简单的解决方法,再复杂一点的程序可能思路相同,但是在编程细节中需要考虑的就更多了,比如今天我在重构过程中遇到的问题.也是接下来 ...
随机推荐
- vue cli 打包项目造成css背景图路径错误
vue cli做项目的时候难免会碰到,css引用背景图或者css,js之间的相互引用!!!这时候打包后可能会出现一个错误!!如下图: 写法: 错误: 会无端多出一个“/css/static/” 这样就 ...
- 解决ios下的微信页面背景音乐无法自动播放问题
在做各种html5场景页面的时候,插入背景音乐是一个很普遍的需求,我们都知道,ios下的safari是无法自动播放音乐的,以至于现在行程一种认知,ios是没有办法自动播放媒体资源的,这个认知其实是错误 ...
- Java Spring JDBC访问数据库
一.首先采用org.springframework.jdbc.datasource.DriverManagerDataSource类进行实现 1.applicationContext.xml配置如下: ...
- clustering
搞了将近一年的单细胞,聚类也是自认为得心应手了,自信满满. 但是多半是跑软件,对聚类的深层次的思想不甚了了. Google了一下clustering,看了一篇文章,突然了解到了clustering的算 ...
- 记 linux 下面初次使用的convert 工具完成拼长图功能
今天,遇到了一个需要把大量图片合并到一个长图的功能.本来找了各种图片处理界顶顶大佬.. “PS(手动一张张 的加).光影魔术手(批处理功能没有看到拼图功能).美图秀秀(可以有个拼图,限制30张,而且需 ...
- (Go rails)使用Rescue_from(ActiveSupport:Rescuable::ClassMethods)来解决404(ActiveRecord::RecordNotFound)❌
https://gorails.com/episodes/handle-404-using-rescue_from?autoplay=1 我的git: https://github.com/chent ...
- vue.js用select实现省,市,提交后,默认显示省,市信息
<select v-model="citys" name="cityVal" @change="schoolName(citys)"& ...
- PHP流程控制笔记
一.运算符(Operator) 1.运算符 2.运算符分类 (1)按功能分 (2)按操作数个数分 3.按功能分 (1)算术运算符 (2)递增递减 (3)字符运算符 (4)赋值运 ...
- 诡异的楼梯 HDU - 1180
Hogwarts正式开学以后,Harry发现在Hogwarts里,某些楼梯并不是静止不动的,相反,他们每隔一分钟就变动一次方向. 比如下面的例子里,一开始楼梯在竖直方向,一分钟以后它移动到了水平方向, ...
- leetcode-algorithms-30 Substring with Concatenation of All Words
leetcode-algorithms-30 Substring with Concatenation of All Words You are given a string, s, and a li ...