You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

556 lines
18 KiB

/*
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();
}
}
}