定义集线器接口
IOnlineUserHub
public interface IOnlineUserHub
{
/// 在线用户列表
Task OnlineUserList(OnlineUserList context);
/// 强制下线
Task ForceOffline(object context);
/// 发布站内消息
Task PublicNotice(SysNotice context);
/// 接收消息
Task ReceiveMessage(object context);
}
实现集线器
OnlineUserHub
/// <summary>
/// 在线用户集线器
/// </summary>
[MapHub("/hubs/onlineUser")]
public class OnlineUserHub : Hub<IOnlineUserHub>
{
private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _hub;
public OnlineUserHub(...,
IHubContext<OnlineUserHub, IOnlineUserHub> hub)
{
...
// 注入 Hub
_hub = hub;
}
// 有客户端通过SignalR连接过来, 把连接信息保存到数据库
public override async Task OnConnectedAsync()
{
...... // 业务代码
var user = new SysOnlineUser
{
ConnectionId = Context.ConnectionId,
UserId = userId,
UserName = account,
RealName = realName,
Time = DateTime.Now,
Ip = httpContext.GetRemoteIpAddressToIPv4(true),
TenantId = tenantId,
};
await _sysOnlineUerRep.InsertAsync(user); // 保存当前登录用户到数据库
// 制作分组
var groupName = $"{GROUP_ONLINE}{user.TenantId}";
await this.Groups.AddToGroupAsync(Context.ConnectionId, groupName);
// 推送消息给前端, 推送给一个分组
await this.Clients.Groups(groupName).OnlineUserList(new OnlineUserList
{
RealName = user.RealName,
Online = true,
...
});
}
// 客户端断开了
public override async Task OnDisconnectedAsync(Exception exception)
{ ... }
// 自定义方法
public async Task ForceUserOffline()
{
...... // 业务代码
// 推送消息给前端, 推送给指定的人
await _hub.Clients.Client(input.ConnectionId).ForceOffline("强制下线");
}
}
前端监听消息
import { signalR } from './signalR';
onMounted(async () => {
signalR.off('ForceOffline');// 关闭监听
// 开始监听, 编写回调
signalR.on('ForceOffline', async (data: any) => {
// 收到这个消息, 把当前用户下线
// 停止接收消息
await signalR.stop();
// 告诉后端我下线了( 根据业务需要, 可以不用)
await getAPI(SysAuthApi).apiSysAuthLogoutPost();
// 清空本地Token (下线)
clearAccessTokens();
});
});
前端SignalR配置
signalR.ts
import * as SignalR from '@microsoft/signalr';
import { ElNotification } from 'element-plus';
import { getToken } from '/@/utils/axios-utils';
// 初始化SignalR对象
const connection = new SignalR.HubConnectionBuilder()
.configureLogging(SignalR.LogLevel.Information)
.withUrl(`${window.__env__.VITE_API_URL}/hubs/onlineUser?token=${getToken()}`, { transport: SignalR.HttpTransportType.WebSockets, skipNegotiation: true })
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: () => {
return 5000; // 每5秒重连一次
},
})
.build();
connection.keepAliveIntervalInMilliseconds = 15 * 1000; // 心跳检测15s
connection.serverTimeoutInMilliseconds = 30 * 60 * 1000; // 超时时间30m
// 启动连接
connection.start().then(() => {
console.log('启动连接');
});
// 断开连接
connection.onclose(async () => {
console.log('断开连接');
});
// 重连中
connection.onreconnecting(() => {
ElNotification({
title: '提示',
message: '服务器已断线...',
type: 'error',
position: 'bottom-right',
});
});
// 重连成功
connection.onreconnected(() => {
console.log('重连成功');
});
connection.on('OnlineUserList', () => {});
export { connection as signalR };