/// <summary> /// 开始监听 /// </summary> private static Task StartAsync() { return(Task.Run(() => { _webSocketServer.Start(socket => { socket.OnOpen = () => //连接建立事件 { //获取客户端信息 MessageAuthKey messageAuthKey = GetAppAuthKey(socket); if (null == messageAuthKey) { return; } if (AddAgentSocket(socket, messageAuthKey)) { LOG.Info(DateTime.Now.ToString() + $"|服务器:和客户端:{messageAuthKey.AgentType},{messageAuthKey.AppId} 建立WebSock连接!"); } }; socket.OnClose = () => //连接关闭事件 { // string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort; MessageAuthKey messageAuthKey = GetAppAuthKey(socket); if (null == messageAuthKey) { return; } if (RemoveAgentSocket(messageAuthKey)) { LOG.Info(DateTime.Now.ToString() + $"|服务器:和客户端:{messageAuthKey.AgentType},{messageAuthKey.AppId}断开WebSock连接!"); } }; socket.OnMessage = message => { // 接受客户端消息事件 SendAsync(socket, message); }; socket.OnBinary = file => { // 传入文件 string path = ("D:/test.txt"); //创建一个文件流 FileStream fs = new FileStream(path, FileMode.Create); //将byte数组写入文件中 fs.Write(file, 0, file.Length); //所有流类型都要关闭流,否则会出现内存泄露问题 fs.Close(); }; }); })); }
/// <summary> /// 获取客户端ID /// </summary> /// <param name="socket"></param> /// <returns></returns> private static MessageAuthKey GetAppAuthKey(IWebSocketConnection socket) { MessageAuthKey messageAuthKey = null; NameValueCollection queryParams = UriUtil.GetQueryString(socket.ConnectionInfo.Path); string appKey = queryParams["appKey"]; string appId = queryParams["appId"]; // string authKeyStr =socket.ConnectionInfo.Path.Substring(socket.ConnectionInfo.Path.LastIndexOf("?") + 1); string callbackMsg = ""; if (string.IsNullOrEmpty(appKey) || string.IsNullOrEmpty(appId)) { callbackMsg = $"appKey或appId 信息不存在,连接被拒绝"; LOG.Error(callbackMsg); socket.Send(callbackMsg); socket.Close(); return(null); } try { // 获取agent类型,支持多端同时连接 EnumUserAgentType enumUserAgentType = NetAssist.GetUserAgentType(socket.ConnectionInfo.Headers["User-Agent"]); string agentType = EnumUtil.GetEnumName <EnumUserAgentType>((int)enumUserAgentType); if (appId_Agent_AuthKeyMap.ContainsKey(appId)) { if (appId_Agent_AuthKeyMap[appId].ContainsKey(agentType)) { return(appId_Agent_AuthKeyMap[appId][agentType]); } } messageAuthKey = new MessageAuthKey { AppId = appId, AppKey = appKey, AgentType = agentType }; // @TODO 验证appkey的有限性和appId是否唯一 return(messageAuthKey); } catch (Exception ex) { LOG.Error(ex, ex.Message); callbackMsg = $"连接信息缺失,连接被拒绝。详情:{ex.Message}"; socket.Send(callbackMsg); socket.Close(); return(null); } }
/// <summary> /// 发送消息 /// </summary> /// <param name="socket"></param> /// <param name="message"></param> /// <returns></returns> private static Task <Boolean> SendAsync(IWebSocketConnection socket, string message) { return(Task.Run(() => { MessageAuthKey fromClientId = GetAppAuthKey(socket); if (null == fromClientId) { return false; } MessageBody messageBody = JsonConvert.DeserializeObject <MessageBody>(message); // string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort; LOG.Info(DateTime.Now.ToString() + $"|服务器:【收到】来客户端:{fromClientId.AppId}的信息:\n" + message); // 判断同一个appId下多端 if (appId_Agent_SocketsMap.ContainsKey(messageBody.To)) { IDictionary <string, IWebSocketConnection> agents = appId_Agent_SocketsMap[messageBody.To]; foreach (var agent in agents) { // 说明肯定在线 LOG.Info($"客户端[{messageBody.To}]在线,设备类型[{agent.Key}],发送在线消息"); IWebSocketConnection desSocket = agent.Value; MessageBody desSendMsgBody = new MessageBody { From = fromClientId.AppId, To = messageBody.To, ChannelType = messageBody.ChannelType, Payload = messageBody.Payload }; LOG.Info(DateTime.Now.ToString() + $"|服务器:【发出】消息,来源:{fromClientId.AppId},目的:{messageBody.To}\n,内容:{messageBody.Payload}"); desSocket.Send(JsonConvert.SerializeObject(desSendMsgBody)); } } else { LOG.Info($"客户端[{messageBody.To}]离线,发送离线消息"); // TODO 保存离线消息 } return true; })); }
/// <summary> /// 移除某类客户端连接 /// </summary> /// <param name="messageAuthKey"></param> private static Boolean RemoveAgentSocket(MessageAuthKey messageAuthKey) { //如果存在这个客户端,那么对这个socket进行移除 if (appId_Agent_SocketsMap.ContainsKey(messageAuthKey.AppId) && appId_Agent_SocketsMap[messageAuthKey.AppId].ContainsKey(messageAuthKey.AgentType)) { //注:Fleck中有释放 //关闭对象连接 //if (dic_Sockets[clientUrl] != null) //{ //dic_Sockets[clientUrl].Close(); //} appId_Agent_SocketsMap[messageAuthKey.AppId].Remove(messageAuthKey.AgentType); appId_Agent_AuthKeyMap[messageAuthKey.AppId].Remove(messageAuthKey.AgentType); } // 删除分布式缓存记录 IDictionary <string, HashSet <string> > node_agentTypesMap = ServiceFactory.CacheService.Get <IDictionary <string, HashSet <string> > >(messageAuthKey.AppId); if (node_agentTypesMap != null) { if (node_agentTypesMap.ContainsKey(GetNodeKey())) { HashSet <string> agentTypes = node_agentTypesMap[GetNodeKey()]; if (agentTypes.Remove(messageAuthKey.AgentType)) { LOG.Info($"清理缓存appId[{messageAuthKey.AppId}],agentType[{messageAuthKey.AgentType}]记录"); ServiceFactory.CacheService.ReplaceAsync(messageAuthKey.AppId, node_agentTypesMap); } else { LOG.Info($"没有找到缓存appId[{messageAuthKey.AppId}],agentType[{messageAuthKey.AgentType}]记录"); } } } return(true); }
/// <summary> /// 添加某类客户端连接 /// </summary> /// <param name="socket"></param> /// <param name="messageAuthKey"></param> private static Boolean AddAgentSocket(IWebSocketConnection socket, MessageAuthKey messageAuthKey) { // 上锁 lock (_LOCK_OBJ) { // 先判断分布式缓存中是否已经有对应的socket IDictionary <string, HashSet <string> > node_agentTypesMap = ServiceFactory.CacheService.Get <IDictionary <string, HashSet <string> > >(messageAuthKey.AppId); if (node_agentTypesMap != null) { // 当前用户id下跟多少个node建立了socket连接 bool hasSameAgentType = false; foreach (var node_agentTypesPair in node_agentTypesMap) { // 排除当前节点 if (Object.Equals(node_agentTypesPair.Key, GetNodeKey())) { continue; } HashSet <string> agentTypes = node_agentTypesPair.Value; if (agentTypes.Contains(messageAuthKey.AgentType)) { hasSameAgentType = true; // 说明需要踢掉其它节点同一个设备的socket // 发消息 KafkaProducer.Send(nameof(EnumMessageType.KICK), JsonConvert.SerializeObject(new KickMessageBody() { AppId = messageAuthKey.AppId, AgentType = messageAuthKey.AgentType, NodeKey = node_agentTypesPair.Key })); } } if (!hasSameAgentType) { // 如果其它节点没有相同的设备类型,则把这个设备类型添加到当前节点 node_agentTypesMap[GetNodeKey()].Add(messageAuthKey.AgentType); // 添加到分布式缓存中 ServiceFactory.CacheService.ReplaceAsync(messageAuthKey.AppId, node_agentTypesMap); } } else { // 添加到分布式缓存 node_agentTypesMap = new Dictionary <string, HashSet <string> > { [GetNodeKey()] = new HashSet <string>() { messageAuthKey.AgentType } }; ServiceFactory.CacheService.AddAsync(messageAuthKey.AppId, node_agentTypesMap); } IDictionary <string, IWebSocketConnection> agentSocket = null; if (appId_Agent_SocketsMap.ContainsKey(messageAuthKey.AppId)) { agentSocket = appId_Agent_SocketsMap[messageAuthKey.AppId]; if (agentSocket.ContainsKey(messageAuthKey.AgentType)) { // 强制踢人 // TODO 后续应该需要客户端主动触发确认才踢掉 agentSocket[messageAuthKey.AgentType].Close(); } agentSocket[messageAuthKey.AgentType] = socket; } else { agentSocket = new Dictionary <string, IWebSocketConnection> { [messageAuthKey.AgentType] = socket }; appId_Agent_SocketsMap.Add(messageAuthKey.AppId, agentSocket); } IDictionary <string, MessageAuthKey> agentAuthkey = null; if (appId_Agent_AuthKeyMap.ContainsKey(messageAuthKey.AppId)) { agentAuthkey = appId_Agent_AuthKeyMap[messageAuthKey.AppId]; if (!agentAuthkey.ContainsKey(messageAuthKey.AgentType)) { agentAuthkey[messageAuthKey.AgentType] = messageAuthKey; } } else { agentAuthkey = new Dictionary <string, MessageAuthKey> { [messageAuthKey.AgentType] = messageAuthKey }; appId_Agent_AuthKeyMap.Add(messageAuthKey.AppId, agentAuthkey); } return(true); } }