SignalR实现在线聊天室功能
2016-04-24 20:46
302 查看
一、在线聊天室
1、新建解决方案 SignalROnlineChatDemo2、新建MVC项目 SignalROnlineChatDemo.Web
(无身份验证)
3、安装SignalR
PM> install-package Microsoft.AspNet.SignalR
4、 创建一个称为 Startup.cs 的新类
public class Startup { public void Configuration(IAppBuilder app) { // 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888 app.MapSignalR(); } }
5、添加Hubs
public class ChatHub : Hub { public void Hello() { Clients.All.hello(); } }
6、Action/View
/// <summary> /// 在线聊天室 /// </summary> /// <returns></returns> public ActionResult Chat(string groupName) { if (string.IsNullOrWhiteSpace(groupName)) { return Content("groupName is nllOrWhiteSpace"); } return View((object)groupName); }
@model string @{ ViewBag.Title = "Chat"; } <style> .chat-container div { margin: 10px 0; } .dN { display: none; } </style> <h2>Chat <span id="chatRoomName"></span></h2> <div class="chat-container"> <ul id="discussion"></ul> <div class="sendto-wrap dN"> 发送给: <span></span> </div> <div class="sendcontent-wrap"> <input type="text" name="message" /> <input type="button" id="btnSendMessage" value="Send" /> </div> </div> @section scripts{ <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script> <!-- Reference the autogenerated SignalR hub script. --> <script src="~/signalr/hubs"></script> <script type="text/javascript"> $(function () { //聊天室编号 var groupName = '@Model'; //成员昵称 var nickName = ''; //成员聊天Id var connectionId = ''; //Reference the auto-generated proxy for the hub. var chat = $.connection.chatHub; $("#chatRoomName").html(groupName); //Get the user name and store it to prepend to message while (nickName == '' || $.trim(nickName) == '') { nickName = prompt('Enter your name:', '') $('#displayname').val(nickName); } $('#message').focus(); }); </script> }
7、功能1:新成员加入,群发欢迎
/// <summary> /// newcomer 进入聊天室 /// </summary> /// <param name="groupName"></param> public void JoinGroup(string groupName, string userNickName) { //对聊天室成员群发‘新成员加入’ var conId = Context.ConnectionId; Groups.Add(conId, groupName); var psn = new ChatPerson() { ConnectionId = conId, NickName = userNickName, GroupName = groupName, }; Clients.Caller.setCallerInfo(psn); //Clients.Group(groupName).welcome(psn); //不能广播给自己,所以分成了两句 Clients.Caller.welcome(psn); Clients.Group(groupName, conId).welcome(psn); }
//新成员身份信息(connectionId) chat.client.setCallerInfo = function (psn) { connectionId = psn.ConnectionId; groupName = psn.groupName; }; //welcome newcomer chat.client.welcome = function (psn) { $("#discussion").append('<li><a href="javascript:;" data-conId="' + psn.ConnectionId + '">' + psn.NickName + '</a>加入了聊天室</li>'); };
结果截图:
8、功能2:群发
/// <summary> /// newcomer进入聊天室,对聊天室成员群发‘新成员加入’ /// </summary> /// <param name="groupName"></param> public void JoinGroup(string groupName, string userNickName) { var conId = Context.ConnectionId; var psn = new ChatPerson() { ConnectionId = conId, NickName = userNickName, GroupName = groupName, }; //成员信息计入Redis中 //var redisClient = RedisManager.GetClient(); //if (redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, psn.ConnectionId)) != null) //{ // //connected // return; //} //redisClient.Set<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, psn.ConnectionId), psn); //redisClient.SaveAsync(); using (var redisClient = RedisManager.GetClient()) { IRedisTypedClient<ChatPerson> psns = redisClient.As<ChatPerson>(); if (psns.GetValue(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, psn.ConnectionId)) != null) { //connected return; } psns.SetValue(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, psn.ConnectionId), psn); } Groups.Add(conId, groupName); Clients.Caller.setCallerInfo(psn); //Clients.Group(groupName).welcome(psn); //不能广播给自己,所以分成了两句 Clients.Caller.welcome(psn); Clients.Group(groupName, conId).welcome(psn); } /// <summary> /// 群发内容 /// </summary> public void SendMessage(string message) { if (string.IsNullOrWhiteSpace(message)) { return; } var conId = Context.ConnectionId; ChatPerson psn = null; var redisClient = RedisManager.GetClient(); if ((psn = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, conId))) == null || string.IsNullOrWhiteSpace(psn.GroupName) || string.IsNullOrWhiteSpace(psn.NickName) ) { //invalid ConnectionId return; } Clients.Group(psn.GroupName).sendMessage(conId, psn.NickName, message); }
//群发内容 chat.client.sendMessage = function (sendFromConnectionId, sendFromNickName, message) { $("#discussion").append('<li><a href="javascript:;" data-conId="' + sendFromConnectionId + '">' + sendFromNickName + '</a>:' + message + '</li>'); };
结果截图:
9、功能3:回复
/// <summary> /// 回复(@) /// </summary> /// <param name="sendTo"></param> /// <param name="message"></param> public void SendMessageTo(string sendTo, string message) { if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(sendTo)) { return; } var connId = Context.ConnectionId; if (connId == sendTo) { return; } ChatPerson curPerson = null; ChatPerson desPerson = null; var redisClient = RedisManager.GetClient(); if ((curPerson = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, connId))) == null || string.IsNullOrWhiteSpace(curPerson.GroupName) || string.IsNullOrWhiteSpace(curPerson.NickName) ) { //invalid ConnectionId return; } if ((desPerson = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, sendTo))) == null || string.IsNullOrWhiteSpace(desPerson.GroupName) || string.IsNullOrWhiteSpace(desPerson.NickName) ) { //invalid ConnectionId return; } if (curPerson.GroupName != desPerson.GroupName) { return; } Clients.Group(curPerson.GroupName).sendMessageTo(curPerson.ConnectionId, curPerson.NickName, desPerson.ConnectionId, desPerson.NickName, message); }
//at回复 chat.client.sendMessageTo = function (fromConnId, fromNickName, toConnId, toNickName, message) { $("#discussion").append('<li data-connId="' + fromConnId + '" data-nickName="' + fromNickName + '"><a href="javascript:;">' + fromNickName + '</a>对<a href="javascript:;">' + toNickName + '</a> 说:' + message + ' ' + (fromConnId == connectionId ? '' : ' <a href="javascript:;" action="at">@他</a>') + '' + (fromConnId == connectionId ? '' : ' <a title="屏蔽其发言" href="javascript:;" action="shielding">屏蔽</a>') + '</li>'); };
//发送 $("#btnSendMessage").click(function () { var desConnId = $('.sendto-wrap a').attr('data-desConnId'); if (!desConnId || desConnId.length == 0) { //群发 chat.server.sendMessage($('[name=message]').val()); } else { //回复 chat.server.sendMessageTo(desConnId, $('[name=message]').val()); } $('.sendto-wrap').addClass('dN'); $('.sendto-wrap a').attr('data-desConnId', ''); $('[name=message]').val('').focus(); }); //at $('#discussion').on('click', '[action=at]', function () { var desConnId = $(this).closest('li').attr('data-connId'); var desNickName = $(this).closest('li').attr('data-nickName'); $('.sendto-wrap').removeClass('dN'); $('.sendto-wrap a').attr('data-desConnId', desConnId).html(desNickName); });
结果截图:
10、功能4:私信
/// <summary> /// 私信给 /// </summary> /// <param name="sendTo"></param> /// <param name="message"></param> public void PrivateMessageTo(string sendTo, string message) { if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(sendTo)) { return; } var connId = Context.ConnectionId; if (connId == sendTo) { return; } ChatPerson curPerson = null; ChatPerson desPerson = null; var redisClient = RedisManager.GetClient(); if ((curPerson = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, connId))) == null || string.IsNullOrWhiteSpace(curPerson.GroupName) || string.IsNullOrWhiteSpace(curPerson.NickName) ) { //invalid ConnectionId return; } if ((desPerson = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, sendTo))) == null || string.IsNullOrWhiteSpace(desPerson.GroupName) || string.IsNullOrWhiteSpace(desPerson.NickName) ) { //invalid ConnectionId return; } if (curPerson.GroupName != desPerson.GroupName) { return; } Clients.Caller.myPrivateMessageTo(desPerson.ConnectionId, desPerson.NickName, message); Clients.Client(sendTo).bePrivateMessageTo(curPerson.ConnectionId, curPerson.NickName, message); }
//私信 $('#discussion').on('click', '[action=privateAt]', function () { var desConnId = $(this).closest('li').attr('data-connId'); var desNickName = $(this).closest('li').attr('data-nickName'); $('.sendto-wrap span.sendto-to').html('私信给'); $('.sendto-wrap').removeClass('dN'); $('.sendto-wrap a').attr('data-desConnId', desConnId).attr('data-desAction', 'privateAt').html(desNickName); });
//privateAt私信 //我发的 chat.client.myPrivateMessageTo = function (toConnId, toNickName, message) { $("#discussion").append('<li data-connId="' + connectionId + '" data-nickName="' + nickName + '">我对<a href="javascript:;">' + toNickName + '</a> 说:' + message + ' ' //+ getActionBlockHtml(connectionId) ); }; //发给我的 chat.client.bePrivateMessageTo = function (fromConnId, fromNickName, message) { $("#discussion").append('<li data-connId="' + fromConnId + '" data-nickName="' + fromNickName + '"><a href="javascript:;">' + fromNickName + '</a>对我私信说:' + message + ' ' + getActionBlockHtml(fromConnId) ); };
结果截图:
11、功能5:屏蔽
/// <summary> /// /// </summary> public class PersonShielding { /// <summary> /// 成员的ConnectionId /// </summary> public string ConnectionId { get; set; } /// <summary> /// 被屏蔽 我被哪些人屏蔽(这样设计似乎不合理,但好用) /// </summary> public string[] BeShieldingByConnIdArr { get; set; } }
/// <summary> /// 屏蔽某人的发言 /// </summary> /// <param name="desConId"></param> public void Shielding(string desConnId) { if (string.IsNullOrWhiteSpace(desConnId)) { return; } var connId = Context.ConnectionId; if (connId == desConnId) { return; } var redisClient = RedisManager.GetClient(); ChatPerson curPerson = null; ChatPerson desPerson = null; if ((curPerson = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, connId))) == null || string.IsNullOrWhiteSpace(curPerson.GroupName) || string.IsNullOrWhiteSpace(curPerson.NickName) ) { //invalid ConnectionId return; } if ((desPerson = redisClient.Get<ChatPerson>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, desConnId))) == null || string.IsNullOrWhiteSpace(desPerson.GroupName) || string.IsNullOrWhiteSpace(desPerson.NickName) ) { //invalid ConnectionId return; } var personShielding = redisClient.Get<PersonShielding>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_PersonShielding, desConnId)); if (personShielding == null) { personShielding = new PersonShielding() { ConnectionId = desConnId, BeShieldingByConnIdArr = new string[] { connId } }; } else { if (personShielding.BeShieldingByConnIdArr == null) { personShielding.BeShieldingByConnIdArr = new string[] { connId }; } else if (!personShielding.BeShieldingByConnIdArr.Contains(connId)) { personShielding.BeShieldingByConnIdArr = personShielding.BeShieldingByConnIdArr.Union(new string[] { connId }).ToArray(); } } redisClient.Set<PersonShielding>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_PersonShielding, desConnId), personShielding); redisClient.SaveAsync(); Clients.Caller.shieldingSuccess(desConnId, desPerson.NickName); }
var personShielding = redisClient.Get<PersonShielding>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_PersonShielding, connId)); if (personShielding != null && personShielding.BeShieldingByConnIdArr != null && personShielding.BeShieldingByConnIdArr.Length > 0) { //屏蔽我的 不发 Clients.Group(curPerson.GroupName, personShielding.BeShieldingByConnIdArr).sendMessage(connId, curPerson.NickName, message); } else { Clients.Group(curPerson.GroupName).sendMessage(connId, curPerson.NickName, message); }
var personShielding = redisClient.Get<PersonShielding>(string.Format(RedisKey.SignalROnlineChatDemoWebModels_PersonShielding, connId)); if (personShielding == null || personShielding.BeShieldingByConnIdArr == null || !personShielding.BeShieldingByConnIdArr.Contains(sendTo)) { //sendTo没有屏蔽我,那我就发 Clients.Group(curPerson.GroupName).sendMessageTo(curPerson.ConnectionId, curPerson.NickName, desPerson.ConnectionId, desPerson.NickName, message); }
//屏蔽 $('#discussion').on('click', '[action=shielding]', function () { if (confirm('确定屏蔽其发言么!')) { var desConnId = $(this).closest('li').attr('data-connId'); chat.server.shielding(desConnId); } });
//屏蔽成功 callback chat.client.shieldingSuccess = function (desConnId, desNickName) { $("#discussion").append('<li data-connId="' + connectionId + '" data-nickName="' + nickName + '">你成功屏蔽了<a href="javascript:;">' + desNickName + '</a>的发言' + '</li>' ); };
结果截图:
二、其他工作
1、退出后重新接入,能确定唯一身份么?关于这个问题,通常的解决方案是:使用UserId,以UserId作为主线。当时是想到一点就写一点的!
如果能在页面加载$.connection.hub.start()的时候带入上次使用的connectionId就好了,但暂时我还没发现能这么做
(如果谁知道怎么解决,分享给我下,谢谢!!!)
2、退出聊天室时从Redis清理用户的相关数据
先来看看Hub的这三个方法:Hub.OnConnected 、Hub.OnDisconnected 、 Hub.OnReconnected
/// <summary> /// Called when the connection connects to this hub instance. /// </summary> /// <returns></returns> public override Task OnConnected() { DefaultLoggerProvider.Instance.InfoFormat("ChatHub.OnConnected, ConnectionId:{0}", Context.ConnectionId); return base.OnConnected(); } /// <summary> /// Called when a connection disconnects from this hub gracefully or due to a timeout. /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> public override Task OnDisconnected(bool stopCalled) { DefaultLoggerProvider.Instance.InfoFormat("ChatHub.OnDisconnected, ConnectionId:{0}", Context.ConnectionId); return base.OnDisconnected(stopCalled); } /// <summary> /// Called when the connection reconnects to this hub instance. /// </summary> /// <returns></returns> public override Task OnReconnected() { DefaultLoggerProvider.Instance.InfoFormat("ChatHub.OnReconnected, ConnectionId:{0}", Context.ConnectionId); return base.OnReconnected(); }
从查看日志可以得出结论:
Onconnected 是在加载页面/刷新页面重新加载的时候触发($.connection.hub.start())。
OnDisConnected 在离开(关闭选项卡/刷新页面)的时候触发($.connection.hub.stop())。
OnReconnected 仅会在生成网站的时候触发。重启网站不会触发。(暂不知所以然)
所以可以这样
public override Task OnDisconnected(bool stopCalled) { var connId = Context.ConnectionId; DefaultLoggerProvider.Instance.InfoFormat("ChatHub.OnDisconnected, ConnectionId:{0}, stopCalled:{1}", connId, stopCalled); using (var redisClient = RedisManager.GetClient()) { redisClient.Remove(string.Format(RedisKey.SignalROnlineChatDemoWebModels_ChatPerson, connId)); redisClient.Remove(string.Format(RedisKey.SignalROnlineChatDemoWebModels_PersonShielding, connId)); redisClient.SaveAsync(); } return base.OnDisconnected(stopCalled); }
附:源码下载
相关文章推荐
- 第八周工作总结
- Android WebView 获取网页数据(html)
- Vanya and Brackets
- python自顶向下的设计方法进行体育竞技分析
- UVA_457题的一些思考
- STL之deque详解
- LeetCode 237. Delete Node in a Linked List C语言
- Android UI基础——ListView性能优化ViewHolder
- 基于POSIX信号量实现生产者消费模型
- 2.3 在maven项目中搭建springmvc中spring
- 使用Safari进行调试
- CodeForces 427B Prison Transfer
- html基础
- hdu 1392(凸包)
- linux命令---删除一个目录下的所有文件,但保留一个指定文件
- 党课心得一(杂谈)
- 转:在Eclipse中关联Android Private Libraries中文件的源代码
- fixed 实现div居中
- markdown 公式实例
- Map与JavaBean之间的相互转化