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

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

一、登陆页面

登陆页面如下所示:

好简洁,有没有?

当用户成功登录之后,将用户信息保存到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. vue cli 打包项目造成css背景图路径错误

    vue cli做项目的时候难免会碰到,css引用背景图或者css,js之间的相互引用!!!这时候打包后可能会出现一个错误!!如下图: 写法: 错误: 会无端多出一个“/css/static/” 这样就 ...

  2. 解决ios下的微信页面背景音乐无法自动播放问题

    在做各种html5场景页面的时候,插入背景音乐是一个很普遍的需求,我们都知道,ios下的safari是无法自动播放音乐的,以至于现在行程一种认知,ios是没有办法自动播放媒体资源的,这个认知其实是错误 ...

  3. Java Spring JDBC访问数据库

    一.首先采用org.springframework.jdbc.datasource.DriverManagerDataSource类进行实现 1.applicationContext.xml配置如下: ...

  4. clustering

    搞了将近一年的单细胞,聚类也是自认为得心应手了,自信满满. 但是多半是跑软件,对聚类的深层次的思想不甚了了. Google了一下clustering,看了一篇文章,突然了解到了clustering的算 ...

  5. 记 linux 下面初次使用的convert 工具完成拼长图功能

    今天,遇到了一个需要把大量图片合并到一个长图的功能.本来找了各种图片处理界顶顶大佬.. “PS(手动一张张 的加).光影魔术手(批处理功能没有看到拼图功能).美图秀秀(可以有个拼图,限制30张,而且需 ...

  6. (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 ...

  7. vue.js用select实现省,市,提交后,默认显示省,市信息

    <select v-model="citys" name="cityVal" @change="schoolName(citys)"& ...

  8. PHP流程控制笔记

    一.运算符(Operator) 1.运算符 2.运算符分类   (1)按功能分   (2)按操作数个数分 3.按功能分   (1)算术运算符   (2)递增递减   (3)字符运算符   (4)赋值运 ...

  9. 诡异的楼梯 HDU - 1180

    Hogwarts正式开学以后,Harry发现在Hogwarts里,某些楼梯并不是静止不动的,相反,他们每隔一分钟就变动一次方向. 比如下面的例子里,一开始楼梯在竖直方向,一分钟以后它移动到了水平方向, ...

  10. 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 ...