/// <summary> /// 清楚订阅的离线客户端 /// </summary> /// <param name="channel">订阅名称</param> public void ClearChannelClient(string channel) { string channelKey = RedisKeyFormatUtil.GetSubscribeChannelKey(_appId, channel); var websocketIds = _redis.HKeys(channelKey); var offline = new List <string>(); var span = websocketIds.AsSpan(); var start = span.Length; while (start > 0) { start = start - 10; var length = 10; if (start < 0) { length = start + 10; start = 0; } var slice = span.Slice(start, length); string onlineKey = RedisKeyFormatUtil.GetOnLineClientKey(_appId); var hvals = _redis.HMGet(onlineKey, slice.ToArray().Select(b => b.ToString()).ToArray()); for (var a = length - 1; a >= 0; a--) { if (string.IsNullOrEmpty(hvals[a])) { offline.Add(span[start + a]); span[start + a] = null; } } } //删除离线订阅 if (offline.Any()) { _redis.HDel(channelKey, offline.ToArray()); } }
/// <summary> /// 发送订阅消息,所有在线的用户将收到消息 /// </summary> /// <param name="senderClientId">发送者的客户端id</param> /// <param name="channel">订阅名称</param> /// <param name="message">消息</param> public void SendChanMessage(Guid senderClientId, string channel, string message) { string channelKey = RedisKeyFormatUtil.GetSubscribeChannelKey(_appId, channel); var websocketIds = _redis.HKeys(channelKey); IEnumerable <Guid> receiveClientId = websocketIds.Where(a => !string.IsNullOrEmpty(a)) .Select(a => Guid.TryParse(a, out var tryuuid) ? tryuuid : Guid.Empty).ToArray(); this.SendMessage(Guid.Empty, receiveClientId, message); }
/// <summary> /// 加入订阅 /// </summary> /// <param name="clientId">客户端id</param> /// <param name="channel">订阅名称</param> public void SubscribeChannel(Guid clientId, string channel) { string channelKey = RedisKeyFormatUtil.GetSubscribeChannelKey(_appId, channel); string ClientChannelKey = RedisKeyFormatUtil.GetClientChannelKey(_appId, clientId); _redis.StartPipe(a => a .HSet(channelKey, clientId.ToString(), 0) .HSet(ClientChannelKey, channel, 0) .HIncrBy(this.ChannelListKey, channel, 1)); }
public WebSocketServerBroker(WebSocketServerConfig options) : base(options) { ClusterServer = new ConcurrentDictionary <Guid, ConcurrentDictionary <Guid, WebSocketSession> >(); ChannelClient = new WebSocketChannelClient(options); _server = options.ServerPath; var ServerKey = RedisKeyFormatUtil.GetServerKey(_appId, _server); var OnLineServerKey = RedisKeyFormatUtil.GetOnLineServerKey(_appId); _redis.HSet(OnLineServerKey, _appId, _server); _redis.Subscribe((ServerKey, RedisSubScribleMessage)); }
/// <summary> /// 连接前的负载、授权,返回 ws 目标地址, /// 使用该地址连接 websocket 服务端 /// </summary> /// <param name="SessionId">客户端id</param> /// <param name="clientExtraProps">客户端相关信息,比如ip</param> /// <returns>websocket 地址:ws://xxxx/ws?token=xxx</returns> public string PrevConnectServer(Guid SessionId, string clientExtraProps) { var server = SelectServer(SessionId); var token = TokenUtil.GeneratorToken(); TokenValue tokenValue = new TokenValue() { SessionId = SessionId, clientExtraProps = clientExtraProps }; var tokenRedisKey = RedisKeyFormatUtil.GetConnectToken(this._appId, token); _redis.Set(tokenRedisKey, JsonConvert.SerializeObject(tokenValue), ConstData.TokenExpireTime); return($"ws://{server}{_pathMatch}?token={token}"); }
/// <summary> /// 新链接到达校验 /// </summary> /// <param name="context"></param> /// <returns></returns> protected TokenValue NewSessionTokenVerify(HttpContext context) { string token = context.Request.Query["token"]; if (string.IsNullOrEmpty(token)) { throw new WebSocketException((int)WebSocketErrorCodeType.TokenExpired, WebSocketErrorCodeType.TokenExpired.ToString()); } var tokenRedisKey = RedisKeyFormatUtil.GetConnectToken(this._appId, token); var token_value = _redis.Get(tokenRedisKey); if (string.IsNullOrEmpty(token_value)) { throw new WebSocketException((int)WebSocketErrorCodeType.TokenExpired, WebSocketErrorCodeType.TokenExpired.ToString()); } return(JsonConvert.DeserializeObject <TokenValue>(token_value)); }
/// <summary> /// 负载分区规则:取SessionId后四位字符, /// 转成10进制数字0-65535,求模 /// </summary> /// <param name="SessionId">客户端id</param> /// <returns></returns> protected string SelectServer(Guid SessionId) { string OnLineServerKey = RedisKeyFormatUtil.GetOnLineServerKey(_appId); var ret = _redis.HGetAll <string>(OnLineServerKey); if (ret == null || ret.Count == 0) { throw new WebSocketException((int)WebSocketErrorCodeType.ServiceNotStart, WebSocketErrorCodeType.ServiceNotStart.ToString()); } _servers = ret.Values.ToList(); var servers_idx = int.Parse(SessionId.ToString("N").Substring(28), NumberStyles.HexNumber) % _servers.Count; if (servers_idx >= _servers.Count) { servers_idx = 0; } return(_servers[servers_idx]); }
/// <summary> /// 取消订阅 /// </summary> /// <param name="clientId">客户端id</param> /// <param name="channels">订阅名称</param> public void UnSubscribeChannel(Guid clientId, params string[] channels) { if (channels?.Any() != true) { return; } using (var pipe = _redis.StartPipe()) { string ClientChannelKey = RedisKeyFormatUtil.GetClientChannelKey(_appId, clientId); foreach (var channel in channels) { string channelKey = RedisKeyFormatUtil.GetSubscribeChannelKey(_appId, channel); pipe.HDel(channelKey, clientId.ToString()) .HDel(ClientChannelKey, channel) .Eval($"if redis.call('HINCRBY', KEYS[1], '{channel}', '-1') <= 0 then redis.call('HDEL', KEYS[1], '{channel}') end return 1", this.ChannelListKey); } } }
/// <summary> /// 发送消息 /// </summary> /// <param name="notifications"></param> public void SendMessage(WebSocketNotifications notifications) { Dictionary <string, NotificationsEventArgs> redata = new Dictionary <string, NotificationsEventArgs>(); foreach (var uid in notifications.ReceiveSessionIds) { string server = SelectServer(uid); if (redata.ContainsKey(server) == false) { redata.Add(server, new NotificationsEventArgs(server, notifications)); } redata[server].AddReceiveClientId(uid); } foreach (var sendArgs in redata.Values) { OnSend?.Invoke(this, sendArgs); var ServerKey = RedisKeyFormatUtil.GetServerKey(_appId, sendArgs.Server); _redis.Publish(ServerKey, JsonConvert.SerializeObject(notifications)); } }
/// <summary> /// 获取用户参与的所有订阅 /// </summary> /// <param name="clientId">客户端id</param> /// <returns></returns> public string[] GetChanListByClientId(Guid clientId) { string ClientChannelKey = RedisKeyFormatUtil.GetClientChannelKey(_appId, clientId); return(_redis.HKeys(ClientChannelKey)); }
/// <summary> /// 获取订阅的客户端列表 /// </summary> /// <param name="channel">订阅名称</param> /// <returns></returns> public Guid[] GetChannelClientList(string channel) { string channelKey = RedisKeyFormatUtil.GetSubscribeChannelKey(_appId, channel); return(_redis.HKeys(channelKey).Select(a => Guid.Parse(a)).ToArray()); }