using System; using XGame.Framework.Interfaces; using XGame.Framework.Json; using XGame.Framework.Time; namespace XGame.Framework.Network { public partial class NetModule : INetModule, IDisposable, IHeartbeatListener, IMsgReceiverListener, ISessionContext { public IMsgSender Sender { get; private set; } public IMsgGenerator Generator { get; private set; } public IMsgProcesser Processer { get; private set; } public IFrameSynchronizer Synchronizer { get; private set; } public ISerializer Serializer { get; private set; } public ITimeModule Time { get; private set; } private IHeartbeat _heartBeat; private MsgContextCache _contextCache; private MsgContextCache ContextCache => _contextCache ??= new MsgContextCache(); /// 网络消息打印开关 public bool IsDebug { get; set;} /// 加密开关,SetKey时默认设置 public bool IsOpenEncrypt { get; private set;} private IMsgEncryptor _encryptor; public IMsgEncryptor Encryptor => _encryptor ??= new RC4Encryptor(); private IMsgCompressor _compressor; public IMsgCompressor Compressor => _compressor ??= new ZstdCompressor(); #region 网络会话变量 private TCPSession _tcpRemote; private WSSession _wsRemote; private IRemoteSession _curRemote; private AddressInfo _addressInfo; #endregion private IBytesReader _bytesReader; private INetModuleListener _listener; public NetModule(ITimeModule time, IMsgGenerator generator, INetModuleListener listener) { Time = time; Generator = generator; _listener = listener; Sender = new MsgSender(this); var receiver = new MsgReceiver(this, this); _bytesReader = new BytesReader(receiver); Processer = new MsgProcesser(this, this); Serializer = new JsonSerializer(generator); Synchronizer = new FrameSynchronizer(_bytesReader, Processer, this); } #region 接口实现 public void SetEncryptKey(string key) { IsOpenEncrypt = !string.IsNullOrEmpty(key); if (IsOpenEncrypt) { Encryptor.SetKey(key); } } public void Connect(AddressInfo address) { Assert.IsNotNull(address, $"[NetModule] 连接失败, AddressInfo 不能为空."); _addressInfo = address; InnerConnect(address); } public void Disconnect() { _heartBeat?.Stop(); Synchronizer.Reset(); _bytesReader.Reset(); _curRemote?.Disconnect(); _curRemote = null; } public void Reconnect() { Disconnect(); Assert.IsNotNull(_addressInfo, $"[NetModule] 重连失败, AddressInfo 不能为空."); InnerConnect(_addressInfo); } void INetModule.Send(IMsgRequest msg, bool isFilter) { var args = new RequestEventArgs() { protoId = msg.ProtocolID, isFilter = isFilter, }; if (!IsConnected) { args.code = ESessionCode.SendNoConnect; } else if (!Sender.IsCanSend(msg, isFilter)) { args.code = ESessionCode.SendFilted; } else { if (IsDebug) { Log.Info("[NetModule] Send <{1}> {2}: Seq:{4} Timestamp:{0}\n{3}", Time.GetNowTime(ClockType.Client), msg.ProtocolID, msg.GetType().Name, XJson.ToJson(msg), msg.InstanceID); } ContextCache.Push(msg.InstanceID, msg.ProtocolID, msg.Context); args.seqId = msg.InstanceID; args.code = Sender.Send(msg, IsOpenEncrypt); } _listener?.OnRequest(args); } void INetModule.StartHeartbeat() { if (IsConnected) { var impl = _heartBeat ??= new Heartbeat(this, this); impl.Start(); } else { Log.Error($"[NetModule] 心跳启动失败, 网络还没连接或初始化."); } } void INetModule.StopHeartbeat() { _heartBeat?.Stop(); } void IDisposable.Dispose() { _listener = null; _bytesReader = null; (Synchronizer as IDisposable)?.Dispose(); (Processer as IDisposable)?.Dispose(); (Sender as IDisposable)?.Dispose(); (_heartBeat as IDisposable)?.Dispose(); _heartBeat = null; _contextCache?.Dispose(); _contextCache = null; _compressor = null; _encryptor = null; (_tcpRemote as IDisposable)?.Dispose(); (_wsRemote as IDisposable)?.Dispose(); _curRemote = null; _tcpRemote = null; _wsRemote = null; _addressInfo = null; } public IRemoteSession Session { get { Assert.IsNotNull(_curRemote, $"[NetModule] 网络还没连接或初始化."); return _curRemote; } } void IHeartbeatListener.OnTimeout() { //TODO 广播 _listener?.OnHeartbeatTimeout(); } #endregion #region IMsgReceiverListener 实现 void IMsgReceiverListener.OnHeartbeatReceived() { _heartBeat?.Receive(); } void IMsgReceiverListener.OnPreMessage(IMessage message) { Sender.VerifyInstanceID(message.InstanceID); if (message is IMsgResponse) { var seqID = message.InstanceID; var protoID = message.ProtocolID; if (ContextCache.Pop(seqID, out var reqProtocol, out var reqContext)) { if (protoID != reqProtocol) { if (protoID != NetDefine.GATEWAY_ERROR_ID)//protoId=1为网关异常消息,由业务处理,框架屏蔽此项的提示 Log.Error($"[NetModule] [Response] ProtoID和Seq没有对应 recv-ProtoID:{protoID} seq:{seqID} seq-ProtoID:{reqProtocol}"); _listener?.OnResponseSeqError(seqID, reqProtocol, protoID); } else { // 将缓存的Context赋值给Response message.Context = reqContext; } } else { //收到 没有context的消息 if (protoID != NetDefine.GATEWAY_ERROR_ID)//protoId=1为网关异常消息,由业务处理,框架屏蔽此项的提示 Log.Error($"[NetModule] [Response] seq:{seqID} protoid:{protoID} seq没有对应的contextInfo"); _listener?.OnResponseSeqError(seqID, -1, protoID); } } if (IsDebug) { Log.Info("[NetModule] Receive <{1}> {2}: Seq:{4} Timestamp:{0}\n{3}", Time.GetNowTime(ClockType.Client), message.ProtocolID, message.GetType().Name, XJson.ToJson(message), message.InstanceID); } } void IMsgReceiverListener.OnResponseFinish(ResponseEventArgs args) { _listener?.OnResponseFinish(args); } void IMsgReceiverListener.OnEvent(ESessionCode code, Exception exception) { LogException(code, exception); SessionEventArgs args = default; args.code = code; switch (code) { case ESessionCode.StartConnect: _listener?.OnConnectStart(); return; case ESessionCode.Connected: (Sender as IReset)?.Reset();//seq、lastid重置 ContextCache.Reset();//清空缓存 _listener?.OnConnected(); return; case ESessionCode.Disconnect: _listener?.OnDisconnected(); return; case ESessionCode.ConnectFail: //args.errorMsg = exception.Message; //break; case ESessionCode.DisconnectError: case ESessionCode.SendException: case ESessionCode.RecvError: args.errorMsg= exception.Message; if (exception is System.Net.Sockets.SocketException se) { args.isSocketError = true; args.socketError = se.SocketErrorCode; } else if (exception is Web.WebSocketException webEx) { args.websocketCode = webEx.code; } break; } _listener?.OnSessionError(args); } private void LogException(ESessionCode sessionCode, Exception ex) { if (ex != null) { string socketCode; if (ex is System.Net.Sockets.SocketException se) { socketCode = se.SocketErrorCode.ToString(); } else { socketCode = "NONE"; } Log.Exception($"[NetModule] Exception: Code: {sessionCode}, SocketError: {socketCode}\n", ex); } } #endregion private void InnerConnect(AddressInfo address) { if (_curRemote != null && _curRemote.Status != SessionStatus.FREE) { Log.Error($"[NetModule] SessionStatus:{_curRemote.Status} 已连接或者正在连接,又尝试连接"); return; } switch (address.ProtocolType) { case ProtocolType.TCP: _curRemote = _tcpRemote ??= new TCPSession(NetDefine.SESSION_BUFFER_SIZE, NetDefine.SESSION_TIMEOUT, this); break; case ProtocolType.WS: case ProtocolType.WSS: #if !UNITY_EDITOR && UNITY_WEBGL _curRemote = new Web.WebSocket(this); #else _curRemote = _wsRemote ??= new WSSession(NetDefine.SESSION_BUFFER_SIZE, NetDefine.SESSION_TIMEOUT, this); #endif break; default: Log.Error($"[NetModule] 连接失败,协议类型错误{address.ProtocolType}"); return; } Log.Debug($"[NetModule] StartConnect: Type:{address.ProtocolType}, Address:{address.Address}, Port:{address.Port}"); _curRemote.Connect(address); } private bool IsConnected => _curRemote != null && _curRemote.Status == SessionStatus.CONNECTED; } }