486 lines
15 KiB
C#
486 lines
15 KiB
C#
//////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// 文件: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);
|
||
OnReceiveData(nReceiveLength);
|
||
}
|
||
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;
|
||
}
|
||
|
||
Receive();
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|