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.
919 lines
35 KiB
919 lines
35 KiB
/*
|
|
Sog 游戏基础库
|
|
2016 by zouwei
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Sog;
|
|
using Sog.Crypto;
|
|
using Sog.Gate;
|
|
using Sog.IO;
|
|
using Sog.Log;
|
|
|
|
namespace Gate
|
|
{
|
|
public class GateClientService : BaseReloadableService
|
|
{
|
|
private ServerApp m_app;
|
|
|
|
ClientServiceData m_data;
|
|
|
|
private object m_clientDictLocker = new object();
|
|
private object m_needCloseClientsLocker = new object();
|
|
|
|
private long m_lastNotifyBackEndClientCountTimeMs = 0;
|
|
|
|
private int ZipClientMsgSize;
|
|
|
|
//接口实现
|
|
public override int GetServiceType()
|
|
{
|
|
return GateServiceType.GateClientService;
|
|
}
|
|
|
|
//接口实现
|
|
public override void Dispose()
|
|
{
|
|
//反注册消息处理,必须做这一步,否则内存泄漏,对象无法引用各种错误
|
|
m_data.m_socketListener.DataReceived -= socketListener_DataReceived;
|
|
m_data.m_socketListener.Connected -= socketListener_OnTcpConnectCompleted;
|
|
m_data.m_socketListener.Disconnected -= socketListener_Disconnected;
|
|
|
|
if (m_data.m_udpListener != null)
|
|
{
|
|
m_data.m_udpListener.DataReceived -= socketListener_DataReceived;
|
|
m_data.m_udpListener.Connected -= socketListener_OnUdpConnectCompleted;
|
|
m_data.m_udpListener.Disconnected -= socketListener_Disconnected;
|
|
}
|
|
if (m_data.m_webSessionListener != null)
|
|
{
|
|
m_data.m_webSessionListener.DataReceived -= socketListener_DataReceived;
|
|
m_data.m_webSessionListener.Connected -= socketListener_OnWebSocketConnectCompleted;
|
|
m_data.m_webSessionListener.Disconnected -= socketListener_Disconnected;
|
|
}
|
|
|
|
m_app = null;
|
|
m_data = null;
|
|
}
|
|
|
|
public GateClientService(ServerApp app)
|
|
{
|
|
m_app = app;
|
|
|
|
m_data = ServerDataObjMgr.GetDataObj<ClientServiceData>(GateDataObjType.ClientServiceData);
|
|
|
|
InitTCPListener();
|
|
// InitWebSocketListener();
|
|
InitUDPListener();
|
|
}
|
|
|
|
private void InitUDPListener()
|
|
{
|
|
if (m_data.m_udpListener == null)
|
|
{
|
|
m_data.m_socketSetting = new SessionSettings(10, 10, 10, 128000, GateServerUtils.GetServerConfig().listenport +1);
|
|
m_data.m_udpListener = new UdpListener(m_data.m_socketSetting, "GateClientUdpListener");
|
|
m_data.m_udpListener.DataReceived += socketListener_DataReceived;
|
|
m_data.m_udpListener.Connected += socketListener_OnUdpConnectCompleted;
|
|
m_data.m_udpListener.Disconnected += socketListener_Disconnected;
|
|
}
|
|
}
|
|
|
|
private void InitTCPListener()
|
|
{
|
|
if (m_data.m_socketListener == null)
|
|
{
|
|
SessionNetSelectMode nsMode = SessionNetSelectMode.Asynchronous;
|
|
|
|
string clusterNetmode = m_app.GetCluster().GetAppParamByKey("netmode");
|
|
var config = GateServerUtils.GetServerConfig();
|
|
m_app.GetCluster().SetNetMode(config.netMode);
|
|
if (config.netMode == 1)
|
|
{
|
|
nsMode = SessionNetSelectMode.Synchronous;
|
|
}
|
|
if (config.netMode == 0)
|
|
{
|
|
nsMode = clusterNetmode == "1" ?
|
|
SessionNetSelectMode.Synchronous : SessionNetSelectMode.Asynchronous;
|
|
}
|
|
|
|
TraceLog.Debug("GateClientService.GateClientService netmode SessionNetSelectMode is {0}", nsMode);
|
|
|
|
m_data.m_socketSetting = new SessionSettings(10, 10, 10, 128000, GateServerUtils.GetServerConfig().listenport);
|
|
m_data.m_socketListener = new SessionListener(m_app, m_data.m_socketSetting, "GateClientListener", nsMode);
|
|
|
|
if (nsMode == SessionNetSelectMode.Asynchronous)
|
|
{
|
|
TraceLog.Debug("GateClientService.GateClientService config.maxAsyncRecursionCount : {0}", config.maxAsyncRecursionCount);
|
|
m_data.m_socketListener.SetMaxAsyncRecursionCount(config.maxAsyncRecursionCount);
|
|
}
|
|
|
|
}
|
|
//注册消息处理
|
|
m_data.m_socketListener.DataReceived += socketListener_DataReceived;
|
|
m_data.m_socketListener.Connected += socketListener_OnTcpConnectCompleted;
|
|
m_data.m_socketListener.Disconnected += socketListener_Disconnected;
|
|
}
|
|
|
|
private void InitWebSocketListener()
|
|
{
|
|
TraceLog.Debug("GateClientService.InitWebSocketListener ");
|
|
|
|
if (m_data.m_webSessionListener == null)
|
|
{
|
|
SessionNetSelectMode nsMode = SessionNetSelectMode.Asynchronous;
|
|
|
|
string clusterNetmode = m_app.GetCluster().GetAppParamByKey("netmode");
|
|
var config = GateServerUtils.GetServerConfig();
|
|
m_app.GetCluster().SetNetMode(config.netMode);
|
|
if (config.netMode == 1)
|
|
{
|
|
nsMode = SessionNetSelectMode.Synchronous;
|
|
}
|
|
if (config.netMode == 0)
|
|
{
|
|
nsMode = clusterNetmode == "1" ?
|
|
SessionNetSelectMode.Synchronous : SessionNetSelectMode.Asynchronous;
|
|
}
|
|
TraceLog.Debug($"GateClientService.InitWebSocketListener {nsMode}");
|
|
var serverConfig = GateServerUtils.GetServerConfig();
|
|
m_data.m_socketSetting = new SessionSettings(10, 10, 10, 128000, serverConfig.websocketport);
|
|
m_data.m_webSessionListener = new WebSessionListener(m_app, m_data.m_socketSetting, "GateWebClientListener", nsMode);
|
|
|
|
if (serverConfig.websocketSecure)
|
|
{
|
|
TraceLog.Debug($"GateClientService.InitWebSocketListener open wss");
|
|
|
|
X509Certificate2 cert = new X509Certificate2(serverConfig.certificatePath, serverConfig.certificatePassword);
|
|
m_data.m_webSessionListener.SetSchemeAndCertificate(true, cert);
|
|
}
|
|
else
|
|
{
|
|
m_data.m_webSessionListener.SetSchemeAndCertificate(false);
|
|
|
|
}
|
|
if (nsMode == SessionNetSelectMode.Asynchronous)
|
|
{
|
|
m_data.m_webSessionListener.SetMaxAsyncRecursionCount(config.maxAsyncRecursionCount);
|
|
}
|
|
|
|
}
|
|
if (m_data.m_webSessionListener != null)
|
|
{
|
|
m_data.m_webSessionListener.DataReceived += socketListener_DataReceived;
|
|
m_data.m_webSessionListener.Connected += socketListener_OnWebSocketConnectCompleted;
|
|
m_data.m_webSessionListener.Disconnected += socketListener_Disconnected;
|
|
}
|
|
}
|
|
|
|
|
|
public void CloseListen()
|
|
{
|
|
m_data = ServerDataObjMgr.GetDataObj<ClientServiceData>(GateDataObjType.ClientServiceData);
|
|
|
|
m_data.m_socketListener.Close();
|
|
m_data.m_udpListener?.Close();
|
|
m_data.m_webSessionListener?.Close();
|
|
}
|
|
|
|
|
|
public void SetZipMsgSize(int size)
|
|
{
|
|
TraceLog.Trace("GateClientService.SetZipMsgSize {0}", size);
|
|
ZipClientMsgSize = size;
|
|
}
|
|
|
|
public void StartListen()
|
|
{
|
|
|
|
m_data.m_lastCheckTimeoutTime = m_app.GetTimeSecond();
|
|
|
|
m_data.m_socketListener?.StartListen(true);
|
|
|
|
//先取消upd
|
|
m_data.m_udpListener?.StartListen();
|
|
// m_data.m_webSessionListener?.StartListen(true);
|
|
}
|
|
|
|
public int GetClientCount()
|
|
{
|
|
return m_data.m_clientsDict.Count;
|
|
}
|
|
|
|
private void socketListener_OnTcpConnectCompleted(object sender, SessionEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
long sessionID = Sog.Service.GateSessionID.GenNextID(m_app.ServerID);
|
|
e.Session.ResetSessionID(sessionID);
|
|
e.Session.SetSocketBufferSize(128000);
|
|
|
|
socketListener_OnConnectCompleted(sender, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("socketListener_OnTcpConnectCompleted error:{0}", ex);
|
|
}
|
|
}
|
|
|
|
private void socketListener_OnUdpConnectCompleted(object sender, SessionEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
long sessionID = Sog.Service.GateSessionID.GenNextID(m_app.ServerID);
|
|
e.Session.ResetSessionID(sessionID);
|
|
socketListener_OnConnectCompleted(sender, e);
|
|
|
|
// debug
|
|
if (e.Session is UdpSession udpSession)
|
|
{
|
|
TraceLog.Trace("socketListener_OnUdpConnectCompleted conv {0} session {1}"
|
|
, udpSession.GetKcpConvId(), udpSession.SessionID);
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Error("socketListener_OnUdpConnectCompleted session {0} not UdpSession"
|
|
, e.Session.SessionID);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("socketListener_OnUdpConnectCompleted error:{0}", ex);
|
|
}
|
|
}
|
|
|
|
private void socketListener_OnWebSocketConnectCompleted(object sender, SessionEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
long sessionID = Sog.Service.GateSessionID.GenNextID(m_app.ServerID);
|
|
e.Session.ResetSessionID(sessionID);
|
|
// debug
|
|
if (e.Session is WebSession webSession)
|
|
{
|
|
TraceLog.Trace("socketListener_OnWebSocketConnectCompleted session {0}"
|
|
, webSession.SessionID);
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Error("socketListener_OnTcpConnectCompleted session {0} not UdpSession"
|
|
, e.Session.SessionID);
|
|
}
|
|
socketListener_OnConnectCompleted(sender, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("socketListener_OnWebSocketConnectCompleted error:{0}", ex);
|
|
}
|
|
}
|
|
|
|
private void socketListener_Disconnected(object sender, SessionEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
long sessionID = e.Session.SessionID;
|
|
TraceLog.Trace("socketListener_Disconnected session {0} disconneted ,notify to server", sessionID);
|
|
AddToNeedCloseClient(e.Session.SessionID);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
TraceLog.Error("socketListener_Disconnected Disconnected error:{0}", err);
|
|
}
|
|
}
|
|
|
|
private void socketListener_OnConnectCompleted(object sender, SessionEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
long sessionID = e.Session.SessionID;
|
|
|
|
e.Session.WriteSendRecvLog = true;
|
|
|
|
GateClientInfo info = new GateClientInfo(m_app.GetTimeSecond(), e.Session);
|
|
//wait server ack
|
|
info.State = GameClientState.WaitServerAck;
|
|
|
|
lock (m_clientDictLocker)
|
|
{
|
|
m_data.m_clientsDict.Add(sessionID, info);
|
|
|
|
//加入等待队列
|
|
m_data.m_waitServerAckDict.Add(sessionID, info);
|
|
m_data.m_waitClientPublicKeyDict.Add(sessionID, info);
|
|
}
|
|
|
|
string ip = "0.0.0.0";
|
|
if(e.Session.RemoteEndPoint != null)
|
|
{
|
|
|
|
try
|
|
{
|
|
ip = e.Session.RemoteEndPoint.ToString().Split(':')[0];
|
|
}
|
|
catch(Exception)
|
|
{
|
|
}
|
|
}
|
|
if(e.Session is WebSession)
|
|
{
|
|
ip = ((WebSession)(e.Session)).clientIp;
|
|
}
|
|
|
|
TraceLog.Trace("socketListener_OnConnectCompleted session {0} ip {1} conneted, notify to server", sessionID, ip);
|
|
|
|
GateClientConnectedReq req = new GateClientConnectedReq();
|
|
req.IPAddress = ip;
|
|
GateServerUtils.GetServerMsgTrans().NotifyServerClientConnected(info, req);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
TraceLog.Error("socketListener_OnConnectCompleted ConnectCompleted error:{0}", err);
|
|
}
|
|
}
|
|
|
|
private void socketListener_DataReceived(object sender, SessionEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
long sessionID = e.Session.SessionID;
|
|
int messageID = e.Message.Header.Type;
|
|
GateServerConfig configInfo = GateServerUtils.GetServerConfig();
|
|
GateClientInfo info = GetGateClientInfoThreadSafe(sessionID);
|
|
if (info == null)
|
|
{
|
|
TraceLog.Trace("socketListener_DataReceived session {0} no GateClientInfo", sessionID);
|
|
return;
|
|
}
|
|
|
|
if (configInfo.checkSeq)
|
|
{
|
|
ushort recvSeq = (ushort)(e.Message.Header.ServerID >> 16);
|
|
//检查seq
|
|
if (recvSeq != info.RecvSeq + 1)
|
|
{
|
|
TraceLog.Error("socketListener_DataReceived session {0}, seq {1} invalid last recv seq {2}"
|
|
, sessionID, recvSeq, info.RecvSeq);
|
|
|
|
AddToNeedCloseClient(info.SessionID);
|
|
return;
|
|
}
|
|
|
|
info.RecvSeq++;
|
|
|
|
ushort recvCrc = (ushort)(e.Message.Header.ServerID & 0xFFFF);
|
|
ushort crcValue = 0;
|
|
if (e.Message.Buffer.Data != null && e.Message.Buffer.Length > 0)
|
|
{
|
|
crcValue = CRC.CRC16(e.Message.Buffer.Data, e.Message.Buffer.Length);
|
|
}
|
|
|
|
if(recvCrc != crcValue)
|
|
{
|
|
TraceLog.Error("socketListener_DataReceived session {0}, calc crc {1} != recv crc {2}"
|
|
, sessionID, crcValue, recvCrc);
|
|
|
|
AddToNeedCloseClient(info.SessionID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//检查流量
|
|
if (configInfo.checkFlow)
|
|
{
|
|
GateServerUtils.OnAddOneMessage(info, configInfo, messageID, 1, e.Message.Header.Length);
|
|
|
|
if (info.FlowCheckInfo.aboveLimitTimes >= configInfo.maxLimitTimes)
|
|
{
|
|
if (configInfo.messageIgnoreLimitList == null ||
|
|
!configInfo.messageIgnoreLimitList.Contains(messageID))
|
|
{
|
|
TraceLog.Error("socketListener_DataReceived session:{0}, flow limit:{1}__{2} messageId={3}"
|
|
, sessionID, info.FlowCheckInfo.aboveLimitTimes, configInfo.maxLimitTimes,messageID);
|
|
AddToNeedCloseClient(info.SessionID);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (messageID == (int)SpecialMessageType.PublicKey)
|
|
{
|
|
TraceLog.TraceDetail("socketListener_DataReceived session {0} recv data len {1}, Type PublicKey"
|
|
, sessionID, e.Message.Header.Length);
|
|
|
|
OnRecvClientPublicKey(info, sessionID, e.Message);
|
|
}
|
|
else
|
|
{
|
|
TraceLog.TraceDetail("socketListener_DataReceived session {0} recv data len {1}, msg {2} trans to server"
|
|
, sessionID, e.Message.Header.Length, messageID);
|
|
|
|
//需要解密,空消息不解密
|
|
if(info.TeaKey != null && e.Message.Buffer.Data != null && e.Message.Buffer.Length > 0)
|
|
{
|
|
//解密有可能失败,需要try cache,要不然会丢很多消息
|
|
try
|
|
{
|
|
SogFastXTEA.Decrypt(e.Message.Buffer.Data, e.Message.Buffer.Length, info.TeaKey);
|
|
|
|
GateServerUtils.GetServerMsgTrans().TransClientMessageToServer(info, e.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("socketListener_DataReceived decrypt msg data failed! session {0} MsgType {1}"
|
|
, sessionID, messageID);
|
|
TraceLog.Exception(ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GateServerUtils.GetServerMsgTrans().TransClientMessageToServer(info, e.Message);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("GateClientService.socketListener_DataReceived recv to Host:{0} error:{1}", e.Session.RemoteEndPoint, ex);
|
|
}
|
|
finally
|
|
{
|
|
e.Message.FreeData();
|
|
}
|
|
}
|
|
|
|
public GateClientInfo GetGateClientInfoThreadSafe(long sessionID)
|
|
{
|
|
GateClientInfo info;
|
|
lock (m_clientDictLocker)
|
|
{
|
|
m_data.m_clientsDict.TryGetValue(sessionID, out info);
|
|
}
|
|
|
|
if (info == null)
|
|
{
|
|
TraceLog.Trace("GateClientService.GetGateClientInfoThreadSafe session {0} no GateClientInfo", sessionID);
|
|
return null;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
public void Update(long nowMs)
|
|
{
|
|
int maxClient = GateServerUtils.GetServerConfig().maxClientCount;
|
|
bool isLimit = maxClient > 0 && m_data.m_clientsDict.Count >= maxClient;
|
|
if (m_data.m_socketListener != null
|
|
&& m_data.m_socketListener.AcceptLimited != isLimit)
|
|
{
|
|
m_data.m_socketListener.AcceptLimited = isLimit;
|
|
}
|
|
if (m_data.m_webSessionListener != null
|
|
&& m_data.m_webSessionListener.AcceptLimited != isLimit)
|
|
{
|
|
m_data.m_webSessionListener.AcceptLimited = isLimit;
|
|
}
|
|
|
|
m_data.m_socketListener?.UpdateAccept();
|
|
m_data.m_socketListener?.UpdateAcceptedSessions(nowMs);
|
|
|
|
m_data.m_webSessionListener?.UpdateAccept();
|
|
m_data.m_webSessionListener?.UpdateAcceptedSessions(nowMs);
|
|
|
|
m_data.m_udpListener?.Update(nowMs);
|
|
|
|
CheckTimeoutClient();
|
|
CloseNeedCloseClients();
|
|
|
|
UpdateNotifyBackEndClientCount(nowMs);
|
|
int clientSocketCount = m_data.m_socketListener.GetClientSocketCount();
|
|
if (m_data.m_webSessionListener != null)
|
|
{
|
|
clientSocketCount += m_data.m_webSessionListener.GetClientSocketCount();
|
|
}
|
|
|
|
ServerStat.Instance.SetValue("SocketSessionMapCount", clientSocketCount);
|
|
}
|
|
|
|
private void CheckTimeoutClient()
|
|
{
|
|
long now = m_app.GetTimeSecond();
|
|
|
|
// 3秒检查一次,10超时
|
|
if(now - m_data.m_lastCheckTimeoutTime < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_data.m_lastCheckTimeoutTime = now;
|
|
|
|
List<GateClientInfo> infoList;
|
|
lock (m_clientDictLocker)
|
|
{
|
|
infoList = m_data.m_waitServerAckDict.Values.ToList();
|
|
}
|
|
|
|
foreach (var info in infoList)
|
|
{
|
|
if (now - info.ConnectedTime > 30)
|
|
{
|
|
TraceLog.Error("GateClientService.CheckTimeoutClient session {0} wait server ack connet timeout", info.SessionID);
|
|
|
|
AddToNeedCloseClient(info.SessionID);
|
|
}
|
|
}
|
|
|
|
|
|
//等待公钥校验
|
|
lock (m_clientDictLocker)
|
|
{
|
|
infoList = m_data.m_waitClientPublicKeyDict.Values.ToList();
|
|
}
|
|
|
|
foreach (var info in infoList)
|
|
{
|
|
if (now - info.ConnectedTime > 30)
|
|
{
|
|
TraceLog.Error("GateClientService.CheckTimeoutClient session {0} wait client public key timeout", info.SessionID);
|
|
|
|
AddToNeedCloseClient(info.SessionID);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void CloseNeedCloseClients()
|
|
{
|
|
if (m_data.m_needCloseClients.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
List<long> closeSessionID;
|
|
lock (m_needCloseClientsLocker)
|
|
{
|
|
closeSessionID = m_data.m_needCloseClients.ToList();
|
|
m_data.m_needCloseClients.Clear();
|
|
}
|
|
|
|
foreach (long sessionID in closeSessionID)
|
|
{
|
|
lock (m_clientDictLocker)
|
|
{
|
|
m_data.m_waitServerAckDict.Remove(sessionID);
|
|
m_data.m_waitClientPublicKeyDict.Remove(sessionID);
|
|
}
|
|
|
|
GateClientInfo info = GetGateClientInfoThreadSafe(sessionID);
|
|
if (info != null)
|
|
{
|
|
// 通知后端服务器客户端断线了
|
|
GateServerUtils.GetServerMsgTrans().NotifyServerClientDisconnected(info.SessionID, info.LinkServerID);
|
|
|
|
if (info.Session is WebSession)
|
|
{
|
|
if (info.Session.WorkSocket != null)
|
|
{
|
|
m_data.m_webSessionListener.ForceCloseNetSession(info.Session);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (info.Session.WorkSocket != null)
|
|
{
|
|
m_data.m_socketListener.ForceCloseNetSession(info.Session);
|
|
}
|
|
}
|
|
|
|
lock (m_clientDictLocker)
|
|
{
|
|
m_data.m_clientsDict.Remove(sessionID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnServerAckConnected(uint remoteAppID, long sessionID)
|
|
{
|
|
GateClientInfo info;
|
|
|
|
lock (m_clientDictLocker)
|
|
{
|
|
if (m_data.m_waitServerAckDict.TryGetValue(sessionID, out info))
|
|
{
|
|
m_data.m_waitServerAckDict.Remove(sessionID);
|
|
}
|
|
}
|
|
|
|
if (info == null)
|
|
{
|
|
TraceLog.Trace("GateClientService.OnServerAckConnected server ack session {0} conneted, but client not in waitlist", sessionID);
|
|
}
|
|
|
|
info = GetGateClientInfoThreadSafe(sessionID);
|
|
if (info != null)
|
|
{
|
|
TraceLog.Trace("GateClientService.OnServerAckConnected server {0} ack session {1} conneted ,change state to normal"
|
|
, ServerIDUtils.IDToString(remoteAppID), sessionID);
|
|
|
|
info.LinkServerID = remoteAppID;
|
|
info.State = GameClientState.Noraml;
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Error("GateClientService.OnServerAckConnected server ack session {0} conneted, but client not in m_clientsDict, server login error!"
|
|
, sessionID);
|
|
}
|
|
|
|
}
|
|
|
|
//服务器主动断开连接
|
|
public void OnServerReqDisconnect(uint remoteAppID,long sessionID)
|
|
{
|
|
GateClientInfo info = GetGateClientInfoThreadSafe(sessionID);
|
|
if (info != null)
|
|
{
|
|
TraceLog.Trace("GateClientService.OnServerReqDisconnect server req session {0} disconnect ,add to close list"
|
|
, sessionID);
|
|
|
|
info.State = GameClientState.Closed;
|
|
AddToNeedCloseClient(sessionID);
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Trace("GateClientService.OnServerReqDisconnect server req session {0} disconnect ,but client not in m_clientsDict"
|
|
, sessionID);
|
|
|
|
//不存在了,不回包算了 zouwei 20180604
|
|
//回个包比较好,兼容性更好,比如game运行的时候可以杀gate进程,还有切服兼容 20190214
|
|
GateServerUtils.GetServerMsgTrans().NotifyServerClientDisconnected(sessionID, remoteAppID);
|
|
}
|
|
}
|
|
|
|
public void TransChannelMessageToClient(uint remoteAppID, long sessionID, MessageData channelMessage)
|
|
{
|
|
GateClientInfo info = GetGateClientInfoThreadSafe(sessionID);
|
|
if (info == null)
|
|
{
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToClient can not find session {0}, drop msg"
|
|
, sessionID);
|
|
|
|
//回个包比较好,兼容性更好,比如game运行的时候可以杀gate进程,还有切服兼容 20190214
|
|
GateServerUtils.GetServerMsgTrans().NotifyServerClientDisconnected(sessionID, remoteAppID);
|
|
return;
|
|
}
|
|
|
|
if (info.State != GameClientState.Noraml)
|
|
{
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToClient session {0} stat {1} need Noraml, drop msg"
|
|
, sessionID, info.State);
|
|
return;
|
|
}
|
|
|
|
int headLength = GateMsgHeaderPacker.GetLength();
|
|
|
|
MessageData message = new MessageData();
|
|
message.Header.Type = channelMessage.Header.Type;
|
|
|
|
int iChannelDataLength = channelMessage.Header.Length - headLength;
|
|
if (iChannelDataLength < 0)
|
|
{
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToClient session {0} channelMessage length error"
|
|
, sessionID);
|
|
return;
|
|
}
|
|
|
|
//加个判断,大消息打个日志
|
|
if (iChannelDataLength > 60000)
|
|
{
|
|
TraceLog.Error("GateClientService.TransChannelMessageToClient session {0} channelMessage length {1} too long, msg type {2}"
|
|
, sessionID, iChannelDataLength, message.Header.Type);
|
|
}
|
|
|
|
message.MallocData(iChannelDataLength);
|
|
Buffer.BlockCopy(channelMessage.Buffer.Data, headLength, message.Buffer.Data, 0, message.Buffer.Length);
|
|
|
|
// 消息压缩
|
|
if (ZipClientMsgSize > 0 && message.Buffer.Length > ZipClientMsgSize)
|
|
{
|
|
ZipMessageData(message, info);
|
|
}
|
|
|
|
// 消息加密, 空消息不加密
|
|
if (info.TeaKey != null && iChannelDataLength > 0)
|
|
{
|
|
SogFastXTEA.Encrypt(message.Buffer.Data, message.Buffer.Length, info.TeaKey);
|
|
}
|
|
|
|
message.Header.Length = message.Buffer.Length;
|
|
message.Header.ObjectID = 0; //发给客户端的这个全部设置成空
|
|
info.SendSeq++;
|
|
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToClient session {0} MsgType {1} length {2}",
|
|
sessionID, message.Header.Type, message.Header.Length);
|
|
|
|
SendSessionMessage(info.Session, message);
|
|
}
|
|
|
|
public void ZipMessageData(MessageData message, GateClientInfo info)
|
|
{
|
|
byte[] zipData = ZipUtils.CompressBytes(message.Buffer.Data, message.Buffer.Length);
|
|
if (zipData.Length < message.Buffer.Length)
|
|
{
|
|
TraceLog.Trace("GateClientService.ZipMessageData message {0} old {1} zip {2}"
|
|
, message.Header.Type, message.Buffer.Length, zipData.Length);
|
|
|
|
Buffer.BlockCopy(zipData, 0, message.Buffer.Data, 0, zipData.Length);
|
|
message.Buffer.Length = zipData.Length;
|
|
message.Header.isZip = true;
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Error("GateClientService.ZipMessageData message {0} not zip, old {1} < zip {2}"
|
|
, message.Header.Type, message.Buffer.Length, zipData.Length);
|
|
}
|
|
}
|
|
|
|
public void TransChannelMessageToMultiClient(ref GateMsgMultiSessionHeader mutilsession, MessageData channelMessage)
|
|
{
|
|
//总消息长度
|
|
int headLength = GateMsgHeaderPacker.GetLength() + mutilsession.GetLength();
|
|
|
|
int iChannelDataLength = channelMessage.Header.Length - headLength;
|
|
if (iChannelDataLength < 0)
|
|
{
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToMultiClient trans message {0},channelMessage length error "
|
|
, channelMessage.Header.Type);
|
|
return;
|
|
}
|
|
|
|
//加个判断,免得不知道错误
|
|
if (iChannelDataLength > 60000)
|
|
{
|
|
TraceLog.Error("GateClientService.TransChannelMessageToMultiClient trans message {0} ,channelMessage length {1} too long"
|
|
, channelMessage.Header.Type,iChannelDataLength);
|
|
return;
|
|
}
|
|
|
|
unsafe
|
|
{
|
|
for (int i = 0; i < mutilsession.Count; i++)
|
|
{
|
|
long sessionId = mutilsession.SessionIDs[i];
|
|
GateClientInfo info = GetGateClientInfoThreadSafe(sessionId);
|
|
if (info == null)
|
|
{
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToMultiClient trans session {0} message ,but can not find session"
|
|
, sessionId);
|
|
continue;
|
|
}
|
|
|
|
if (info.State != GameClientState.Noraml)
|
|
{
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToMultiClient trans session {0} message ,but session stat is {1}"
|
|
, sessionId, GameClientState.Noraml);
|
|
continue;
|
|
}
|
|
|
|
info.SendSeq++;
|
|
|
|
MessageData message = new MessageData();
|
|
message.Header.Type = channelMessage.Header.Type;
|
|
message.Header.ObjectID = 0; //发给客户端的这个全部设置成空
|
|
message.MallocData(iChannelDataLength);
|
|
|
|
Buffer.BlockCopy(channelMessage.Buffer.Data, headLength, message.Buffer.Data, 0, message.Buffer.Length);
|
|
|
|
// 广播应用不多, 后续可以把消息压缩改成1次
|
|
if (ZipClientMsgSize > 0 && message.Buffer.Length > ZipClientMsgSize)
|
|
{
|
|
ZipMessageData(message, info);
|
|
}
|
|
|
|
//空消息不加密
|
|
if (info.TeaKey != null && iChannelDataLength > 0)
|
|
{
|
|
SogFastXTEA.Encrypt(message.Buffer.Data, message.Buffer.Length, info.TeaKey);
|
|
}
|
|
|
|
message.Header.Length = message.Buffer.Length;
|
|
|
|
SendSessionMessage(info.Session, message);
|
|
|
|
TraceLog.Trace("GateClientService.TransChannelMessageToMultiClient trans session {0} message ,MsgType {1} length {2}",
|
|
sessionId, message.Header.Type, message.Header.Length);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void AddToNeedCloseClient(long sessionID)
|
|
{
|
|
lock (m_needCloseClientsLocker)
|
|
{
|
|
if (m_data.m_needCloseClients.Contains(sessionID))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_data.m_needCloseClients.Add(sessionID);
|
|
}
|
|
}
|
|
|
|
private void OnRecvClientPublicKey(GateClientInfo info, long sessionID, MessageData message)
|
|
{
|
|
|
|
lock (m_clientDictLocker)
|
|
{
|
|
m_data.m_waitClientPublicKeyDict.Remove(sessionID);
|
|
}
|
|
|
|
if(message.Buffer.Length > 2)
|
|
{
|
|
int modLen = BitConverter.ToInt16(message.Buffer.Data, 0);
|
|
int expLen = message.Buffer.Length - modLen - 2;
|
|
if (modLen < 0 || expLen < 0)
|
|
{
|
|
TraceLog.Debug("GateClientService.OnRecvClientPublicKey session {0} length invalid"
|
|
, sessionID);
|
|
return;
|
|
}
|
|
|
|
byte[] mod = new byte[modLen];
|
|
byte[] exp = new byte[expLen];
|
|
Buffer.BlockCopy(message.Buffer.Data, 2, mod, 0, modLen);
|
|
Buffer.BlockCopy(message.Buffer.Data, 2 + modLen, exp, 0, expLen);
|
|
|
|
byte[] newTeaKey = TeaKeyGenerator.Instance.GenerateNew();
|
|
byte[] encrypteaKey = null;
|
|
|
|
if (expLen == 4)
|
|
{
|
|
//自己特殊rsa算法
|
|
encrypteaKey = CryptoConvertUtils.EncryptWithPublicKeyWithChewKeongTANBigInteger(newTeaKey, mod, exp);
|
|
TraceLog.Debug("GateClientService.OnRecvClientPublicKey EncryptWithPublicKeyWithChewKeongTANBigInteger session {0} keyLen {1}"
|
|
, sessionID, encrypteaKey.Length);
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Debug("GateClientService.OnRecvClientPublicKey EncryptWithPublicKey session {0}"
|
|
, sessionID);
|
|
|
|
TraceLog.Error("GateClientService.OnRecvClientPublicKey EncryptWithPublicKey session {0} no support"
|
|
, sessionID);
|
|
return;
|
|
|
|
//通用rsa算法,和微软的实现一样
|
|
//encrypteaKey = CryptoConvertUtils.EncryptWithPublicKey(newTeaKey, mod, exp);
|
|
}
|
|
|
|
|
|
MessageData sessionKeyMessage = new MessageData();
|
|
sessionKeyMessage.Header.ObjectID = 0;
|
|
sessionKeyMessage.Header.ServerID = 0;
|
|
sessionKeyMessage.Header.Type = (int)SpecialMessageType.SessionKey;
|
|
sessionKeyMessage.Header.Length = encrypteaKey.Length;
|
|
|
|
//拷贝一份数据,目的是所有的MessageData的内存全都走cache,这样统一
|
|
sessionKeyMessage.MallocData(encrypteaKey.Length);
|
|
Buffer.BlockCopy(encrypteaKey, 0, sessionKeyMessage.Buffer.Data, 0, encrypteaKey.Length);
|
|
|
|
SendSessionMessage(info.Session, sessionKeyMessage);
|
|
|
|
info.TeaKey = new SogFastXTEAKey(newTeaKey);
|
|
}
|
|
}
|
|
|
|
private void SendSessionMessage(NetSession session, MessageData message)
|
|
{
|
|
if (session is WebSession)
|
|
{
|
|
m_data.m_webSessionListener.Send(session, message);
|
|
}
|
|
else
|
|
{
|
|
m_data.m_socketListener.Send(session, message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通知后端服务器连接数量
|
|
/// </summary>
|
|
private void UpdateNotifyBackEndClientCount(long nowMs)
|
|
{
|
|
//10秒一个包
|
|
if(nowMs - m_lastNotifyBackEndClientCountTimeMs < 10000)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_lastNotifyBackEndClientCountTimeMs = nowMs;
|
|
|
|
GateServerUtils.GetServerMsgTrans().NotifyServerClientCount(GetClientCount());
|
|
}
|
|
}
|
|
}
|
|
|