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.
 
 
 
 
 
 

325 lines
9.9 KiB

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