private void DoTask() { if (taskQueue.Count > 0) { bool res = taskQueue.TryDequeue(out OperateObject result); if (res) { try { if (result.ope != MsgOperation.登录校验) { if (!loginDic.ContainsKey(result.workSocket) || !loginDic[result.workSocket]) { Console.WriteLine($" ip=[{result.workSocket.RemoteEndPoint.ToString()}] 尚未登录! 无法执行后续操作"); return; } } switch (result.ope) { //这个用服务端主动[推] ,服务端不保存发布消息 case MsgOperation.发布广播: { PublishObject publishObject = (PublishObject)result.body; Task.Run(() => { //订阅者中有人关注这个话题 就向订阅者发送消息 if (subscribeListSocket.ContainsKey(publishObject.topic)) { Console.WriteLine($"准备发布消息给Socket订阅者,订阅数:{subscribeListSocket[publishObject.topic].Count}"); //并行执行,任务开销大的时候效率高于单纯的for循环 Parallel.ForEach(subscribeListSocket[publishObject.topic], (socket) => { if (socket != null && socket.Connected) { Send(socket, publishObject.content, MsgOperation.发布广播); } }); } if (subscribeListHttp.ContainsKey(publishObject.topic)) { Console.WriteLine($"准备发布消息给Http订阅者,订阅数:{subscribeListHttp[publishObject.topic].Count}"); //并行执行,任务开销大的时候效率高于单纯的for循环 Parallel.ForEach(subscribeListHttp[publishObject.topic], (notifyUrl) => { string resp = HttpHelper.PostJsonData(notifyUrl, JsonConvert.SerializeObject(publishObject.content)).Result; }); } }); } break; //采用得是服务端主动[推]的轮询模式,一个个按顺序发 不推荐使用 客户端主动拉比较好 case MsgOperation.发布消息: { PublishObject publishObject = (PublishObject)result.body; Task.Run(() => { //订阅者中有人关注这个话题 就向订阅者发送消息 if (subscribeListSocket.ContainsKey(publishObject.topic) && subscribeListSocket[publishObject.topic].Count > 0) { //轮询发 Console.WriteLine($"准备发布消息给Socket订阅者,订阅数:{subscribeListSocket[publishObject.topic].Count}"); tryTakeSocketAgain: subscribeListSocket[publishObject.topic].TryDequeue(out Socket socket); //头部取出来 if (socket == null || !socket.Connected) { QueueHelper.Remove(ref subscribeListSocket, publishObject.topic, socket); goto tryTakeSocketAgain; } Send(socket, publishObject.content, MsgOperation.发布消息); if (!subscribeListSocket[publishObject.topic].Contains(socket)) { subscribeListSocket[publishObject.topic].Enqueue(socket); //加入到尾部去 } } if (subscribeListHttp.ContainsKey(publishObject.topic) && subscribeListHttp[publishObject.topic].Count > 0) { Console.WriteLine($"准备发布消息给Http订阅者,订阅数:{subscribeListHttp[publishObject.topic].Count}"); tryTakeHttpAgain: subscribeListHttp[publishObject.topic].TryDequeue(out string notifyUrl); //头部取出来 string resp = HttpHelper.PostJsonData(notifyUrl, JsonConvert.SerializeObject(publishObject.content)).Result; if (resp == null || resp.ToUpper() != "SUCCESS") { QueueHelper.Remove(ref subscribeListHttp, publishObject.topic, notifyUrl); goto tryTakeHttpAgain; } if (!subscribeListHttp[publishObject.topic].Contains(notifyUrl)) { subscribeListHttp[publishObject.topic].Enqueue(notifyUrl); //加入到尾部去 } } }); } break; //采用客户端来自己[拉]取,服务端保存消息队列,可以实现一个消息只会被一个订阅者拉取消费,这样客户端可以在做一个操作,在自己任务量少于N条时,有余力处理消息就可以向服务器拉取数据,可以保持负载均衡. case MsgOperation.发布消息存消息队列: { int failcount = 3; PublishObject publishObject = (PublishObject)result.body; tryStorePublishMsgSocketAgain: if (publishMsgList.ContainsKey(publishObject.topic)) { publishMsgList[publishObject.topic].Enqueue(publishObject.content); } else { ConcurrentQueue <object> _cache = new ConcurrentQueue <object>(); _cache.Enqueue(publishObject.content); bool success = publishMsgList.TryAdd(publishObject.topic, _cache); if (!success) { failcount++; if (failcount > 3) { break; } goto tryStorePublishMsgSocketAgain; } } } break; case MsgOperation.订阅消息Socket方式: { int failcount = 3; SubscribeObject subscribeObject = (SubscribeObject)result.body; trySubscribeSocketAgain: if (subscribeListSocket.ContainsKey(subscribeObject.topic)) { if (!subscribeListSocket[subscribeObject.topic].Contains(result.workSocket)) { subscribeListSocket[subscribeObject.topic].Enqueue(result.workSocket); } } else { ConcurrentQueue <Socket> _cache = new ConcurrentQueue <Socket>(); _cache.Enqueue(result.workSocket); bool success = subscribeListSocket.TryAdd(subscribeObject.topic, _cache); if (!success) { failcount++; if (failcount > 3) { break; } goto trySubscribeSocketAgain; } } } break; case MsgOperation.订阅消息Http方式: { int failcount = 3; SubscribeObject subscribeObject = (SubscribeObject)result.body; if (string.IsNullOrEmpty(subscribeObject.notifyUrl)) { break; } trySubscribeHttpAgain: if (subscribeListHttp.ContainsKey(subscribeObject.topic)) { if (!subscribeListHttp[subscribeObject.topic].Contains(subscribeObject.notifyUrl)) { subscribeListHttp[subscribeObject.topic].Enqueue(subscribeObject.notifyUrl); } } else { ConcurrentQueue <string> _cache = new ConcurrentQueue <string>(); _cache.Enqueue(subscribeObject.notifyUrl); bool success = subscribeListHttp.TryAdd(subscribeObject.topic, _cache); if (!success) { failcount++; if (failcount > 3) { break; } goto trySubscribeHttpAgain; } } } break; //没有主题或者没消息暂时先不通知客户端 case MsgOperation.客户端主动拉取消息: { SubscribeObject subscribeObject = (SubscribeObject)result.body; if (publishMsgList.ContainsKey(subscribeObject.topic)) { tryPullMsgAgain: if (publishMsgList[subscribeObject.topic].Count > 0) { if (string.IsNullOrEmpty(subscribeObject.notifyUrl)) { //回给socket bool isGet = publishMsgList[subscribeObject.topic].TryDequeue(out object data); if (isGet) { Send(result.workSocket, data, MsgOperation.客户端主动拉取消息); } else { goto tryPullMsgAgain; } } else { //回给notifyUrl bool isGet = publishMsgList[subscribeObject.topic].TryDequeue(out object data); if (isGet) { var resp = HttpHelper.PostJsonData(subscribeObject.notifyUrl, JsonConvert.SerializeObject(publishMsgList[subscribeObject.topic])).Result; } else { goto tryPullMsgAgain; } } } } } break; case MsgOperation.取消订阅: { SubscribeObject subscribeObject = (SubscribeObject)result.body; if (subscribeListSocket.ContainsKey(subscribeObject.topic) && subscribeListSocket[subscribeObject.topic].Contains(result.workSocket)) { Task.Run(() => { QueueHelper.Remove(ref subscribeListSocket, subscribeObject.topic, result.workSocket); }); } if (subscribeListHttp.ContainsKey(subscribeObject.topic) && subscribeListHttp[subscribeObject.topic].Contains(subscribeObject.notifyUrl)) { Task.Run(() => { QueueHelper.Remove(ref subscribeListHttp, subscribeObject.topic, subscribeObject.notifyUrl); }); } } break; case MsgOperation.登录校验: { AccessObject access = (AccessObject)result.body; AccessResult accessResult = new AccessResult(); if (access != null) { if (!accessDic.ContainsKey(access.AccessKeyId)) { accessResult.Code = -1; accessResult.Message = $"[{result.workSocket.RemoteEndPoint.ToString()}]您没有登录权限"; Send(result.workSocket, accessResult, MsgOperation.登录校验); return; } DateTime loginTime = Parse.TS2DT(Convert.ToDouble(access.CurrentTimeSpan)); if (loginTime < DateTime.Now.AddMinutes(-5) || loginTime > DateTime.Now.AddMinutes(5)) { accessResult.Code = -1; accessResult.Message = $"[{result.workSocket.RemoteEndPoint.ToString()}]CurrentTimeSpan超时"; Send(result.workSocket, accessResult, MsgOperation.登录校验); return; } string prepay = access.AccessKeyId + access.CurrentTimeSpan + accessDic[access.AccessKeyId]; string sign = MD5Helper.Sign(prepay); if (sign.Equals(access.Sign.ToUpper())) { loginDic.TryAdd(result.workSocket, true); accessResult.Code = 0; accessResult.Message = $"[{result.workSocket.RemoteEndPoint.ToString()}]登录成功"; Send(result.workSocket, accessResult, MsgOperation.登录校验); } else { accessResult.Code = -1; accessResult.Message = $"[{result.workSocket.RemoteEndPoint.ToString()}]登录失败"; Send(result.workSocket, accessResult, MsgOperation.登录校验); } } } break; default: break; } } catch (Exception ex) { Console.WriteLine($"task任务处理失败 ip:[{result.workSocket.RemoteEndPoint.ToString()}] ex:{ex}"); } } } }
private void ReadCallback(IAsyncResult ar) { Socket handler = null; try { // Retrieve the state object and the handler socket // from the asynchronous state object. StateObject state = (StateObject)ar.AsyncState; handler = state.workSocket; // Read data from the client socket. int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { //不管是不是第一次接收,只要接收字节总长度小于4 if (state.readBufferLength + bytesRead < 4) { Array.Copy(state.buffer, 0, state.totalBuffer, state.readBufferLength, bytesRead); state.readBufferLength += bytesRead; //继续接收 state.buffer = new byte[StateObject.BufferSize]; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); return; } //已读长度如果小于4 要先获取总的消息长度 if (state.readBufferLength < 4) { //先拼出消息体长度 byte[] totalLengthBytes = new byte[4]; if (state.readBufferLength > 0) { Array.Copy(state.totalBuffer, 0, totalLengthBytes, 0, state.readBufferLength); } int readLength = 4 - state.readBufferLength; Array.Copy(state.buffer, 0, totalLengthBytes, state.readBufferLength, readLength); state.totalLength = ByteConvert.Bytes2UInt(totalLengthBytes); state.totalBuffer = new byte[state.totalLength]; } //还是没读完 if (state.totalLength > state.readBufferLength + bytesRead) { Array.Copy(state.buffer, 0, state.totalBuffer, state.readBufferLength, bytesRead); state.readBufferLength += bytesRead; //继续接收 state.buffer = new byte[StateObject.BufferSize]; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); return; } //已经够读完了 Array.Copy(state.buffer, 0, state.totalBuffer, state.readBufferLength, state.totalLength - state.readBufferLength); Task.Run(() => { Handle(state); }); //继续接收这个客户端发来的下一个消息 StateObject nextState = new StateObject(); nextState.workSocket = handler; if (state.readBufferLength + bytesRead > state.totalLength) { //这里说明一个完整的消息体接收完了,有可能还会读到下一次的消息体 byte[] newTotalBuffer = new byte[state.readBufferLength + bytesRead - state.totalLength]; Array.Copy(state.buffer, state.totalLength - state.readBufferLength, newTotalBuffer, 0, state.readBufferLength + bytesRead - state.totalLength); //要重新建一个对象,不然会影响到异步执行后续处理方法 nextState.readBufferLength = newTotalBuffer.Length; if (nextState.readBufferLength >= 4) { //拼出消息体长度 byte[] totalLengthBytes = new byte[4]; Array.Copy(newTotalBuffer, 0, totalLengthBytes, 0, 4); nextState.totalLength = ByteConvert.Bytes2UInt(totalLengthBytes); nextState.totalBuffer = new byte[nextState.totalLength]; } Array.Copy(newTotalBuffer, 0, nextState.totalBuffer, 0, nextState.readBufferLength); } handler.BeginReceive(nextState.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), nextState); } } catch (SocketException ex) { Console.WriteLine(ex.ToString()); if (ex.ErrorCode == 10054)//断开连接了 { if (handler == null) { return; } //登录状态移除 loginDic.TryRemove(handler, out bool loginResult); Task.Run(() => { foreach (var item in subscribeListSocket) { //队列移除 QueueHelper.Remove(ref subscribeListSocket, item.Key, handler); } }); } } }