视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Asp.net使用SignalR实现酷炫端对端聊天功能
2020-11-27 22:37:54 责编:小采
文档


一、引言

  在前一篇文章已经详细介绍了SignalR了,并且简单介绍它在Asp.net MVC 和WPF中的应用。在上篇博文介绍的都是群发消息的实现,然而,对于SignalR是为了实时聊天而生的,自然少了不像QQ一样的端对端的聊天了。本篇博文将介绍如何使用SignalR来实现类似QQ聊天的功能。

二、使用SignalR实现端对端聊天的思路

   在介绍具体实现之前,我先来介绍了使用SignalR实现端对端聊天的思路。相信大家在前篇文章已经看到过Clients.All.sendMessage(name, message);这样的代码,其表示调用所有客户端的SendMessage。SignalR的集线器使得客户端和服务端可以进行实时通信。那要实现端对端的聊天,自然就不能像所有客户端发送消息了,而只能向特定的客户端发送消息才可以,不然不就乱套了,没有任何隐私权了。那怎样才可以向特定的客户端发送消息呢?这个问题也就是我们实现端对端聊天功能的关键。

  我们发送Clients对象除了All属性外,还具有其他属性,你可以在VS中按F12来查看Clients对象的所有属性或方法,具体的定义如下:

 public interface IHubConnectionContext<T>
 {
 T All { get; } // 代表所有客户端

 T AllExcept(params string[] excludeConnectionIds); // 除了参数中的所有客户端
 T Client(string connectionId); // 特定的客户端,这个方法也就是我们实现端对端聊天的关键
 T Clients(IList<string> connectionIds); // 参数中的客户端端
 T Group(string groupName, params string[] excludeConnectionIds); // 指定客户端组,这个也是实现群聊的关键所在
 T Groups(IList<string> groupNames, params string[] excludeConnectionIds);
 T User(string userId); // 特定的用户
 T Users(IList<string> userIds); // 参数中的用户
 }

  在SignalR中,每一个客户端为标记其唯一性,SignalR都会分配它一个ConnnectionId,这样我们就可以通过ConnnectionId来找到特定的客户端了。这样,我们在向某个客户端发送消息的时候,除了要将消息传入,也需要将发送给对方的ConnectionId输入,这样服务端就能根据传入的ConnectionId来转发对应的消息给对应的客户端了。这样也就完成了端对端聊天的功能。另外,如果用户如果不在线的话,服务端可以把消息保存到数据库中,等对应的客户端上线的时候,再从数据库中查看该客户端是否有消息需要推送,有的话,从数据库取出数据,将该数据推送给该客户端。(不过这点,服务端缓存数据的功能本篇博文没有实现,在这里介绍就是让大家明白QQ一个实现原理)。

  下面我们来梳理下端对端聊天功能的实现思路:

客户端登入的时候记录下客户端的ConnnectionId,并将用户加入到一个静态数组中,该数据为了记录所有在线用户。
用户可以点击在线用户中的用户聊天,在发送消息的时候,需要将ConnectionId一并传入到服务端。
服务端根据传入的消息内容和ConnectionId调用Clients.Client(connnection).sendMessage方法来进行转发到对应的客户端。

三、实现酷炫聊天功能核心代码

   有了实现思路,实现功能也就得心应手了,接下来,让我们先看下集线器ChatHub中的代码:

public class ChatHub : Hub
 {
 // 静态属性
 public static List<UserInfo> OnlineUsers = new List<UserInfo>(); // 在线用户列表

 /// <summary>
 /// 登录连线
 /// </summary>
 /// <param name="userId">用户Id</param>
 /// <param name="userName">用户名</param>
 public void Connect(string userId, string userName)
 {
 var connnectId = Context.ConnectionId;

 if (OnlineUsers.Count(x => x.ConnectionId == connnectId) == 0)
 {
 if (OnlineUsers.Any(x => x.UserId == userId))
 {
 var items = OnlineUsers.Where(x => x.UserId == userId).ToList();
 foreach (var item in items)
 {
 Clients.AllExcept(connnectId).onUserDisconnected(item.ConnectionId, item.UserName);
 }
 OnlineUsers.RemoveAll(x => x.UserId == userId);
 }

 //添加在线人员
 OnlineUsers.Add(new UserInfo
 {
 ConnectionId = connnectId,
 UserId = userId,
 UserName = userName,
 LastLoginTime = DateTime.Now
 });
 }

 // 所有客户端同步在线用户
 Clients.All.onConnected(connnectId, userName, OnlineUsers);
 }


 /// <summary>
 /// 发送私聊
 /// </summary>
 /// <param name="toUserId">接收方用户连接ID</param>
 /// <param name="message">内容</param>
 public void SendPrivateMessage(string toUserId, string message)
 {
 var fromUserId = Context.ConnectionId;

 var toUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == toUserId);
 var fromUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == fromUserId);

 if (toUser != null && fromUser != null)
 { 
 // send to 
 Clients.Client(toUserId).receivePrivateMessage(fromUserId, fromUser.UserName, message);

 // send to caller user
 // Clients.Caller.sendPrivateMessage(toUserId, fromUser.UserName, message);
 }
 else
 {
 //表示对方不在线
 Clients.Caller.absentSubscriber();
 }
 }

 /// <summary>
 /// 断线时调用
 /// </summary>
 /// <param name="stopCalled"></param>
 /// <returns></returns>
 public override Task OnDisconnected(bool stopCalled)
 {
 var user = OnlineUsers.FirstOrDefault(u => u.ConnectionId == Context.ConnectionId);

 // 判断用户是否存在,存在则删除
 if (user == null) return base.OnDisconnected(stopCalled);

 Clients.All.onUserDisconnected(user.ConnectionId, user.UserName); //调用客户端用户离线通知
 // 删除用户
 OnlineUsers.Remove(user);


 return base.OnDisconnected(stopCalled);
 }
 }

  上面是服务端主要的实现,接下来看看客户端的实现代码:

 <script type="text/javascript">
 var systemHub = $.connection.chatHub;
 / 连接IM服务器成功
 // 主要是更新在线用户
 systemHub.client.onConnected = function (id, userName, allUsers) {
 var node = chatCore.node, myf = node.list.eq(0), str = '', i = 0;
 myf.addClass('loading');
 onlinenum = allUsers.length;
 if (onlinenum > 0) {
 str += '<li class="ChatCore_parentnode ChatCore_liston">'
 + '<h5><i></i><span class="ChatCore_parentname">在线用户</span><em class="ChatCore_nums">(' + onlinenum + ')</em></h5>'
 + '<ul id="ChatCore_friend_list" class="ChatCore_chatlist">';
 for (; i < onlinenum; i++) {
 str += '<li id="userid-' + allUsers[i].UserID + '" data-id="' + allUsers[i].ConnectionId + '" class="ChatCore_childnode" type="one"><img src="https://www.gxlcms.com/Content/Images/001.jpg?' + allUsers[i].UserID + '" class="ChatCore_oneface"><span class="ChatCore_onename">' + allUsers[i].UserName + '(' + ')</span><em class="ChatCore_time">' + allUsers[i].LoginTime + '</em></li>';
 }
 str += '</ul></li>';
 myf.html(str);
 } else {
 myf.html('<li class="ChatCore_errormsg">没有任何数据</li>');
 }
 myf.removeClass('loading');
 };
 //消息传输
 chatCore.transmit = function () {
 var node = chatCore.node, log = {};
 node.sendbtn = $('#ChatCore_sendbtn');
 node.imwrite = $('#ChatCore_write');

 //发送
 log.send = function () {
 var data = {
 content: node.imwrite.val(),
 id: chatCore.nowchat.id,
 sign_key: '', //密匙
 _: +new Date
 };

 if (data.content.replace(/\s/g, '') === '') {
 layer.tips('说点啥呗!', '#ChatCore_write', 2);
 node.imwrite.focus();
 } else {
 //此处皆为模拟
 var keys = chatCore.nowchat.type + chatCore.nowchat.id;

 //聊天模版
 log.html = function (param, type) {
 return '<li class="' + (type === 'me' ? 'ChatCore_chateme' : '') + '">'
 + '<div class="ChatCore_chatuser">'
 + function () {
 if (type === 'me') {
 return '<span class="ChatCore_chattime">' + param.time + '</span>'
 + '<span class="ChatCore_chatname">' + param.name + '</span>'
 + '<img src="' + param.face + '" >';
 } else {
 return '<img src="' + param.face + '" >'
 + '<span class="ChatCore_chatname">' + param.name + '</span>'
 + '<span class="ChatCore_chattime">' + param.time + '</span>';
 }
 }()
 + '</div>'
 + '<div class="ChatCore_chatsay">' + param.content + '<em class="ChatCore_zero"></em></div>'
 + '</li>';
 };

 log.imarea = chatCore.chatbox.find('#ChatCore_area' + keys);

 log.imarea.append(log.html({
 time: new Date().toLocaleString(),
 name: config.user.name,
 face: config.user.face,
 content: data.content
 }, 'me'));
 node.imwrite.val('').focus();
 log.imarea.scrollTop(log.imarea[0].scrollHeight);

 // 调用服务端sendPrivateMessage方法来转发消息
 systemHub.server.sendPrivateMessage(chatCore.nowchat.id, data.content);
 }

 };
 node.sendbtn.on('click', log.send);

 node.imwrite.keyup(function (e) {
 if (e.keyCode === 13) {
 log.send();
 }
 });
 };

 //用户离线
 systemHub.client.onUserDisconnected = function (id, userName) {
 onlinenum = onlinenum - 1;
 $(".ChatCore_nums").html("(" + onlinenum + ")");
 $("#ChatCore_friend_list li[data-id=" + id + "]").remove();
 };
 // 启动连接
 $.connection.hub.start().done(function () {
 systemHub.server.connect(userid, username); // 调用服务端connect方法
 });
 </script> 

  上面只是列出了一些核心代码实现。另外,为了实现的酷炫的效果,这里采用了一个Jquery插件:layer,官方网址为:http://layer.layui.com/。这个插件主要为了实现弹出框和弹出层的效果,要实现酷炫的聊天特效,就需要自己写JS代码了,由于本人并不是很熟悉前端,所以这个JS特效代码也是参考网络上的实现。大家如果想运行查看效果,建议到文章末尾下载源码进行运行。

四、最终效果

   介绍完了实现思路和实现代码之后,既然就到了我们激动人心的一刻了,那就是看看我们实现功能是否可以满足需求,另外,除了满足基本的聊天功能外,还需要看看界面是不是够酷炫。

五、总结

   看完上面的效果,是不是很炫呢。到此,本文的内容就结束了,在接下来的一篇文章中我会继续介绍如何使用Asp.net SignalR来实现聊天室的功能。

下载本文
显示全文
专题