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

495 lines
15 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;
using UnityEngine;
namespace Framework
{
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 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)
{
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((uint)id);
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 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))
{
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
{
m_Socket.EndSend(iar);
}
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;
}
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;
}
}
}
}
}
}
}