using System; using System.Net.Sockets; using System.Threading.Tasks; namespace XGame.Framework.Network { public class TCPSession : IRemoteSession, IDisposable { readonly int TIMEOUT = 4000; private IAsyncResult _asyncResultRead; private IAsyncResult _asyncResultWrite; private Socket _socket; private NetworkStream _tcpStream; private byte[] _buffers; private volatile SessionStatus _status = SessionStatus.FREE; public SessionStatus Status => _status; public SessionAddress Address { get; private set; } private ISessionContext _context; internal TCPSession(int bufferSize, int timeout, ISessionContext context) { _buffers = new byte[bufferSize]; TIMEOUT = timeout; _context = context; } #region Connect Method private void OnDnsCompleted(SessionAddress address) { if (string.IsNullOrEmpty(address.IP)) { OnConnectFail(new Exception("[Session] 域名解析失败或IP为空")); return; } Task.Factory.StartNew(() => { try { _socket = new Socket(address.IsIpv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); IAsyncResult connectAsync = _socket.BeginConnect(address.IP, address.PORT, null, _socket); _status = SessionStatus.CONNECTING; bool success = connectAsync.AsyncWaitHandle.WaitOne(TIMEOUT, true); if (success && _socket.Connected) { _socket.EndConnect(connectAsync); _tcpStream = new NetworkStream(_socket); _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); //设置无延迟发送 OnConnected(); } else { OnConnectTimeout(); } } catch (Exception e) { OnConnectFail(e); } }); } public void Connect(AddressInfo info) { if (Status != SessionStatus.FREE) { Log.Warn($"[Session]SessionStatus:{Status} 已连接或者正在连接,又尝试连接"); return; } OnStartConnect(); if (Address != null && Address.Domain == info.Address && Address.PORT == info.Port) { OnDnsCompleted(Address); } else { Address = new SessionAddress(info); Address.ParseDnsAsync(OnDnsCompleted); } } public bool IsConnected(bool bPrecise) { if (null != _socket) { if (Status == SessionStatus.CONNECTED && _socket.Connected) { if (!bPrecise) { return true; } try { return !(_socket.Poll(1000, SelectMode.SelectRead) && _socket.Available == 0); } catch (SocketException e) { Log.Warn($"[Session]Error:SocketException --socket poll error: {e.Message} errorcode:{e.SocketErrorCode}"); return false; } catch (Exception e) { Log.Warn("[Session]Error:Exception --socket poll error: {0}", e.Message); return false; } } } return false; } private void OnConnectFail(Exception e) { _status = SessionStatus.CONNECT_FAIL; _context.Synchronizer.Enqueue(ESessionCode.ConnectFail, e); } private void OnConnectTimeout() { _status = SessionStatus.CONNECT_TIMEOUT; _context.Synchronizer.Enqueue(ESessionCode.ConnectTimeout, null); } private void OnConnected() { _status = SessionStatus.CONNECTED; _context.Synchronizer.Enqueue(ESessionCode.Connected, null); StartReceive(); } private void OnStartConnect() { _context.Synchronizer.Enqueue(ESessionCode.StartConnect, null); } #endregion #region Disconnect Method public void Disconnect() { if (Status != SessionStatus.FREE) { try { if (_asyncResultRead != null && !_asyncResultRead.IsCompleted) { _asyncResultRead.AsyncWaitHandle.Close(); _socket.Shutdown(SocketShutdown.Receive); } if (_asyncResultWrite != null && !_asyncResultWrite.IsCompleted) { _asyncResultWrite.AsyncWaitHandle.Close(); _socket.Shutdown(SocketShutdown.Send); } if (_tcpStream != null) { _tcpStream.Close(); _tcpStream.Dispose(); } _socket.Close(); _socket.Dispose(); } catch (Exception e) { OnDisconnectError(e); } _status = SessionStatus.FREE; OnDisconnect(); } } private void OnDisconnect() { _context.Synchronizer.Enqueue(ESessionCode.Disconnect, null); } private void OnDisconnectError(Exception e) { _context.Synchronizer.Enqueue(ESessionCode.DisconnectError, e); } #endregion #region Send Method public bool Send(byte[] bytes, int offset, int length) { try { _asyncResultWrite = _tcpStream.BeginWrite(bytes, offset, length, EndSend, bytes); return true; } catch (Exception e) { SessionBufferPool.Recycle(bytes); OnSendError(e); } return false; } private void EndSend(IAsyncResult ar) { try { _tcpStream.EndWrite(ar); } catch (Exception e) { OnSendError(e); } finally { var buffer = ar.AsyncState as byte[]; SessionBufferPool.Recycle(buffer); } } private void OnSendError(Exception e) { _status = SessionStatus.SEND_ERROR; _context.Synchronizer.Enqueue(ESessionCode.SendException, e); } #endregion #region Receive Method public void StartReceive() { try { _asyncResultRead = _tcpStream.BeginRead(_buffers, 0, _buffers.Length, EndReceive, _socket); } catch (Exception e) { OnReceiveError(e); } } private void EndReceive(IAsyncResult asyncReceive) { try { int length = _tcpStream.EndRead(asyncReceive); if (length > 0) { OnRead(_buffers, 0, length);//处理 StartReceive(); } else { OnRemoteClose(); } } catch (Exception e) { OnReceiveError(e); } } private void OnReceiveError(Exception e) { _status = SessionStatus.RECV_ERROR; _context.Synchronizer.Enqueue(ESessionCode.RecvError, e); } private void OnRead(byte[] bytes, int offset, int length) { var buffer = SessionBufferPool.Acquire(); Array.Copy(bytes, offset, buffer, 0, length); _context.Synchronizer.Enqueue(buffer, 0, length); } private void OnRemoteClose() { _status = SessionStatus.CLOSED; _context.Synchronizer.Enqueue(ESessionCode.SessionClose, null); } #endregion void IDisposable.Dispose() { Disconnect(); _context = null; } } }