/* Sog 游戏基础库 2016 by zouwei */ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Net.Sockets; namespace SogClient { public class TcpSession :NetSession { const int TCP_BUFFER_SIZE = 1024 * 64; private bool m_recvAsyncPending; private bool m_sendAsyncPending; private object m_recvAsyncLocker; private object m_sendAsyncLocker; private SocketAsyncEventArgs m_recvAsyncEventArgs; private SocketAsyncEventArgs m_sendAsyncEventArgs; // todo shutdown和dontlinger有什么区别? private bool needShutdown; // 只是为了判断是否需要socket.shutdown public TcpSession(Socket socket):base(socket) { m_recvBuffer = new byte[TCP_BUFFER_SIZE]; m_sendBuffer = new byte[TCP_BUFFER_SIZE]; InitRecvSendAsyncObject(); m_remoteEndPoint = (IPEndPoint)m_socket.RemoteEndPoint; m_localEndPoint = (IPEndPoint)m_socket.LocalEndPoint; // socket.Connected表示最近一次IO操作时的socket状态 // 这里Connected说明是server.accept成功时 if (m_socket.Connected) { needShutdown = true; } } protected override void OnBindSocket() { InitRecvSendAsyncObject(); m_remoteEndPoint = (IPEndPoint)m_socket.RemoteEndPoint; m_localEndPoint = (IPEndPoint)m_socket.LocalEndPoint; needShutdown = true; } private void InitRecvSendAsyncObject() { m_recvAsyncLocker = new object(); m_sendAsyncLocker = new object(); m_recvAsyncEventArgs = new SocketAsyncEventArgs(); m_recvAsyncEventArgs.Completed += OnRecvAsyncCallback; m_sendAsyncEventArgs = new SocketAsyncEventArgs(); m_sendAsyncEventArgs.Completed += OnSendAsyncCallback; } protected override void CloseSocket() { try { // 在调用Accept和Connect之前访问socket.RemoteEndPoint会抛出异常 TraceLog.Trace("TcpSession.CloseSocket remote {0}", m_remoteEndPoint); if (m_socket != null && needShutdown) { needShutdown = false; m_socket.Shutdown(SocketShutdown.Both); } } catch (SocketException ex) { TraceLog.Trace("TcpSession.CloseSocket SocketException errcode {0} msg {1}" , ex.ErrorCode, ex.Message); } catch (Exception ex) { TraceLog.Exception(ex); } finally { if(m_socket != null) { m_socket.Close(); m_socket = null; } } } public override void CheckSocketAndSendKeepAlive() { if (m_socket == null) { return; } //发送0字节是没有效果的,这里发一个空包,这个功能增大网络流量,可以将时间设置大点 //需要客户端兼容 TimeSpan span = DateTime.Now - LastDataTime; if(span.TotalSeconds > 10) { // LastSendDataTimeSecond = AppTime.GetNowSysSecond(); LastDataTime = DateTime.Now; try { if (m_socket.Poll(0, SelectMode.SelectWrite)) { m_noWriteableCheckCount = 0; //有数据不能发送,会破坏结构 if (m_sendDataLeft == 0) { m_socket.Send(m_zeroLengthBytes, 0, m_zeroLengthBytes.Length, SocketFlags.None); } } // else // { // CheckNoWriteableSocket(); // } } catch (SocketException ex) { TraceLog.Error("TcpSession.CheckSocketAndSendKeepAlive remote {0} SocketException err {1}" , m_remoteEndPoint, ex.SocketErrorCode); if (ex.SocketErrorCode != SocketError.WouldBlock) { Close(); } } catch (Exception ex) { TraceLog.Error("TcpSession.CheckSocketAndSendKeepAlive remote {0} SocketException message {1}" , m_remoteEndPoint, ex.Message); Close(); } } } private void CheckNoWriteableSocket() { if (m_socket == null) { return; } // 通过send来判断socket是否还有效,万一成功时m_sendDataLeft!=0,就把消息打断了 // 改为count++; 后续只要socket有一次成功的recv/send就把计数清0 m_noWriteableCheckCount++; TraceLog.Trace("TcpSession.CheckNoWriteableSocket socket no writeable, remote {0}, noWriteableCheckCount {1}" , m_remoteEndPoint, m_noWriteableCheckCount); // LastSendDataTimeSecond = AppTime.GetNowSysSecond(); //如果连续多个5秒socket不可写,可以认为是断线了 if (m_noWriteableCheckCount >= 5) { TraceLog.Error("TcpSession.CheckNoWriteableSocket socket no writeable, need close it remote {0}, noWriteableCheckCount {1} to much" , m_remoteEndPoint, m_noWriteableCheckCount); Close(); } } public override void RecvReadableSocket() { if(m_socket == null) { return; } try { int iRecvLen = m_socket.Receive(m_recvBuffer, m_alreadyRecvLength, m_recvBuffer.Length - m_alreadyRecvLength,SocketFlags.None); if(iRecvLen > 0) { TotalRecvLength += iRecvLen; // LastRecvDataTimeSecond = AppTime.GetNowSysSecond(); m_alreadyRecvLength += iRecvLen; if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.RecvReadableSocket sessionid {0} try recv from remote {1} to local {4},realy recv length {2} left length {3} " , SessionID, m_remoteEndPoint, iRecvLen, m_alreadyRecvLength, m_localEndPoint); } TryReadMessageFromBuffer(false,false); } } catch(SocketException socketex) { TraceLog.Debug("TcpSession.RecvReadableSocket socket session {0} receive error HResult {1} message {2}" , SessionID, socketex.HResult, socketex.Message); Close(); } catch(Exception ex) { TraceLog.Error("TcpSession.RecvReadableSocket socket session {0} receive unkonw error, message {1}" , SessionID, ex.Message); Close(); } } public override void WriteWriteableSocket() { if (m_socket == null) { return; } m_noWriteableCheckCount = 0; try { if(m_sendDataLeft == 0) { CopySendBuffer(); } if(m_sendDataLeft > 0) { int requestSendLength = m_sendDataLeft; int iSendBytes = m_socket.Send(m_sendBuffer, m_sendDataPos, requestSendLength, SocketFlags.None); if(iSendBytes > 0) { TotalSendLength += iSendBytes; // LastSendDataTimeSecond = AppTime.GetNowSysSecond(); m_sendDataLeft -= iSendBytes; m_sendDataPos += iSendBytes; if(m_sendDataLeft == 0) { m_sendDataPos = 0; } if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.WriteWriteableSocket sessionid {5} try send from local : {6} to remote :{0} length {1},realy send length {2} left length {3} dataLengthInSendQueue {4}" , m_remoteEndPoint, requestSendLength, iSendBytes, m_sendDataLeft, m_dataLengthInSendQueue, SessionID, m_localEndPoint); } } else { if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.WriteWriteableSocket send no data length 0 from local : {6} to remote {0} request length {1} send length {2} left length {3}" , m_remoteEndPoint, requestSendLength, iSendBytes, m_sendDataLeft, m_localEndPoint); } } } else { if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.WriteWriteableSocket send from local : {1} to remote {0} no data", m_remoteEndPoint, m_localEndPoint); } } } catch(SocketException ex) { // 正常 if (ex.SocketErrorCode == SocketError.WouldBlock) { TraceLog.Trace("TcpSession.WriteWriteableSocket session {0} send erro, errorcode WouldBlock" , SessionID); } else { TraceLog.Error("TcpSession.WriteWriteableSocket session {0} send erro, errorcode {1} message {2}" , SessionID, ex.SocketErrorCode, ex.Message); Close(); } } catch (Exception ex) { TraceLog.Error("TcpSession.WriteWriteableSocket socket session {0} send erro, message {1}", SessionID, ex.Message); Close(); } } public override void StartSendAsync() { if (m_socket == null) { return; } TraceLog.Trace("TcpSession.StartSendAsync socket session {0}", SessionID); lock (m_sendAsyncLocker) { if (m_sendAsyncPending) { return; } m_sendAsyncPending = true; } DoSendAsync(); } private void DoSendAsync() { try { if (m_sendDataLeft == 0) { CopySendBuffer(); } if (m_sendDataLeft > 0) { int requestSendLength = m_sendDataLeft; TraceLog.Trace("TcpSession.DoSendAsync session {0} remote {1} requestSendLength {2}" , SessionID, m_remoteEndPoint, requestSendLength); m_sendAsyncEventArgs.SetBuffer(m_sendBuffer, m_sendDataPos, requestSendLength); bool pending = m_socket.SendAsync(m_sendAsyncEventArgs); if (!pending) { OnSendAsyncCallback(null, m_sendAsyncEventArgs); } }// keep alive else if((DateTime.Now - LastDataTime).TotalSeconds >= 60) { TraceLog.Trace("TcpSession.DoSendAsync socket session {0} keep alive, send empty message", SessionID); // LastSendDataTimeSecond = AppTime.GetNowSysSecond(); m_sendAsyncEventArgs.SetBuffer(m_zeroLengthBytes, 0, m_zeroLengthBytes.Length); bool pending = m_socket.SendAsync(m_sendAsyncEventArgs); if (!pending) { OnSendAsyncCallback(null, m_sendAsyncEventArgs); } if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.DoSendAsync send remote {0} no data", m_remoteEndPoint); } } else { lock (m_sendAsyncLocker) { m_sendAsyncPending = false; } if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.DoSendAsync send remote {0} no data" , m_remoteEndPoint); } } } catch (SocketException ex) { TraceLog.Error("TcpSession.DoSendAsync socket session {0} send erro, errorcode {1} message {2}" , SessionID, ex.SocketErrorCode, ex.Message); Close(); lock (m_sendAsyncLocker) { m_sendAsyncPending = false; } } catch (Exception ex) { TraceLog.Error("TcpSession.DoSendAsync socket session {0} send erro, message {1}", SessionID, ex.Message); Close(); lock (m_sendAsyncLocker) { m_sendAsyncPending = false; } } } private void OnSendAsyncCallback(object sender, SocketAsyncEventArgs args) { int iSendBytes = args.BytesTransferred; // TraceLog.Trace("TcpSession.OnSendAsyncCallback sessionid {0} send length {1} " // , SessionID, iSendBytes); if (args.SocketError != SocketError.Success || args.BytesTransferred <= 0) { TraceLog.Trace("TcpSession.OnSendAsyncCallback session {0} receive error {1}" , SessionID, args.SocketError); Close(); lock (m_sendAsyncLocker) { m_sendAsyncPending = false; } return; } m_noWriteableCheckCount = 0; // LastSendDataTimeSecond = AppTime.GetNowSysSecond(); if(args.Buffer == m_sendBuffer) { TotalSendLength += iSendBytes; m_sendDataLeft -= iSendBytes; m_sendDataPos += iSendBytes; if (m_sendDataLeft == 0) { m_sendDataPos = 0; } } else if(args.Buffer == m_zeroLengthBytes) { //keep alive } if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.OnSendAsyncCallback sessionid {5} try send remote {0} length {1},realy send length {2} left length {3} dataLengthInSendQueue {4}" , m_remoteEndPoint, iSendBytes, iSendBytes, m_sendDataLeft, m_dataLengthInSendQueue, SessionID); } DoSendAsync(); } public override void StartRecvAsync() { if (m_socket == null) { return; } TraceLog.Trace("TcpSession.StartRecvAsync socket session {0}" , SessionID); lock (m_recvAsyncLocker) { if (m_recvAsyncPending) { return; } m_recvAsyncPending = true; } DoRecvAsync(); } private void DoRecvAsync() { try { TraceLog.Trace("TcpSession.DoRecvAsync socket session {0} m_alreadyRecvLength {1}", SessionID, m_alreadyRecvLength); m_recvAsyncEventArgs.SetBuffer(m_recvBuffer, m_alreadyRecvLength, m_recvBuffer.Length - m_alreadyRecvLength); bool pending = m_socket.ReceiveAsync(m_recvAsyncEventArgs); if (!pending) { OnRecvAsyncCallback(null, m_recvAsyncEventArgs); } } catch (SocketException socketex) { TraceLog.Trace("TcpSession.DoRecvAsync socket session {0} receive error HResult {1} message {2}" , SessionID, socketex.HResult, socketex.Message); Close(); lock (m_recvAsyncLocker) { m_recvAsyncPending = false; } } catch (Exception ex) { TraceLog.Error("TcpSession.DoRecvAsync socket session {0} receive unkonw error, message {1}" , SessionID, ex.Message); Close(); lock (m_recvAsyncLocker) { m_recvAsyncPending = false; } } } private void OnRecvAsyncCallback(object sender, SocketAsyncEventArgs args) { int iRecvLen = args.BytesTransferred; if (args.SocketError != SocketError.Success || args.BytesTransferred <= 0) { TraceLog.Trace("TcpSession.OnRecvAsyncCallback socket session {0} SocketError {1}" , SessionID, args.SocketError); Close(); lock (m_recvAsyncLocker) { m_recvAsyncPending = false; } return; } TotalRecvLength += iRecvLen; // LastRecvDataTimeSecond = AppTime.GetNowSysSecond(); m_alreadyRecvLength += iRecvLen; if (WriteSendRecvLog) { TraceLog.Trace("TcpSession.OnRecvAsyncCallback sessionid {0} try recv remote {1},realy recv length {2} m_alreadyRecvLength {3} " , SessionID, m_remoteEndPoint, iRecvLen, m_alreadyRecvLength); } TryReadMessageFromBuffer(false,true); //继续接收消息 DoRecvAsync(); } } }