Asp.net使用SignalR实现聊天室的功能
一、引言
在前一篇文章《asp.net使用signalr实现酷炫端对端聊天功能》中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用signalr实现群聊这样的功能。
二、实现思路
要想实现群聊的功能,首先我们需要创建一个房间,然后每个在线用户可以加入这个房间里面进行群聊,我们可以为房间设置一个唯一的名字来作为标识。那signalr类库里面是否有这样现有的方法呢?答案是肯定的。
// igroupmanager接口提供如下方法 // 作用:将连接id加入某个组 // context.connectionid 连接id,每个页面连接集线器即会产生唯一id // roomname分组的名称 groups.add(context.connectionid, roomname); // 作用:将连接id从某个分组移除 groups.remove(context.connectionid, roomname); // ihubconnectioncontext接口提供了如下方法 // 调用客户端方法向房间内所有用户群发消息 // room:分组名称 // new string[0]:过滤(不发送)的连接id数组 clients.group(room, new string[0]).clientmethod
上面的代码也就是实现群聊的核心方法。groups对象说白了也就是signalr类库维护的一个列表对象而已,其实我们完全可以自己来维护一个dictionary<string, list<string>>这个对象,创建一个房间的时候,我们将房间名称和进入房间的客户端的connectionid加入到这个字典里面,然后在聊天室里面点发送消息的时候,我们根据房间名查找到所有加入群聊的connectionid,然后调用clients.clients(ilist<string> connectionids)方法来将消息群发到每个客户端。以上也就是实现聊天室的原理。
三、使用signalr实现聊天室的功能
理清楚了实现思路之后,接下来我们就看下具体的实现代码,同时大家也可以对照代码来对照前面的实现思路。
首先看下聊天室功能所涉及实体类的实现代码:
/// <summary> /// 用户类 /// </summary> public class user { /// <summary> /// 用户id /// </summary> public string userid { get; set; } /// <summary> /// 用户的连接集合 /// </summary> public list<connection> connections { get; set; } /// <summary> /// 用户房间集合,一个用户可以加入多个房间 /// </summary> public list<chatroom> rooms { get; set; } public user() { connections = new list<connection>(); rooms = new list<chatroom>(); } } public class connection { //连接id public string connectionid { get; set; } //用户代理 public string useragent { get; set; } //是否连接 public bool connected { get; set; } } /// <summary> /// 房间类 /// </summary> public class chatroom { // 房间名称 public string roomname { get; set; } // 用户集合 public list<user> users { get; set; } public chatroom() { users = new list<user>(); } } /// <summary> /// 上下文类,用来模拟ef中的dbcontext /// </summary> public class chatcontext { public list<user> users { get; set; } public list<connection> connections { get; set; } public list<chatroom> rooms { get; set; } public chatcontext() { users = new list<user>(); connections = new list<connection>(); rooms = new list<chatroom>(); } }
2. 接下来,让我们来看到集线器的实现:
[hubname("chatroomhub")] public class groupshub : hub { public static chatcontext dbcontext = new chatcontext(); #region ihub members // 重写hub连接事件 public override task onconnected() { // 查询用户 var user = dbcontext.users.firstordefault(u => u.userid == context.connectionid); if (user == null) { user = new user { userid = context.connectionid }; dbcontext.users.add(user); } // 发送房间列表 var items = dbcontext.rooms.select(p => new {p.roomname}); clients.client(this.context.connectionid).getroomlist(jsonhelper.tojsonstring(items.tolist())); return base.onconnected(); } // 重写hub连接断开的事件 public override task ondisconnected(bool stopcalled) { // 查询用户 var user = dbcontext.users.firstordefault(u => u.userid == context.connectionid); if (user != null) { // 删除用户 dbcontext.users.remove(user); // 从房间中移除用户 foreach (var item in user.rooms) { removeuserfromroom(item.roomname); } } return base.ondisconnected(stopcalled); } #endregion #region public methods // 为所有用户更新房间列表 public void updateroomlist() { var itme = dbcontext.rooms.select(p => new {p.roomname}); var jsondata = jsonhelper.tojsonstring(itme.tolist()); clients.all.getroomlist(jsondata); } /// <summary> /// 加入聊天室 /// </summary> public void joinroom(string roomname) { // 查询聊天室 var room = dbcontext.rooms.find(p => p.roomname == roomname); // 存在则加入 if (room == null) return; // 查找房间中是否存在此用户 var isexistuser = room.users.firstordefault(u => u.userid == context.connectionid); // 不存在则加入 if (isexistuser == null) { var user = dbcontext.users.find(u => u.userid == context.connectionid); user.rooms.add(room); room.users.add(user); // 将客户端的连接id加入到组里面 groups.add(context.connectionid, roomname); //调用此连接用户的本地js(显示房间) clients.client(context.connectionid).joinroom(roomname); } else { clients.client(context.connectionid).showmessage("请勿重复加入房间!"); } } /// <summary> /// 创建聊天室 /// </summary> /// <param name="roomname"></param> public void createroom(string roomname) { var room = dbcontext.rooms.find(a => a.roomname == roomname); if (room == null) { var cr = new chatroom { roomname = roomname }; //将房间加入列表 dbcontext.rooms.add(cr); // 本人加入聊天室 joinroom(roomname); updateroomlist(); } else { clients.client(context.connectionid).showmessage("房间名重复!"); } } public void removeuserfromroom(string roomname) { //查找房间是否存在 var room = dbcontext.rooms.find(a => a.roomname == roomname); //存在则进入删除 if (room == null) { clients.client(context.connectionid).showmessage("房间名不存在!"); return; } // 查找要删除的用户 var user = room.users.firstordefault(a => a.userid == context.connectionid); // 移除此用户 room.users.remove(user); //如果房间人数为0,则删除房间 if (room.users.count <= 0) { dbcontext.rooms.remove(room); } groups.remove(context.connectionid, roomname); //提示客户端 clients.client(context.connectionid).removeroom("退出成功!"); } /// <summary> /// 给房间内所有的用户发送消息 /// </summary> /// <param name="room">房间名</param> /// <param name="message">信息</param> public void sendmessage(string room, string message) { // 调用房间内所有客户端的sendmessage方法 // 因为在加入房间的时候,已经将客户端的connectionid添加到groups对象中了,所有可以根据房间名找到房间内的所有连接id // 其实我们也可以自己实现group方法,我们只需要用list记录所有加入房间的connectionid // 然后调用clients.clients(connectionidlist),参数为我们记录的连接id数组。 clients.group(room, new string[0]).sendmessage(room, message + " " + datetime.now); } #endregion }
3. 上面signalr服务端的代码实现已经完成,接下来就让我们一起看看客户端视图的实现:
@{ layout = null; } <!doctype html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>index</title> <script src="~/scripts/jquery-2.2.2.min.js"></script> <script src="~/scripts/jquery.signalr-2.2.0.min.js"></script> <script src="~/scripts/layer/layer.min.js"></script> <!--这里要注意,这是虚拟目录,也就是你在owin startup中注册的地址--> <script src="/signalr/hubs"></script> <script type="text/javascript"> var chat; var roomcount = 0; $(function() { chat = $.connection.chatroomhub; chat.client.showmessage = function(message) { alert(message); }; chat.client.sendmessage = function(roomname, message) { $("#" + roomname).find("ul").each(function() { $(this).append('<li>' + message + '</li>'); }); }; chat.client.removeroom = function(data) { alert(data); }; chat.client.joinroom = function (roomname) { var html = '<div style="float:left; margin-left:360px; border:double; height:528px;width:493px" id="' + roomname + '" roomname="' + roomname + '"><button onclick="removeroom(this)">退出</button>\ ' + roomname + '房间\ 聊天记录如下:<ul>\ </ul>\ <textarea class="chatcore_write" id="chatcore_write" style="width:400px"></textarea> <button onclick="sendmessage(this)">发送</button>\ </div>'; $("#roomlist").append(html); }; //注册查询房间列表的方法 chat.client.getroomlist = function(data) { if (data) { var jsondata = $.parsejson(data); $("#roomlist").html(" "); for (var i = 0; i < jsondata.length; i++) { var html = ' <li>房间名:' + jsondata[i].roomname + '<button roomname="' + jsondata[i].roomname + '" onclick="addroom(this)">加入</button></li>'; $("#roomlist").append(html); } } }; // 获取用户名称。 $('#username').html(prompt('请输入您的名称:', '')); $.connection.hub.start().done(function() { $('#creatroom').click(function() { chat.server.createroom($("#roomname").val()); }); }); }); function sendmessage(btn) { var message = $(btn).prev().val(); var room = $(btn).parent(); var username = $("#username").html(); message = username + ":" + message; var roomname = $(room).attr("roomname"); chat.server.sendmessage(roomname, message); $(btn).prev().val('').focus(); } function removeroom(btn) { var room = $(btn).parent(); var roomname = $(room).attr("roomname"); chat.server.removeuserfromroom(roomname); } function addroom(roomname) { var data =$(roomname).attr("roomname"); chat.server.joinroom(data); } </script> </head> <body> <div> <div>名称:<p id="username"></p></div> 输入房间名: <input type="text" value="聊天室1" id="roomname" /> <button id="creatroom">创建聊天室</button> </div> <div style="float:left;border:double"> <div>房间列表</div> <ul id="roomlist"></ul> </div> <div id="roomlist"> </div> </body> </html>
4. 经过上面3步,聊天室的功能就已经完成了,在看具体效果之前,这里附加一个帮助类的代码:
/// <summary> /// json 帮助类 /// </summary> public class jsonhelper { /// <summary> /// 从一个对象信息生成json字符串 /// </summary> /// <param name="obj"></param> /// <returns></returns> public static string tojsonstring(object obj) { return jsonconvert.serializeobject(obj); } /// <summary> /// 从json字符串生成对象 /// </summary> /// <typeparam name="t"></typeparam> /// <param name="jsonstring"></param> /// <returns></returns> public static t toobject<t>(string jsonstring) { return jsonconvert.deserializeobject<t>(jsonstring); } }
四、运行结果
接下来,就具体看看聊天室功能的运行效果,具体运行效果如下图所示:
源码下载:signalrchatroom
到这里,本篇的所有内容都介绍完了,接下来我一篇文章将实现如何使用signalr来实现发图片的功能。
推荐阅读