使用ASP.NET SignalR实现一个简单的聊天室

Posted 小确幸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用ASP.NET SignalR实现一个简单的聊天室相关的知识,希望对你有一定的参考价值。

 前言

  距离我写上一篇博客已经又过了一年半载了,时间过得很快,一眨眼,就把人变得沧桑了许多。青春是短暂的,知识是无限的。要用短暂的青春,去学无穷无尽的知识,及时当勉励,岁月不待人。今天写个随笔小结记录一下。

 什么是SignalR?

  陌生的关键字,百度科普一下,什么是SignalR?ASP .NET SignalR 是一个ASP .NET 下的类库,可以在web中实现实时通信。服务器端可以将消息自动推送到已连接的客户端。官方网站SignalR介绍写得很详细, http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr  官网是英文的,如果像我一样看英文看的头疼的,可以像我一样试试把url中的  “en-us”改为 “zh-cn” 刷新即变成中文的了,

 

 

 

先看几个我做的效果图

 

 

 

 

这个聊天室,刚开始前端我用的是BootStrap,Css和页面布局我是拷贝了网上的别人改过的,直接搬过来用的,具体是哪里找的忘记了。。  QAQ。在此谢过了。聊天室中我实现了登录,公共聊天,组件群聊,私聊,消息推送,保存聊天记录等等功能。后来基本功能实现了后,前端我使用vue.js+webapi前后端分离了。

 

新建项目,SignalR入门

1)新建一个asp.net web项目,类型为MVC,取名为SignalRChat,然后在引用中添加NuGet浏览中安装SignalR。或者在工具栏中,程序包管理控制台输入如下语句安装SignalR:install-package Microsoft.AspNet.SignalR

2)  添加hub文件
 项目右键新建文件一个文件夹取名为Hubs,在该文件夹下新建一个Signalr集线类(v2),ChatHub类得上面自定义HubName,然后在 startup文件里配置hub路径,默认得HubName是该类名称开头字母小写

3)  建立一个 OWIN Startup 类来配置应用.

 1 [assembly: OwinStartup(typeof(SignalRChat.Startup))]
 2 namespace SignalRChat   
 3 {
 4     public class Startup
 5     {
 6         public void Configuration(IAppBuilder app)
 7         {
 8             app.MapSignalR();
 9         }
10     }
11 }
View Code

 我的项目结构

Scripts中我只保留了必需用到的几个js,其他不必要的都删除,Model放实体类,common放公共类。SignalRContext类是自定义的一个类,用户保存在线用户的一些连接信息和房间信息等。没有涉及数据库,登陆数据是手动模拟造的数据。

重点在创建的ChatHub集线器中

 1  [HubName("chatHub")]
 2     public class ChatHub : Hub
 3     {
 4         #region 全局对象
 5         protected static List<UserInfo> userInfoList = new List<UserInfo>();
 6         protected static SignalRContext DbContext = new SignalRContext();
 7         protected static List<ChatHistory> chatHistoryList = new List<ChatHistory>();
 8         #endregion
 9 
10         #region 连接
11       
12         /// <summary>
13         /// 客户端重连接时
14         /// </summary>
15         /// <returns></returns>
16         public override Task OnConnected()
17         {
18             AddUserGroup();//添加用户组
19             UpdateAllRoomList();//更新房间列表
20 
21             return base.OnConnected();
22         }
23         /// <summary>
24         /// 断线
25         /// </summary>
26         /// <param name="stopCalled"></param>
27         /// <returns></returns>
28         public override Task OnDisconnected(bool stopCalled)
29         {
30             return base.OnDisconnected(stopCalled);
31         }
32         #endregion
View Code

1)前端引用自动生成得集线器代理对象

var chat = $.connection.chatHub;注意红色标明得注意取 HubName中得名称,如果hubname没注释,就取集线器类中得类名首字母小写。

 2) 开始连接服务器
   $.connection.hub.start().done(function () {  });

公共聊天方法

 1  #region 公共聊天
 2 
 3         /// <summary>
 4         /// 公共聊天
 5         /// </summary>
 6         /// <param name="message"></param>
 7         /// <param name="name"></param>
 8         public void PublicSendMsg(string message, string userId)
 9         {
10             var user = userInfoList.FirstOrDefault(x => x.UserID == userId);
11             Clients.All.sendPublicMessage(user.UserID, user.UserName, message);
12             AddChatHistory(ChatType.PubChat,user.UserName, message, user.UserID,"");//添加历史记录
13         }
14         #endregion
View Code

一对一聊天方法

 1  /// <summary>
 2         /// 发送私聊消息
 3         /// </summary>
 4         /// <param name="sendName">发送名称</param>
 5         /// <param name="userId">用户id</param>
 6         /// <param name="message">消息</param>
 7         public void SendPrivateMsg(string sendName, string userId, string message)
 8         {
 9             var toUser = userInfoList.FirstOrDefault(x => x.UserID == userId);//接收用户信息
10             var fromUser = userInfoList.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);//发送用户信息
11             if (toUser != null && fromUser != null)
12             {
13                 Clients.Caller.showMsgToPages(fromUser.UserID, sendName, message);
14                 if (fromUser.UserID != userId)//判断是否是自己给自己发消息
15                 {
16                     Clients.Client(toUser.ConnectionId).remindMsg(fromUser.UserID, fromUser.UserName,message);
17                 }
18                 AddChatHistory(ChatType.PriChat, sendName, message, fromUser.UserID, userId, "");
19             }
20         }
View Code

多对多聊天,群聊方法 

 1  /// <summary>
 2         /// 创建聊天室
 3         /// </summary>
 4         /// <param name="roomName"></param>
 5         public void CreateRoom(string roomName)
 6         {
 7             var room = DbContext.Rooms.Find(x => x.RoomName == roomName);
 8             if (room == null)
 9             {
10                 var rom = new ChatRoom
11                 {
12                     RoomName = roomName,
13                     RoomId = Guid.NewGuid().ToString().ToUpper()
14                 };
15                 DbContext.Rooms.Add(rom);//加入房间列表
16                 UpdateAllRoomList();//更新房间列表
17                 Clients.Client(Context.ConnectionId).showGroupMsg("success");
18             }
19             else
20             {
21                 Clients.Client(Context.ConnectionId).showGroupMsg("error");
22             }
23         }
24 
25         /// <summary>
26         ///加入聊天室
27         /// </summary>
28         public void JoinRoom(string roomId,string current_Id)
29         {
30             // 查询聊天室,
31             var room = DbContext.Rooms.Find(x => x.RoomId == roomId.Trim());
32             var u = userInfoList.Find(x => x.UserID == current_Id);
33             if (room != null)
34             {
35                 //检测该用户是否存在在该房间
36                 var isExistUser = room.Users.Find(x => x.UserConnectionId == Context.ConnectionId);
37                 if (isExistUser == null)
38                 {
39                     var user = DbContext.Users.Find(x => x.UserConnectionId == Context.ConnectionId);
40                     user.Rooms.Add(room);//用户信息中加入房间信息
41                     room.Users.Add(user);//房间信息中加入用户信息
42                     Groups.Add(Context.ConnectionId, room.RoomName);//添加到组中
43                     Clients.Group(room.RoomName, new string[0]).showSysGroupMsg(u.UserName);
44                 }
45             }
46             else
47             {
48                 Clients.Client(Context.ConnectionId).showMessage("该群组不存在");
49             }
50         }
51 
52         /// <summary>
53         /// 给指定房间内的所有用户发消息
54         /// </summary>
55         /// <param name="room">房间名</param>
56         /// <param name="message">消息</param>
57         public void SendMessageByRoom(string roomId, string current_Id, string message)
58         {
59             var room = DbContext.Rooms.FirstOrDefault(x=>x.RoomId==roomId);
60             var user = userInfoList.Find(x => x.UserID == current_Id);
61             if (room != null && user != null)
62             {
63                 Clients.Group(room.RoomName, new string[0]).showGroupByRoomMsg(user.UserName,room.RoomId, message);
64                 AddChatHistory(ChatType.GroChat, user.UserName, message, user.UserID, "", room.RoomId);
65             }
66         }
67 
68         /// <summary>
69         /// 退出房间
70         /// </summary>
71         public void RemoveRoom(string roomId)
72         {
73             var room = DbContext.Rooms.Find(x => x.RoomId == roomId);
74             if (room != null)
75             {
76                 var user = DbContext.Users.Find(x => x.UserConnectionId == Context.ConnectionId);
77                 room.Users.Remove(user);//从房间里移除该用户
78                 if (room.Users.Count <= 0)
79                 {
80                     DbContext.Rooms.Remove(room);//如果房间里没人了,删除该房间
81                 }
82                 Groups.Remove(Context.ConnectionId, room.RoomName);
83                 UpdateAllRoomList();//更新房间列表
84                 Clients.Client(Context.ConnectionId).removeRoom();
85             }
86             else
87             {
88                 Clients.Client(Context.ConnectionId).showMessage("该房间不存在");
89             }
90         }
View Code

 前端调用后台代码,使用  chat.server.方法名(参数1,参数2) 例如

 1   // 开始连接服务器
 2         $.connection.hub.start().done(function () {
 3         $(\'#btnSend\').click(function () {
 4             var msg = $(\'#textMessage\').val().trim();
 5             if (msg == "" || msg == undefined || msg == null) {
 6                 alert("请输入聊天信息");
 7                 $(\'#textMessage\').focus();
 8             } else {
 9                 // 调用服务器端集线器的Send方法
10                 chat.server.publicSendMsg(msg, current_userid);
11                 // 清空输入框信息并获取焦点
12                 $(\'#textMessage\').val(\'\').focus();
13             }
14         });
View Code

后台调用前端的代码。使用 chat.client.方法名。例如

1  //显示新用户加入消息
2         chat.client.showJoinMessage = function (nickName) {
3         $("#js-panel-content").append(\'<div class="js-time text-white text-center"><span>\' + nickName + \'加入了聊天</span></div>\');
4     }
View Code

 

 最后还有个保存和获取聊天记录的主要方法

 1        // <summary>
 2         /// 获取历史记录
 3         /// </summary>
 4         /// <param name="chatType">消息类型0公共聊天,1好友,2群</param>
 5         /// <param name="toId">接收者id</param>
 6         /// <param name="frmId">发送方id</param>
 7         /// <param name="roomId">房间id</param>
 8         public void GetChatHistory(int chatType =(int)ChatType.PubChat,string toId="", string frmId="",string roomId="")
 9         {
10             var list = chatHistoryList;
11             var type = (ChatType)chatType;
12             switch (type)
13             {
14                 case ChatType.PubChat:
15                     list = chatHistoryList.Where(x => x.ChatType == type).ToList();
16                     break;
17                 case ChatType.PriChat:
18                     //自己发送给对方的,和对方发给自己的数据集合
19                     list = chatHistoryList.Where(x => x.ChatType == type && ((x.toId == toId && x.frmId == frmId) || (x.toId == frmId && x.frmId == toId))).ToList();
20                     break;
21                 case ChatType.GroChat:
22                     list = chatHistoryList.Where(x => x.ChatType == type && x.RoomId == roomId).ToList();
23                     break;
24                 default:
25                     list = new List<ChatHistory>();
26                     break;
27             }
28             var data = JsonHelper.ToJsonString(list);
29             var user = userInfoList.FirstOrDefault(x=>x.UserID== frmId);
30             var conid = Context.ConnectionId;
31             if (user != null)
32             {
33                 conid = user.ConnectionId;
34             }
35             Clients.Client(conid).initChatHistoryData(data, chatType);
36         }
37         /// <summary>
38         /// 添加历史记录数据
39         /// </summary>
40         /// <param name="name"></param>
41         /// <param name="message"></param>
42         /// <param name="chatType">0公共聊天,1私聊,2群聊</param>
43         public void AddChatHistory(ChatType chatType = 0,string userName="", string message="", string frmId="",string toId="",string roomId="")
44         {
45             ChatHistory history = new ChatHistory()
46             {
47                 Hid = Guid.NewGuid().ToString().ToUpper(),
48                 ChatType = chatType,
49                 Message = message,
50                 UserName = userName,
51                 frmId = frmId,
52                 toId = toId,
53                 RoomId = roomId
54             };
55             chatHistoryList.Add(history); 

以上就是一些主要核心代码。分享给大家共同学习,共同进步,代码方面欢迎各位大佬指点。
后期补充:github地址

ASP.NET SignalR+MVC+BootStrap:https://github.com/wyanmei/SignalRChat

前后端分离的部分,我做成了两个项目,ASP.NET SignalR+WepApi+BootStrap+Vue.js
WebApi后端项目地址:https://github.com/wyanmei/SignalRChatForWebApi
Vue.js前端项目地址:https://github.com/wyanmei/SignalRChatForVue




 

以上是关于使用ASP.NET SignalR实现一个简单的聊天室的主要内容,如果未能解决你的问题,请参考以下文章

[Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

SignalR简单封装

SignalR 2.1 简单入门项目

使用 OWIN 的 ASP.NET WebApi 的有效 WebSockets 实现是啥? (不能是 SignalR。)

使用ASP.NET CORE SignalR实现APP扫描登录

在 Asp.NET MVC 中使用 SignalR 实现推送功能