NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/Net/NetTcp.cs

598 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//////////////////////////////////////////////////////////////////////////
//
// 文件Assets/Code/Scripts/Framework/Net/Base/NetTcp.cs
// 作者Xoen Xie
// 时间2023/07/01
// 描述TCP连接
// 说明:
//
//////////////////////////////////////////////////////////////////////////
using System;
using System.Net;
using System.Net.Sockets;
namespace Framework
{
public static class NetDebugOptions
{
public static bool BlockUpstream { get; private set; }
public static bool BlockDownstream { get; private set; }
public static void SetBlockUpstream(bool value)
{
if (BlockUpstream == value)
return;
BlockUpstream = value;
DebugUtil.Log($"断上行已{(value ? "" : "")}");
}
public static void SetBlockDownstream(bool value)
{
if (BlockDownstream == value)
return;
BlockDownstream = value;
DebugUtil.Log($"断下行已{(value ? "" : "")}");
}
public static void Reset()
{
SetBlockUpstream(false);
SetBlockDownstream(false);
}
}
public class NetTcp
{
enum NetInfoType
{
None = 0,
Error,
Connected,
Data,
}
struct NetInfo
{
public NetInfoType type;
public byte[] data;
public NetError error;
}
enum NetState
{
None = 0,
Connecting,
Connected,
Close,
Release,
NetStateError,
}
// 客户端接受包最大大小32K
private const ushort MAX_BUFFER_SIZE = 1024 * 32;
private byte[] m_TempBuffer;
private byte[] m_RecvBuffer;
private byte[] m_LengthData;
private byte[] m_IDData;
private int m_nUintSize;
private int m_nBufferLength;
private Socket m_Socket;
private NetState m_nState;
private INetHandler m_Handler;
private NetWriter m_Writer;
private AsyncCallback m_AsyncConnect;
private AsyncCallback m_AsyncReceive;
private AsyncCallback m_AsyncSend;
private NetQueue<NetInfo> m_EventQueue;
public bool mIsConnected { get { return m_nState == NetState.Connected; } }
public RC4Cipher Cipher;
private uint m_nPacketIndex;
public NetTcp()
{
m_nState = NetState.None;
m_TempBuffer = new byte[MAX_BUFFER_SIZE];
m_RecvBuffer = new byte[MAX_BUFFER_SIZE];
m_nUintSize = sizeof(uint);
m_LengthData = new byte[4];
m_IDData = new byte[2];
m_nBufferLength = 0;
m_Writer = new NetWriter();
m_AsyncConnect = new AsyncCallback(OnEndConnect);
m_AsyncReceive = new AsyncCallback(OnEndReceive);
m_AsyncSend = new AsyncCallback(OnEndSend);
}
public bool Create(INetHandler handler)
{
m_Handler = handler;
m_EventQueue = new NetQueue<NetInfo>();
return true;
}
public void Release()
{
m_Handler = null;
Close();
m_nState = NetState.Release;
m_EventQueue = null;
}
public bool Connect(string ip,
int port)
{
if (DebugUtil.LogEnable)
DebugUtil.Log(string.Format("NetTcp.Connect Start Connect, ip={0}, port={1}", ip, port));
if (m_nState == NetState.Connecting || m_nState == NetState.Connected)
{
DebugUtil.LogWarning("NetTcp.Connect igone .... m_nState:{0}", m_nState);
return true;
}
try
{
IPAddress[] address = Dns.GetHostAddresses(ip);
for (int i = 0; i < address.Length; ++i)
{
if (DebugUtil.LogEnable)
{
DebugUtil.Log(string.Format("NetTcp.Connect Address:{0}, AddressFamily:{1}", address[i].ToString(), address[i].AddressFamily));
}
}
m_Socket = new Socket(address[0].AddressFamily, SocketType.Stream, ProtocolType.Tcp);
m_Socket.BeginConnect(address[0], port, m_AsyncConnect, null);
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::Connect caught a exception, message: {0}", e.Message);
Close();
PushEventError(NetError.Connect);
return false;
}
m_nState = NetState.Connecting;
return true;
}
/*
public bool Send(ushort id,
byte[] data, uint nRawID)
{
if (m_nState != NetState.Connected)
return false;
m_Writer.Clear();
uint total = (uint)data.Length + 2 + 4;
m_Writer.WriteUInt(total);
m_Writer.WriteUShort(id);
m_Writer.WriteUInt(nRawID);
m_Writer.WriteBuffer(data);
int nSendSize = (int)m_Writer.Size;
byte[] sendData = new byte[nSendSize];
Array.Copy(m_Writer.Buffer, sendData, nSendSize);
try
{
m_Socket.BeginSend(sendData, 0, nSendSize, SocketFlags.None, m_AsyncSend, null);
}
catch (SocketException e)
{
if (e.ErrorCode == (int)SocketError.WouldBlock)
{
DebugUtil.LogWarning("NetTcp::Send SocketError.WouldBlock");
return true;
}
DebugUtil.LogError($"NetTcp::Send 1 caught a exception, error={0}, message={1}",e.ErrorCode, e.Message);
Close();
PushEventError(NetError.Send);
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::Send 2 caught a exception, message={0}", e.Message);
Close();
PushEventError(NetError.Send);
}
return true;
}
//*/
//*
public bool Send(ushort id,
byte[] data, uint nRawID)
{
if (m_nState != NetState.Connected)
return false;
#if DEVELOPMENT_BUILD || DEBUG
if (NetDebugOptions.BlockUpstream)
{
DebugUtil.LogWarning("NetTcp::Send BlockUpstream is true, block send data, id={0}, nRawID={1}, data length={2}", id, nRawID, data != null ? data.Length : 0);
return true;
}
#endif
m_nPacketIndex ++;
m_Writer.Clear();
//m_Writer.WriteUInt(m_nPacketIndex);
m_Writer.WriteUShort(id);
m_Writer.WriteUInt(nRawID);
m_Writer.WriteBuffer(data);
byte [] dest = new byte[m_Writer.Size + 4];
Cipher.XORKeyStream(dest, 4, m_Writer.Buffer, 0, (int)m_Writer.Size);
//Array.Copy(m_Writer.Buffer, 0, dest, 4, m_Writer.Size);
uint total = (uint)m_Writer.Size;
var lengthData = BitConverter.GetBytes(total);
Array.Copy(lengthData, dest, lengthData.Length);
int nSendSize = (int)(total+4);
try
{
m_Socket.BeginSend(dest, 0, nSendSize, SocketFlags.None, m_AsyncSend, null);
}
catch (SocketException e)
{
if (e.ErrorCode == (int)SocketError.WouldBlock)
{
DebugUtil.LogWarning("NetTcp::Send SocketError.WouldBlock");
return true;
}
DebugUtil.LogError($"NetTcp::Send 1 caught a exception, error={0}, message={1}",e.ErrorCode, e.Message);
Close();
PushEventError(NetError.Send);
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::Send 2 caught a exception, message={0}", e.Message);
Close();
PushEventError(NetError.Send);
}
return true;
}
//*/
public void Receive()
{
if (m_nState != NetState.Connected)
return;
try
{
m_Socket.BeginReceive(m_RecvBuffer, m_nBufferLength, m_RecvBuffer.Length - m_nBufferLength, SocketFlags.None, m_AsyncReceive, null);
}
catch (SocketException e)
{
if (e.ErrorCode == (int)SocketError.WouldBlock)
{
DebugUtil.LogWarning("NetTcp::Receive SocketError.WouldBlock");
return;
}
DebugUtil.LogError("NetTcp::Receive 1 caught a exception, error={0}, message={1}", e.ErrorCode, e.Message);
Close();
PushEventError(NetError.Recv);
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::Receive 2 caught a exception, message={0}", e.Message);
Close();
PushEventError(NetError.Recv);
}
}
public void DisConnect()
{
if (m_nState != NetState.Connected && m_nState != NetState.Connecting)
return;
Close();
PushEventError(NetError.None);
}
private void Close()
{
if (m_nState == NetState.Release || m_nState == NetState.Close)
return;
m_nState = NetState.Close;
if (m_Socket != null)
{
try
{
DebugUtil.Log("NetTcp Close Shutdown");
m_Socket.Shutdown(SocketShutdown.Both);
}
catch
{
}
try
{
DebugUtil.Log("NetTcp Close Close");
m_Socket.Close();
}
catch
{
}
}
}
private void PushEventError(NetError error)
{
if (m_nState == NetState.Release || m_nState == NetState.None)
return;
NetInfo info;
info.type = NetInfoType.Error;
info.data = null;
info.error = error;
m_EventQueue.Push(info);
}
private void PushEventData(byte[] data)
{
if (m_nState == NetState.Release || m_nState == NetState.None)
return;
NetInfo info;
info.type = NetInfoType.Data;
info.data = data;
info.error = NetError.None;
m_EventQueue.Push(info);
}
private void PushEventConnected()
{
if (m_nState == NetState.Release || m_nState == NetState.None)
return;
NetInfo info;
info.type = NetInfoType.Connected;
info.data = null;
info.error = NetError.None;
m_EventQueue.Push(info);
}
public void Dispath()
{
if (m_nState == NetState.Release || m_nState == NetState.None)
{
return;
}
NetInfo info;
while (m_EventQueue != null && m_EventQueue.Pop(out info))
{
#if DEVELOPMENT_BUILD || DEBUG
if (NetDebugOptions.BlockDownstream)
{
DebugUtil.LogWarning("NetTcp::Send BlockDownstream is true, block receive data");
continue;
}
#endif
switch (info.type)
{
case NetInfoType.Connected:
m_Handler.OnConnected();
break;
case NetInfoType.Error:
m_Handler.OnError(info.error);
break;
case NetInfoType.Data:
m_Handler.OnRecv(info.data);
break;
}
}
}
private void OnEndConnect(IAsyncResult iar)
{
try
{
m_Socket.EndConnect(iar);
m_nState = NetState.Connected;
PushEventConnected();
DebugUtil.Log("NetTcp::OnEndConnect connected ...");
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::OnEndConnect caught a exception, message={0}", e.Message);
Close();
PushEventError(NetError.Connect);
return;
}
Receive();
}
private void OnEndSend(IAsyncResult iar)
{
if (m_nState != NetState.Connected)
return;
try
{
var len = m_Socket.EndSend(iar);
// DebugUtil.Log("OnEndSend length=" + len);
}
catch (SocketException e)
{
if (e.ErrorCode == (int)SocketError.WouldBlock)
{
DebugUtil.LogWarning("NetTcp::OnEndSend SocketError.WouldBlock");
return;
}
DebugUtil.LogError("NetTcp::OnEndSend 1 caught a exception, error={0}, message={1}", e.ErrorCode, e.Message);
Close();
PushEventError(NetError.Send);
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::OnEndSend 2 caught a exception, message={0}", e.Message);
Close();
PushEventError(NetError.Send);
}
}
private void OnEndReceive(IAsyncResult iar)
{
if (m_nState != NetState.Connected)
return;
try
{
int nReceiveLength = m_Socket.EndReceive(iar);
if(nReceiveLength > 0)
{
OnReceiveData(nReceiveLength);
Receive();
}
else
{
Close();
PushEventError(NetError.Recv);
}
}
catch (SocketException e)
{
if (e.ErrorCode == (int)SocketError.WouldBlock)
{
DebugUtil.LogWarning("NetTcp::OnEndSend SocketError.WouldBlock");
return;
}
if (e.ErrorCode != (int)SocketError.NetworkDown)//网络已关闭
DebugUtil.LogError("NetTcp::OnEndReceive 1 caught a exception, error={0}, message={1}", e.ErrorCode, e.Message);
Close();
PushEventError(NetError.Recv);
return;
}
catch (Exception e)
{
DebugUtil.LogError("NetTcp::OnEndReceive 2 caught a exception, message={0}", e.Message);
Close();
PushEventError(NetError.Recv);
return;
}
}
private int GetPackageLength(byte[] buffer,
int startPos)
{
m_LengthData[0] = buffer[startPos];
m_LengthData[1] = buffer[startPos + 1];
m_LengthData[2] = buffer[startPos + 2];
m_LengthData[3] = buffer[startPos + 3];
return BitConverter.ToInt32(m_LengthData, 0);
}
private void OnReceiveData(int nReceiveLength)
{
// 总数据长度
int nTotalLen = m_nBufferLength + nReceiveLength;
if (nTotalLen <= m_nUintSize) // 数据太小
{
m_nBufferLength += nReceiveLength;
return;
}
int nPackageLen = GetPackageLength(m_RecvBuffer, 0);
if (nPackageLen < 2)
{
throw new NetPackageParseException();
}
// 如果总长度 < 数据长度+ 数据头, 说明数据还未接收完
if (nTotalLen < (int)(nPackageLen + m_nUintSize))
{
m_nBufferLength += nReceiveLength;
return;
}
else if (nTotalLen == nPackageLen + m_nUintSize) // 刚好收到一个包
{
byte[] packetData = new byte[nPackageLen];
Array.Copy(m_RecvBuffer, m_nUintSize, packetData, 0, nPackageLen);
PushEventData(packetData);
m_nBufferLength = 0;
return;
}
else // 收到了多个包,要把包都拆出来
{
Array.Copy(m_RecvBuffer, m_TempBuffer, nTotalLen); // 把已经接收到的数据备份一下
int nReadPos = 0;
while (true)
{
byte[] packetData = new byte[nPackageLen];
Array.Copy(m_TempBuffer, nReadPos + m_nUintSize, packetData, 0, nPackageLen);
PushEventData(packetData);
nReadPos += nPackageLen + m_nUintSize;
m_nBufferLength = nTotalLen - nReadPos; // 剩余数据长度
if (m_nBufferLength <= m_nUintSize) // 如果剩余的长度不够两个字节
{
Array.Copy(m_TempBuffer, nReadPos, m_RecvBuffer, 0, m_nBufferLength); // 把剩余的数据拷贝回去,等待下剩余的数据
break;
}
else // 如果超过四个字节
{
nPackageLen = GetPackageLength(m_TempBuffer, nReadPos); // 得到下一个包的数据长度
if (nPackageLen < 2)
{
throw new NetPackageParseException();
}
// 数据不够,继续去等待下一段数据
if (m_nBufferLength < nPackageLen + m_nUintSize)
{
Array.Copy(m_TempBuffer, nReadPos, m_RecvBuffer, 0, m_nBufferLength);
break;
}
}
}
}
}
}
}