安装Microsoft.AspNet.SignalR(使用的版本2.4.1)
新建Hubs文件夹:Hub的接口和实现
namespace SingralRedis.Hubs
{
interface IChatHub
{
//服务器下发消息到各个客户端
void SendChat(string id, string name, string message);
//用户上线通知
void SendLogin(string id, string name);
//用户下线通知
void SendLogoff(string id, string name);
//接收客户端发送的心跳包并处理
void TriggerHeartbeat(string id, string name);
}
}
提供五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect.
这里只 override OnConnected,看需要重载
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
namespace SingralRedis.Hubs
{
public class ChatHub : Hub,IChatHub
{
public override System.Threading.Tasks.Task OnConnected()
{
string userName = Context.QueryString["userName"]; //获取客户端发送过来的用户名
string userId = Context.QueryString["userId"]; //获取客户端发送过来的用户名
string connectionId = Context.ConnectionId;
if (!string.IsNullOrEmpty(userId))
{
SendLogin(userId, userName, connectionId);
}
//HttpContext.Current.Application.Add(userName, connectionId); //存储关系
return base.OnConnected();
}
public class UserChat
{
public UserChat()
{
Count = 0;
if (Timer == null) Timer = new Timer();
Timer.Interval = 1000; //1s触发一次
Timer.Start();
Timer.Elapsed += (sender, args) =>
{
Count++;
if (Count >= 20)
{
action(); //该用户掉线了,抛出事件通知
Timer.Stop();
}
};
}
private readonly Timer Timer;
public event Action action;
//public event Action action1;
public string UserId { get; set; }
public string ID { get; set; }
public string Name { get; set; }
//内部计数器(每次递增1),如果服务端每5s能收到客户端的心跳包,那么count被重置为0;
//如果服务端20s后仍未收到客户端心跳包,那么视为掉线
public int Count { get; set; }
}
public static class ChatUserCache
{
public static IList<UserChat> userList = new List<UserChat>();
}
private IList<UserChat> userList = ChatUserCache.userList;
/// <summary>
/// 推送个单个用户
/// </summary>
/// <param name="name">用户名</param>
public void SendToUser(string name)
{
//根据username获取对应的ConnectionId
var user = userList.FirstOrDefault(v => v.Name == name);
if (user != null)
{
Clients.Client(user.UserId).addNewMessageToPage("新的信息");
}
}
/// <summary>
/// 自发自收
/// </summary>
public void ReturnId()
{
Clients.Caller.CheckId(Context.ConnectionId);
}
/// <summary>
/// 获取全部用户
/// </summary>
public void GetAllUserList()
{
Clients.All.loginUser(userList);
}
public void SendChat(string id, string name, string message)
{
Clients.Client(id).GetChatMsg("<====用户 " + name +" 发送消息 "+ message+" ======>");
}
public void SendLogin(string id, string name, string connectionId)
{
connectionId = string.IsNullOrEmpty(connectionId) ? connectionId = Context.ConnectionId : connectionId;
if (!userList.Any(v => v.ID == id))
{
var userInfo = new UserChat() { UserId = connectionId, ID = id, Name = name };
userInfo.action += () =>
{
//用户20s无心跳包应答,则视为掉线,会抛出事件,这里会接住,然后处理用户掉线动作。
SendLogoff(id, name);
};
userList.Add(userInfo);
}
else
{
var user = userList.FirstOrDefault(v => v.ID == id);
user.UserId = connectionId;
user.Count = 0;
}
Clients.All.loginUser(userList);
}
public void SendLogoff(string id, string name)
{
var userInfo = userList.Where(x => x.ID.Equals(id) && x.Name.Equals(name)).FirstOrDefault();
if (userInfo != null)
{
var name1 = userInfo.Name;
if (userList.Remove(userInfo))
{
Clients.All.logoffUser(userList);
SendChat(id, name, "<====用户 " + name + " 退出了讨论组====>");
}
}
}
public void TriggerHeartbeat(string id, string name)
{
var userInfo = userList.Where(x => x.ID.Equals(id) && x.Name.Equals(name)).FirstOrDefault();
if (userInfo != null)
{
userInfo.Count = 0; //收到心跳,重置计数器
}
}
}
}
新建Startup.cs文件
using Owin;
namespace SingralRedis
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
//会默认指定一个路径 "/signalr"
app.MapSignalR();
}
}
}
来源:oschina
链接:https://my.oschina.net/u/4266023/blog/4458794