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

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