Asp.net SignalR应用并实现群聊功能

ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程。实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据。(来自官方介绍。)

SignalR官网

-1、写这篇的原因

在上篇文章B/S(Web)实时通讯解决方案中,并没有详情介绍SignalR,所以另起一篇专门介绍SignalR,本文的侧重点是Hub功能。

0、先看最终实现效果

github:https://github.com/Emrys5/SignalRGroupChatDemo

在线演示:http://chat.lining.name/

 1、准备工作

1.1、在NuGet上首先下载SignalR的包。

1.2、配置Owin与SignalR

1.2.1、新建Startup类,注册SignalR

public class Startup
 {
 public void Configuration(IAppBuilder app)
 {
  app.MapSignalR();
 }
 }

然后在web.config配置Startup类,在configuration=>appSettings节点中添加

<add key="owin:AppStartup" value="SignalRChat.App_Start.Startup"/>

1.2.2、在页面引入SignalR的js

1、由于SignalR前端是基于jQuery的,所以页面需引入jQuery。

2、引入SignalR的js 。

3、引入最重要的hubs js,这个js其实并不存在,SignalR会反射获取所有供客户端调用的方法放入hubs js中。

<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.1.min.js"></script>
<script src="~/signalr/hubs"></script>

1.2.3、新建GroupChatHub类,并继承Hub抽象类

在hub类中的方法就是提供给客户端调用的js方法。

在js中就可以用signalr调用SendMsg。

[HubName("simpleHub")]
 public class SimpleHub : Hub
 {
 public void SendMsg(string msg)
 {

 }

 }

这样基本上前期准备工作就做完了,后面就是具体的操作。

2、原理与简单的编程

其实原理如果简单点理解就很简单,因为http是无状态的,所以每次请求以后都会与服务器断开链接,那就是说客户端可以很容易找到服务器,但是服务器如果想给你客户端发送消息就比较麻烦,如果不明白的可以参考上一篇文章B/S(Web)实时通讯解决方案。

SignalR就很好的解决了这个问题,也就说实现了实现了浏览器与服务器的全双工通信。

2.1、客户端至服务端(B=>S)

客户端代码

<script type="text/javascript">
 var ticker = $.connection.simpleHub;
 $.connection.hub.start();

 $("#btn").click(function () {

 // 链接完成以后,可以发送消息至服务端
 ticker.server.sendMsg("需要发送的消息");
 });

</script>

服务端代码

[HubName("simpleHub")]
 public class SimpleHub : Hub
 {
 public void SendMsg(string msg)
 {
  // 获取链接id
  var connectionId = Context.ConnectionId;
  // 获取cookie
  var cookie = Context.RequestCookies;

 }

 }

其中SimpleHub就是我们定义的继承Hub类SimpleHub,然后我们可以用特性HubName进行重命名。

然后开始链接。

在链接完成以后,我们就可以调用在SimpleHub类中调用的方法。这就就很简单的实现了客户端至服务端发送消息。

我们还可以在Context中获取我们想要的东西,比如链接id,cookie等。

2.2、服务端至客户端(S=>B)

服务端代码

[HubName("simpleHub")]
 public class SimpleHub : Hub
 {
 public void SendMsg(string msg)
 {
  Clients.All.msg("发送给客户端的消息");
 }

 }

客户端代码

<script type="text/javascript">

 var ticker = $.connection.groupChatHub;
 $.connection.hub.start();

 ticker.client.msg = function (data) {
 console.log(data);
 }
</script>

这里演示了怎么发送消息至客户端,也是SignalR比较重要的功能,这里有两个问题需要解决。

问题一、这里是发送消息给所有连着的客户端,如果是单个客户端或者是一批客户端应该怎么发送。

问题二、我们在调用msg给个客户端发送消息时是在接收消息以后做的反馈,然后发送消息给客户端,这样就很类似ajax了,服务端并没有主动给客户端发送消息。

解决:

问题一、Clients可以给特性的一群或者一个客户端发送消息

// 所有人
Clients.All.msg("发送给客户端的消息");
// 特定 cooectionId
Clients.Client("connectionId").msg("发送给客户端的消息");
// 特定 group
Clients.Group("groupName").msg("发送给客户端的消息");

这是比较常用的三个,当然还有很多,比如AllExcept,Clients。

在SignalR2.0中还添加了Others,OthersInGroup,OthersInGroups等等。

问题二、我们可以在需要发送消息的地方调用GlobalHost.ConnectionManager.GetHubContext<SimpleHub>().Clients中获取Clients。获取Clients并发送消息我们最好写成单例模式,因为这种需求很符合单例,群聊中有详细的代码。

3、SignalR实现群聊

以上的介绍和代码已经可以实现b=>s和s=>b了,那实现群聊和单独聊天就比较简单了。

由于功能比较简单,所有我把用户名存到了cookie里,也就说第一次进来时需要设置cookie。

还有就是在hub中要实现OnConnected、OnDisconnected和OnReconnected,然后在方法中设置用户和connectionid和统计在线用户,以便聊天使用。

hub代码

/// <summary>
 /// SignalR Hub 群聊类
 /// </summary>
 [HubName("groupChatHub")] // 标记名称供js调用
 public class GroupChatHub : Hub
 {
 /// <summary>
 /// 用户名
 /// </summary>
 private string UserName
 {
  get
  {
  var userName = Context.RequestCookies["USERNAME"];
  return userName == null ? "" : HttpUtility.UrlDecode(userName.Value);
  }
 }

 /// <summary>
 /// 在线用户
 /// </summary>
 private static Dictionary<string, int> _onlineUser = new Dictionary<string, int>();

 /// <summary>
 /// 开始连接
 /// </summary>
 /// <returns></returns>
 public override Task OnConnected()
 {
  Connected();
  return base.OnConnected();
 }

 /// <summary>
 /// 重新链接
 /// </summary>
 /// <returns></returns>
 public override Task OnReconnected()
 {
  Connected();
  return base.OnReconnected();
 }

 private void Connected()
 {
  // 处理在线人员
  if (!_onlineUser.ContainsKey(UserName)) // 如果名称不存在,则是新用户
  {

  // 加入在线人员
  _onlineUser.Add(UserName, 1);

  // 向客户端发送在线人员
  Clients.All.publshUser(_onlineUser.Select(i => i.Key));

  // 向客户端发送加入聊天消息
  Clients.All.publshMsg(FormatMsg("系统消息", UserName + "加入聊天"));
  }
  else
  {
  // 如果是已经存在的用户,则把在线链接的个数+1
  _onlineUser[UserName] = _onlineUser[UserName] + 1;
  }

  // 加入Hub Group,为了发送单独消息
  Groups.Add(Context.ConnectionId, "GROUP-" + UserName);
 }

 /// <summary>
 /// 结束连接
 /// </summary>
 /// <param name="stopCalled"></param>
 /// <returns></returns>
 public override Task OnDisconnected(bool stopCalled)
 {
  // 人员链接数-1
  _onlineUser[UserName] = _onlineUser[UserName] - 1;

  // 判断是否断开了所有的链接
  if (_onlineUser[UserName] == 0)
  {
  // 移除在线人员
  _onlineUser.Remove(UserName);

  // 向客户端发送在线人员
  Clients.All.publshUser(_onlineUser.Select(i => i.Key));

  // 向客户端发送退出聊天消息
  Clients.All.publshMsg(FormatMsg("系统消息", UserName + "退出聊天"));
  }

  // 移除Hub Group
  Groups.Remove(Context.ConnectionId, "GROUP-" + UserName);
  return base.OnDisconnected(stopCalled);
 }

 /// <summary>
 /// 发送消息,供客户端调用
 /// </summary>
 /// <param name="user">用户名,如果为0,则是发送给所有人</param>
 /// <param name="msg">消息</param>
 public void SendMsg(string user, string msg)
 {
  if (user == "0")
  {
  // 发送给所有用户消息
  Clients.All.publshMsg(FormatMsg(UserName, msg));
  }
  else
  {
  //// 发送给自己消息
  //Clients.Group("GROUP-" + UserName).publshMsg(FormatMsg(UserName, msg));

  //// 发送给选择的人员
  //Clients.Group("GROUP-" + user).publshMsg(FormatMsg(UserName, msg));

  // 发送给自己消息
  Clients.Groups(new List<string> { "GROUP-" + UserName, "GROUP-" + user }).publshMsg(FormatMsg(UserName, msg));

  }
 }

 /// <summary>
 /// 格式化发送的消息
 /// </summary>
 /// <param name="name"></param>
 /// <param name="msg"></param>
 /// <returns></returns>
 private dynamic FormatMsg(string name, string msg)
 {
  return new { Name = name, Msg = HttpUtility.HtmlEncode(msg), Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") };
 }
 }

js代码

<script type="text/javascript">
 $(function () {

  // 链接hub
  var ticker = $.connection.groupChatHub;
  $.connection.hub.start();

  // 接收服务端发送的消息
  $.extend(ticker.client, {

  // 接收聊天消息
  publshMsg: function (data) {
   $("#msg").append("<li><span class='p'>" + data.Name + ":</span>" + data.Msg + " <span class='time'>" + data.Time + "</span></li>")
   $("#msg").parents("div")[0].scrollTop = $("#msg").parents("div")[0].scrollHeight;
  },

  // 接收在线人员,然后加入Select,以供单独聊天选中
  publshUser: function (data) {
   $("#count").text(data.length);
   $("#users").empty();
   $("#users").append('<option value="0">所有人</option>');
   for (var i = 0; i < data.length; i++) {
   $("#users").append('<option value="' + data[i] + '">' + data[i] + '</option>')
   }

  }
  });

  // 发送消息按钮
  $("#btn-send").click(function () {
  var msg = $("#txt-msg").val();
  if (!msg) {
   alert('请输入内容!'); return false;
  }
  $("#txt-msg").val('');

  // 主动发送消息,传入发送给谁,和发送的内容。
  ticker.server.sendMsg($("#users").val(), msg);
  });

 });
 </script>

html代码

<h2>
 群聊系统(<span id="count">1</span>人在线):@ViewBag.UserName
</h2>

<div style="overflow:auto;height:300px">
 <ul id="msg"></ul>
</div>

<select id="users" class="form-control" style="max-width:150px;">
 <option value="0">所有人</option>
</select>

<input type="text" onkeydown='if (event.keyCode == 13) { $("#btn-send").click() }' class="form-control" id="txt-msg" placeholder="内容" style="max-width:400px;" />
<br />
<button type="button" id="btn-send">发送</button>

这样就消息了群聊和发送给特定的人聊天功能。

3.1、封装主动发送消息的单例

/// <summary>
 /// 主动发送给用户消息,单例模式
 /// </summary>
 public class GroupChat
 {
 /// <summary>
 /// Clients,用来主动发送消息
 /// </summary>
 private IHubConnectionContext<dynamic> Clients { get; set; }

 private readonly static GroupChat _instance = new GroupChat(GlobalHost.ConnectionManager.GetHubContext<GroupChatHub>().Clients);

 private GroupChat(IHubConnectionContext<dynamic> clients)
 {
  Clients = clients;
 }

 public static GroupChat Instance
 {
  get
  {
  return _instance;
  }
 }

 /// <summary>
 /// 主动给所有人发送消息,系统直接调用
 /// </summary>
 /// <param name="msg"></param>
 public void SendSystemMsg(string msg)
 {
  Clients.All.publshMsg(new { Name = "系统消息", Msg = msg, Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") });
 }
 }

如果需要发送消息,直接调用SendSystemMsg即可。

GroupChat.Instance.SendSystemMsg("消息");

 4、结语

啥也不说了直接源码

github:https://github.com/Emrys5/SignalRGroupChatDemo

在线演示:http://chat.lining.name/

最后望对各位有所帮助,本文原创,欢迎拍砖和推荐。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ASP.NET网站聊天室的设计与实现(第3节)

    大家都玩过网站聊天室吧,那知道它是怎么实现的吗? 今天我们就来设计一个网站聊天室,用户输入用户名登陆聊天室,采用框架结构实现. 学习内容: 第一步,聊天室首页与简单计数器设计 1.打开VS2008.在"解决方案'101'下新建网站,命名为Chatroom.默认首页文件为Default.aspx. 2.为Default.aspx添加窗体控件,切换到"设计"视图,从左侧工具箱标准组中拖出2个Lable控件,1个Textbox控件,一个Button控件,最后给输入昵称的Textbo

  • b/s开发常用javaScript技术第1/4页

    在b/s开发中经常用到的javaScript技术  一.验证类 1.数字验证内   1.1 整数   1.2 大于0的整数 (用于传来的ID的验证)   1.3 负整数的验证   1.4 整数不能大于iMax   1.5 整数不能小于iMin 2.时间类   2.1 短时间,形如 (13:04:06)   2.2 短日期,形如 (2003-12-05)   2.3 长时间,形如 (2003-12-05 13:04:06)   2.4 只有年和月.形如(2003-05,或者2003-5)   2.

  • 在B/S开发中经常用到的JavaScript技术第1/3页

    一.验证类 1.数字验证内   1.1 整数   1.2 大于0的整数 (用于传来的ID的验证)   1.3 负整数的验证   1.4 整数不能大于iMax   1.5 整数不能小于iMin 2.时间类   2.1 短时间,形如 (13:04:06)   2.2 短日期,形如 (2003-12-05)   2.3 长时间,形如 (2003-12-05 13:04:06)   2.4 只有年和月.形如(2003-05,或者2003-5)   2.5 只有小时和分钟,形如(12:03) 3.表单类

  • asp.net mvc signalr简单聊天室制作过程分析

    signalr的神奇.实用很早就知道,但一直都没有亲自去试用,仅停留在文章,看了几篇简单的介绍文字,感觉还是很简单易用的. 由于最后有个项目需要使用到它,所以就决定写个小程序测试一下,实践出真知:别人写的文章,由于环境(版本等)不同,还是或多或少存在一些出入的. 环境:vs2013 / asp.net mvc 5 / signalr 2.2.1 / jquery 1.10.2 先上两个效果图: 系统会自动给加入聊天室的人员分配一个ID,是该人员的唯一标识(绿色为当前用户说的话,橙色为当前用户之外

  • Asp.net使用SignalR实现酷炫端对端聊天功能

    一.引言 在前一篇文章已经详细介绍了SignalR了,并且简单介绍它在Asp.net MVC 和WPF中的应用.在上篇博文介绍的都是群发消息的实现,然而,对于SignalR是为了实时聊天而生的,自然少了不像QQ一样的端对端的聊天了.本篇博文将介绍如何使用SignalR来实现类似QQ聊天的功能. 二.使用SignalR实现端对端聊天的思路 在介绍具体实现之前,我先来介绍了使用SignalR实现端对端聊天的思路.相信大家在前篇文章已经看到过Clients.All.sendMessage(name,

  • .net的socket异步通讯示例分享

    1.首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2.然后添加服务端代码,添加命名空间,界面上添加TextBox控件 复制代码 代码如下: using System.Net;using System.Net.Sockets; 3.FormLoad方法添加代码 复制代码 代码如下: private void Form1_Load(object sender, EventArgs e)        {            IPEndPoint epS

  • ASP.NET 使用application与session对象写的简单聊天室程序

    ASP.Net中有两个重要的对象,一个是application对象,一个是session对象. Application:记录应用程序参数的对象,该对象用于共享应用程序级信息. Session:记录浏览器端的变量对象,用来存储跨网页程序程序的变量或者对象. 说实话,写了快一年的asp.net,application对象还真没怎么用过.看了看书,根据这两个对象的特性写了一个简单的聊天室程序.真的是非常的简陋. 我的思路是,有两个页面Default页和ChatRoom页,页面布局如图: Default

  • Asp.net使用SignalR实现聊天室的功能

    一.引言 在前一篇文章<Asp.net使用SignalR实现酷炫端对端聊天功能>中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用SignalR实现群聊这样的功能. 二.实现思路 要想实现群聊的功能,首先我们需要创建一个房间,然后每个在线用户可以加入这个房间里面进行群聊,我们可以为房间设置一个唯一的名字来作为标识.那SignalR类库里面是否有这样现有的方法呢?答案是肯定的. // IGroupManager接口提供如下方法 // 作用:将连接ID加入某个组 //

  • Asp.net SignalR创建实时聊天应用程序

    一.概述 使用 ASP.NET 那么 SignalR 2 创建一个实时聊天应用程序.将 SignalR 添加 MVC 5 应用程序中,并创建聊天视图发送并显示消息. 在Demo中,将学习SignalR 开发任务包括 ︰ 向 MVC 5 应用程序添加那么 SignalR 图书馆. 创建集线器和浩然启动类,以将内容推送到客户端. 使用 web 页中的那么 SignalR jQuery 库发送邮件并显示更新从集线器. 下面的屏幕快照显示在浏览器中运行的已完成的聊天应用程序. 二.实现 创建一个 ASP

  • 关于B/S判断浏览器断开的问题讨论

    客户端通过脚本和服务器保持请求,每次请求刷新一个时间,服务器检查这个时间,如果发现时间超过预定,则可以判断该客户端浏览器已关闭.然后对进行相应得操作.如果你想知道是那个客户端浏览器关闭,可以把会话绑定到轮询对象中.长连接不是所有服务器都支持得,这种方式,比你的现实多了.我的个人看法.我首先同意这几种做法,它们也能实现这个需求,他们都通过客户端的轮询,更新服务器的最后访问时间,让服务器检测超时.我来谈谈我对这2种做法的理解 1 服务器端如何进行超时判断,启动一个后台线程进行定时轮询?循环检查每个s

随机推荐