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.
525 lines
15 KiB
525 lines
15 KiB
1 month ago
|
/*
|
||
|
Sog 游戏基础库
|
||
|
2016 by zouwei
|
||
|
*/
|
||
|
|
||
|
|
||
|
using System;
|
||
|
using System.Net;
|
||
|
using System.Net.Sockets;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Security.Cryptography;
|
||
|
|
||
|
namespace SogClient
|
||
|
{
|
||
|
public enum SessionNetSelectMode
|
||
|
{
|
||
|
Unknow = 0,
|
||
|
|
||
|
//同步select
|
||
|
Synchronous = 1,
|
||
|
|
||
|
// 异步模式
|
||
|
Asynchronous = 2
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Client socket.
|
||
|
/// </summary>
|
||
|
public abstract class ClientSession : IMessageTrans, IDisposable
|
||
|
{
|
||
|
public abstract void Connect();
|
||
|
|
||
|
public abstract void Reconnect();
|
||
|
|
||
|
public abstract void Update(long nowMs);
|
||
|
|
||
|
protected abstract void CloseSession();
|
||
|
|
||
|
|
||
|
public NetSession NetSessionObj { get; protected set; }
|
||
|
protected Socket m_socketClient;
|
||
|
protected ClientSessionSettings m_clientSettings;
|
||
|
|
||
|
protected bool m_isConnected;
|
||
|
protected bool m_isConnecting;
|
||
|
|
||
|
//windows7 上这个效率很低,执行一次
|
||
|
public static object m_rsaCrypto;
|
||
|
|
||
|
SogFastXTEAKey m_teaKey;
|
||
|
|
||
|
private ushort m_sendSeq;
|
||
|
|
||
|
/// <summary>
|
||
|
/// connected.
|
||
|
/// </summary>
|
||
|
public bool IsConnected
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (m_isConnected == false)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//如果加密需要收到key
|
||
|
if (m_clientSettings.CryptoMessage && m_teaKey == null)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
///
|
||
|
/// </summary>
|
||
|
public ClientSessionSettings Settings { get { return m_clientSettings; } }
|
||
|
|
||
|
/// <summary>
|
||
|
///
|
||
|
/// </summary>
|
||
|
public EndPoint LocalEndPoint
|
||
|
{
|
||
|
get { return m_socketClient.LocalEndPoint; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 接收到数据事件
|
||
|
/// </summary>
|
||
|
public event NetEventHandler DataReceived;
|
||
|
protected void OnDataReceived(SessionEventArgs e)
|
||
|
{
|
||
|
TraceLog.Trace("OnDataReceived clientsession recv data from remote {0} to local {3} SessionID {1} length {2}",
|
||
|
e.Session.RemoteEndPoint,
|
||
|
e.Session.SessionID,
|
||
|
e.Message.Header.Length,
|
||
|
e.Session.LocalEndPoint);
|
||
|
|
||
|
if(m_clientSettings.CryptoMessage)
|
||
|
{
|
||
|
if(e.Message.Header.Type == (int)SpecialMessageType.SessionKey)
|
||
|
{
|
||
|
OnSessionKeyReceive(e.Message);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (DataReceived != null)
|
||
|
{
|
||
|
DataReceived(this, e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// 连接成功事件
|
||
|
/// </summary>
|
||
|
public event NetEventHandler Connected;
|
||
|
protected void OnConnected(SessionEventArgs e)
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.OnConnected {0} success", m_clientSettings.RemoteEndPoint);
|
||
|
|
||
|
//如果需要加密在收到key消息后connected
|
||
|
if (m_clientSettings.CryptoMessage)
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.OnConnected need cryptomessage, so wait sessionkey message , m_clientSettings.RemoteEndPoint{0}", m_clientSettings.RemoteEndPoint);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Connected != null)
|
||
|
{
|
||
|
Connected(this, e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 连接失败事件
|
||
|
/// </summary>
|
||
|
public event NetEventHandler ConnectFail;
|
||
|
protected void OnConnectFail(SessionEventArgs e)
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession OnConnectFail clientsession connect {0} failed", m_clientSettings.RemoteEndPoint);
|
||
|
|
||
|
m_sendSeq = 0;
|
||
|
|
||
|
m_isConnected = false;
|
||
|
m_isConnecting = false;
|
||
|
|
||
|
if (ConnectFail != null)
|
||
|
{
|
||
|
ConnectFail(this, e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 连接断开事件
|
||
|
/// </summary>
|
||
|
public event NetEventHandler Disconnected;
|
||
|
protected void OnDisconnected(SessionEventArgs e)
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.OnDisconnected clientsession connection lost {0} ", m_clientSettings.RemoteEndPoint);
|
||
|
|
||
|
if (Disconnected != null)
|
||
|
{
|
||
|
Disconnected(this, e);
|
||
|
}
|
||
|
|
||
|
Close();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
///
|
||
|
/// </summary>
|
||
|
/// <param name="settings"></param>
|
||
|
/// <param name="requestHandler"></param>
|
||
|
public ClientSession(ClientSessionSettings settings)
|
||
|
{
|
||
|
m_clientSettings = settings;
|
||
|
}
|
||
|
|
||
|
public void Close()
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.Close remote {0}", m_clientSettings.RemoteEndPoint);
|
||
|
|
||
|
//清空密钥,重新连接就需要重新请求密钥
|
||
|
m_teaKey = null;
|
||
|
m_sendSeq = 0;
|
||
|
m_isConnected = false;
|
||
|
m_isConnecting = false;
|
||
|
|
||
|
CloseSession();
|
||
|
}
|
||
|
|
||
|
|
||
|
public void OnUpdateConnectedSocketError()
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.OnUpdateConnectedSocketError close session");
|
||
|
|
||
|
SessionEventArgs e = new SessionEventArgs();
|
||
|
e.Session = NetSessionObj;
|
||
|
OnDisconnected(e);
|
||
|
|
||
|
Close();
|
||
|
}
|
||
|
|
||
|
public void ReadSocketReadable()
|
||
|
{
|
||
|
|
||
|
NetSessionObj.RecvReadableSocket();
|
||
|
|
||
|
var dataList = NetSessionObj.GetReceivedDataQueue();
|
||
|
foreach (MessageData message in dataList)
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.ReadSocketReadable ");
|
||
|
SessionEventArgs e = new SessionEventArgs();
|
||
|
e.Message = message;
|
||
|
e.Session = NetSessionObj;
|
||
|
|
||
|
//加密的消息需要解密
|
||
|
if (m_clientSettings.CryptoMessage && m_teaKey != null && message.Data != null && message.Data.Length > 0)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
SogFastXTEA.Decrypt(message.Data, m_teaKey);
|
||
|
}
|
||
|
catch (Exception ex)
|
||
|
{
|
||
|
//解密失败
|
||
|
TraceLog.Exception(ex);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
OnDataReceived(e);
|
||
|
}
|
||
|
|
||
|
dataList.Clear();
|
||
|
|
||
|
//说明接受数据出错了
|
||
|
if (NetSessionObj.IsSocketClosed)
|
||
|
{
|
||
|
OnUpdateConnectedSocketError();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void ConnectSucc()
|
||
|
{
|
||
|
m_isConnected = true;
|
||
|
m_isConnecting = false;
|
||
|
|
||
|
NetSessionObj.Bind(m_socketClient);
|
||
|
NetSessionObj.LastDataTime = DateTime.Now;
|
||
|
NetSessionObj.WriteSendRecvLog = true;
|
||
|
|
||
|
if(m_clientSettings.CryptoMessage)
|
||
|
{
|
||
|
SendPublicKeyToRemote();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private byte[] GenSessionkeyTest(Random rand)
|
||
|
{
|
||
|
byte[] sessionkey = new byte[16];
|
||
|
|
||
|
byte[] val = new byte[64];
|
||
|
|
||
|
// generate data of random length
|
||
|
int t1 = 16;
|
||
|
while (t1 == 0)
|
||
|
t1 = (int)(rand.NextDouble() * 65);
|
||
|
|
||
|
bool done = false;
|
||
|
while (!done)
|
||
|
{
|
||
|
for (int i = 0; i < 64; i++)
|
||
|
{
|
||
|
if (i < t1)
|
||
|
{
|
||
|
val[i] = (byte)(rand.NextDouble() * 256);
|
||
|
while (val[i] == 0)
|
||
|
val[i] = (byte)(rand.NextDouble() * 256);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
val[i] = 0;
|
||
|
}
|
||
|
|
||
|
if (val[i] != 0)
|
||
|
done = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (val[0] == 0)
|
||
|
val[0] = (byte)(rand.NextDouble() * 256);
|
||
|
|
||
|
|
||
|
Array.Copy(val, sessionkey, 16);
|
||
|
|
||
|
return sessionkey;
|
||
|
}
|
||
|
|
||
|
private void SendPublicKeyToRemote()
|
||
|
{
|
||
|
|
||
|
RSAParameters rp;
|
||
|
rp.Modulus = null;
|
||
|
rp.Exponent = null;
|
||
|
|
||
|
|
||
|
lock (m_rsaCrypto)
|
||
|
{
|
||
|
//RSABigIntegerUtils.GenRSAKey();
|
||
|
|
||
|
// TraceLog.Trace("clientsession ImportParameters ");
|
||
|
#if false
|
||
|
int iErrorNum = 0;
|
||
|
int i = 0;
|
||
|
byte[] sessionkey = null;
|
||
|
byte[] ensessionkey = null;
|
||
|
byte[] desessionkey = null;
|
||
|
|
||
|
Random rand = new Random();
|
||
|
|
||
|
//test
|
||
|
for(; i<100000; i++)
|
||
|
{
|
||
|
RSABigIntegerUtils.GenRSAKey();
|
||
|
|
||
|
sessionkey = GenSessionkeyTest(rand);
|
||
|
|
||
|
ensessionkey = RSABigIntegerUtils.EncryptBytesPublic(sessionkey);
|
||
|
desessionkey = RSABigIntegerUtils.DecryptBytesPrivite(ensessionkey);
|
||
|
|
||
|
if(sessionkey.Length != desessionkey.Length)
|
||
|
{
|
||
|
iErrorNum++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for(int j = 0; j<sessionkey.Length; j++)
|
||
|
{
|
||
|
if(sessionkey[j] != desessionkey[j])
|
||
|
{
|
||
|
iErrorNum++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(iErrorNum > 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
rp = RSABigIntegerUtils.GetRSAParameters();
|
||
|
|
||
|
BigInteger bi_n = new BigInteger(rp.Modulus);
|
||
|
BigInteger bi_e = new BigInteger(rp.Exponent);
|
||
|
|
||
|
BigInteger biText = new BigInteger(sessionkey);
|
||
|
BigInteger biEnText = biText.modPow(bi_e, bi_n);
|
||
|
|
||
|
|
||
|
byte[] outbytes = biEnText.getBytes();
|
||
|
if (ensessionkey.Length != outbytes.Length)
|
||
|
{
|
||
|
iErrorNum++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (int j = 0; j < outbytes.Length; j++)
|
||
|
{
|
||
|
if (outbytes[j] != ensessionkey[j])
|
||
|
{
|
||
|
iErrorNum++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (iErrorNum > 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Console.WriteLine("test EncryptBytesPublic rsa success index {0} ", i);
|
||
|
}
|
||
|
|
||
|
if(iErrorNum > 0)
|
||
|
{
|
||
|
string strSessionKey = RSABigIntegerUtils.ToHexString(sessionkey);
|
||
|
string newensessionkey = RSABigIntegerUtils.EncryptStringPublic(strSessionKey);
|
||
|
string newdesessionkey = RSABigIntegerUtils.DecryptStringPrivite(newensessionkey);
|
||
|
|
||
|
if(strSessionKey != newdesessionkey)
|
||
|
{
|
||
|
Console.Write("Error");
|
||
|
}
|
||
|
RSABigIntegerUtils.CheckError(sessionkey);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
rp = RSABigIntegerUtils.GetRSAParameters();
|
||
|
|
||
|
//清空一下,保证逻辑
|
||
|
m_teaKey = null;
|
||
|
|
||
|
//string publickey = m_rsaCrypto.ToXmlString(false);
|
||
|
|
||
|
MessageData message = new MessageData();
|
||
|
message.Data = null;
|
||
|
message.Header.Type = (int)SpecialMessageType.PublicKey;
|
||
|
|
||
|
//RSAParameters rp;
|
||
|
lock (m_rsaCrypto)
|
||
|
{
|
||
|
//rp = m_rsaCrypto.ExportParameters(false);
|
||
|
//rp = RSABigIntegerUtils.GenRSAKey();
|
||
|
}
|
||
|
|
||
|
byte[] data = new byte[2 + rp.Modulus.Length + rp.Exponent.Length];
|
||
|
|
||
|
//string modulus = Convert.ToBase64String(rp.Modulus);
|
||
|
//string exp = Convert.ToBase64String(rp.Exponent);
|
||
|
|
||
|
byte[] modLenByte = BitConverter.GetBytes((Int16)rp.Modulus.Length);
|
||
|
Buffer.BlockCopy(modLenByte, 0, data, 0, 2);
|
||
|
Buffer.BlockCopy(rp.Modulus, 0, data, 2, rp.Modulus.Length);
|
||
|
Buffer.BlockCopy(rp.Exponent, 0, data, 2 + rp.Modulus.Length, rp.Exponent.Length);
|
||
|
message.Data = data;
|
||
|
message.Header.Length = data.Length;
|
||
|
|
||
|
// debug
|
||
|
if (NetSessionObj is UdpSession udpsession)
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.SendPublicKeyToRemote {0} conv {1} keylen {2}"
|
||
|
, udpsession.GetKcpName(), udpsession.GetKcpConvId(), data.Length);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceLog.Trace("ClientSession.SendPublicKeyToRemote keylen {0}", data.Length);
|
||
|
}
|
||
|
|
||
|
Send(message);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
private void OnSessionKeyReceive(MessageData message)
|
||
|
{
|
||
|
if (m_rsaCrypto == null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
byte[] keyBytes;
|
||
|
lock (m_rsaCrypto)
|
||
|
{
|
||
|
keyBytes = RSABigIntegerUtils.DecryptBytesPrivite(message.Data);
|
||
|
}
|
||
|
if (keyBytes.Length != 16)
|
||
|
{
|
||
|
var aa = (UdpClientSession)this;
|
||
|
var cc = aa.udpSession.GetKcpConvId();
|
||
|
TraceLog.Error("keyBytes.Length != 16 {0} {1}", aa.udpSession.GetKcpName(), cc);
|
||
|
}
|
||
|
|
||
|
m_teaKey = new SogFastXTEAKey(keyBytes);
|
||
|
|
||
|
TraceLog.Trace("ClientSession.OnSessionKeyReceive decrypt session success ");
|
||
|
|
||
|
SessionEventArgs e = new SessionEventArgs();
|
||
|
e.Session = NetSessionObj;
|
||
|
OnConnected(e);
|
||
|
}
|
||
|
|
||
|
public void Send(NetSession socket, MessageData message)
|
||
|
{
|
||
|
if(m_teaKey != null && message.Data != null && message.Data.Length > 0)
|
||
|
{
|
||
|
SogFastXTEA.Encrypt(message.Data, m_teaKey);
|
||
|
}
|
||
|
|
||
|
m_sendSeq++;
|
||
|
ushort crcValue = 0;
|
||
|
if (message.Data != null && message.Data.Length > 0)
|
||
|
{
|
||
|
crcValue = CRC.CRC16(message.Data, message.Data.Length);
|
||
|
}
|
||
|
message.Header.ServerID = ((uint)m_sendSeq << 16) + crcValue;
|
||
|
|
||
|
socket.SendMessageToQueue(message);
|
||
|
}
|
||
|
|
||
|
public void Send(MessageData message)
|
||
|
{
|
||
|
if (m_teaKey != null && message.Data != null && message.Data.Length > 0)
|
||
|
{
|
||
|
SogFastXTEA.Encrypt(message.Data, m_teaKey);
|
||
|
}
|
||
|
|
||
|
m_sendSeq++;
|
||
|
ushort crcValue = 0;
|
||
|
if(message.Data != null && message.Data.Length > 0)
|
||
|
{
|
||
|
crcValue = CRC.CRC16(message.Data, message.Data.Length);
|
||
|
}
|
||
|
message.Header.ServerID = ((uint)m_sendSeq << 16) + crcValue;
|
||
|
|
||
|
NetSessionObj.SendMessageToQueue(message);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
///
|
||
|
/// </summary>
|
||
|
public void Dispose()
|
||
|
{
|
||
|
Close();
|
||
|
}
|
||
|
}
|
||
|
}
|