/* Sog 游戏基础库 2016 by zouwei */ using System; using System.Net; using System.Net.Sockets; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Sog { public class TcpClientSession : ClientSession { private SocketAsyncEventArgs connArgs; private object m_asyncLocker = new object(); private long checkKeepAliveAsyncTime; public TcpClientSession(ClientSessionSettings clientSettings, SessionNetSelectMode netMode) :base(clientSettings, netMode) { AllockSocket(); NetSessionObj = new TcpSession(m_socketClient); if(netMode == SessionNetSelectMode.Asynchronous) { connArgs = new SocketAsyncEventArgs(); connArgs.RemoteEndPoint = settings.RemoteEndPoint; connArgs.Completed += OnConnectCallback; NetSessionObj.AsyncModeDataReceived += OnDataReceived; } } private void AllockSocket() { m_socketClient = new Socket(settings.RemoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); if (netSelectMode == SessionNetSelectMode.Synchronous) { m_socketClient.Blocking = false; } m_socketClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1); } public override void Connect() { TraceLog.Trace("TcpClientSession.Connect remote {0}", settings.RemoteEndPoint); m_isConnected = false; m_isConnecting = true; bool connFail = false; try { if (netSelectMode == SessionNetSelectMode.Asynchronous) { if (!m_socketClient.ConnectAsync(connArgs)) { TraceLog.Debug("TcpClientSession.Connect ConnectAsync return immediately, remote {0} result {1}" , settings.RemoteEndPoint, connArgs.SocketError); OnConnectCallback(this, connArgs); } else { TraceLog.Trace("TcpClientSession.Connect ConnectAsync return true, remote {0} wait OnConnectCallback callback" , settings.RemoteEndPoint); } } else { m_socketClient.Connect(settings.RemoteEndPoint); } } catch (ObjectDisposedException ex) { TraceLog.Error("TcpClientSession.Connect {0} ObjectDisposedException message {1}", settings.RemoteEndPoint, ex.Message); connFail = true; } catch (InvalidOperationException ex) { TraceLog.Error("TcpClientSession.Connect {0} InvalidOperationException message {1}", settings.RemoteEndPoint, ex.Message); connFail = true; } catch (SocketException ex) { if (netSelectMode == SessionNetSelectMode.Synchronous) { //这个是正常的 TraceLog.Trace("TcpClientSession.Connect {0}, wait connected finish SocketErrorCode {1},", settings.RemoteEndPoint, ex.SocketErrorCode); } else { TraceLog.Debug("TcpClientSession.Connect {0} async, SocketException err code {1}", settings.RemoteEndPoint, ex.SocketErrorCode); connFail = true; } } catch (Exception ex) { TraceLog.Error("TcpClientSession.Connect {0} fail, exception {1}", settings.RemoteEndPoint, ex); connFail = true; } if (connFail) { TcpConnectFail(); } } public override void Reconnect() { TraceLog.Trace("TcpClientSession.Reconnect remote {0}", settings.RemoteEndPoint); // 重连时m_socketClient如果不是null,要丢掉session中未发送和未解析的信息 if (m_socketClient != null) { TraceLog.Error("TcpClientSession.Reconnect close old socket"); CloseSession(); } //重连的时候重新allock socket,linux下必须如此 AllockSocket(); Connect(); } private void OnConnectCallback(object sender, SocketAsyncEventArgs e) { TraceLog.Trace("TcpClientSession.OnConnectCallback remote {0} ret {1}" , settings.RemoteEndPoint, e.SocketError); //异步模式下加个锁,防止主线程UpdateAsynchronousMode和socket回调互相影响, 同步模式下也lock下算了,性能影响不大,调用connect频率不高 lock (m_asyncLocker) { if (e.SocketError == SocketError.Success) { ConnectSucc(); return; } TcpConnectFail(); } } private void TcpConnectFail() { TraceLog.Trace("TcpClientSession.TcpConnectFail remote {0}", settings.RemoteEndPoint); CloseSession(); SessionEventArgs arg = new SessionEventArgs(); arg.Session = NetSessionObj; OnConnectFail(arg); } public override void Update(long nowMs) { UpdateConnect(); UpdateReadWrite(nowMs); } public void UpdateConnect() { if (! m_isConnecting || m_socketClient == null || netSelectMode != SessionNetSelectMode.Synchronous) { return; } if (m_socketClient.Poll(0, SelectMode.SelectError)) { TcpConnectFail(); } else { if (m_socketClient.Poll(0, SelectMode.SelectWrite)) { ConnectSucc(); } } } protected void UpdateReadWrite(long nowMs) { if (m_socketClient == null) { return; } if (m_isConnected) { if (netSelectMode == SessionNetSelectMode.Synchronous) { UpdateSynchronousMode(); } else if (netSelectMode == SessionNetSelectMode.Asynchronous) { UpdateAsynchronousMode(nowMs); } } } protected virtual void UpdateSynchronousMode() { if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } bool bError = m_socketClient.Poll(0, SelectMode.SelectError); if (bError) { SocketErrorOnUpdate(); return; } bool bReadable = m_socketClient.Poll(0, SelectMode.SelectRead); if (bReadable) { NetSessionObj.RecvReadableSocket(); Queue dataQueue = NetSessionObj.GetReceivedDataQueue(); while (dataQueue.Count > 0) { MessageData message = dataQueue.Dequeue(); SessionEventArgs e = new SessionEventArgs(); e.Message = message; e.Session = NetSessionObj; OnDataReceived(this, e); } } //read出错的情况下,read调用之后有可能socket会被关闭 if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } NetSessionObj.CheckSocketAndSendKeepAlive(); //write出错的情况下,write调用之后有可能socket会被关闭 if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } if (NetSessionObj.IsHaveDataNeedSend()) { bool bWriteable = m_socketClient.Poll(0, SelectMode.SelectWrite); if (bWriteable) { NetSessionObj.WriteWriteableSocket(); } } //write出错的情况下,write调用之后有可能socket会被关闭 if (NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } } // 异步模式update只负责发送心跳包 protected virtual void UpdateAsynchronousMode(long nowMs) { if(NetSessionObj == null) { return; } if(checkKeepAliveAsyncTime == 0) { checkKeepAliveAsyncTime = nowMs; return; } if(nowMs - checkKeepAliveAsyncTime > 5000) { checkKeepAliveAsyncTime = nowMs; lock (m_asyncLocker) { if (m_isConnected && NetSessionObj.IsSocketClosed) { SocketErrorOnUpdate(); return; } NetSessionObj.StartSendAsync(); } } } protected override void CloseSession() { TraceLog.Trace("TcpClientSession.CloseSession session {0}", NetSessionObj.SessionID); m_isConnected = false; m_isConnecting = false; //丢弃正在发送和接收的数据,关闭socket NetSessionObj.Close(); m_socketClient = null; } } }