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
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;
|
|
}
|
|
}
|
|
}
|