当前位置:网站首页>TCP framework___ Unity
TCP framework___ Unity
2022-07-07 15:53:00 【Le_ Sam】
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using UnityEngine;
using System.Text;
namespace Core
{
public class Protocal
{
public const int Connect = -1; // Connect to server
public const int Exception = -2; // Abnormal dropped line
public const int Disconnect = -3; // Normal disconnection
}
enum PACKET_TYPE
{ // Packet format ( Rules set by the server , The client sends strictly according to the format / receive ),PackLen Used to identify the subsequent package length ,Place Is an invalid placeholder ,Type Is the type of package (PACKET_TYPE),Seq The function is unknown. The default is 0,Code Is the error code ,Body For the body of the package
PUSH_TYPE = 0, // PackLen【2byte】,Place【1byte】,Type【1byte】,Body【 Maximum 4096byte】
REQUSET_TYPE, // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Body【 Maximum 4096byte】
RESPONSE_TYPE, // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Code( Error code )【2byte】,Body【 Maximum 4096byte】
PING_TYPE, // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS: Heartbeat sending
PONG_TYPE // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS: Heartbeat back
};
// Give Way byte[] Press into lua string Not an array userdata
// You can also use LuaByteBufferAttribute To mark byte[]
public struct LuaByteBuffer
{
public LuaByteBuffer(IntPtr source, int len)
: this()
{
buffer = new byte[len];
Length = len;
Marshal.Copy(source, buffer, 0, len);
}
public LuaByteBuffer(byte[] buf)
: this()
{
buffer = buf;
Length = buf.Length;
}
public LuaByteBuffer(byte[] buf, int len)
: this()
{
buffer = buf;
Length = len;
}
public LuaByteBuffer(System.IO.MemoryStream stream)
: this()
{
buffer = stream.GetBuffer();
Length = (int)stream.Length;
}
public static implicit operator LuaByteBuffer(System.IO.MemoryStream stream)
{
return new LuaByteBuffer(stream);
}
public byte[] buffer;
public int Length
{
get;
private set;
}
}
// Byte Data structures , It should be noted that each time data is written in order
public class ByteBuffer
{
MemoryStream stream = null;
BinaryWriter writer = null;
BinaryReader reader = null;
public ByteBuffer()
{
stream = new MemoryStream();
writer = new BinaryWriter(stream);
}
public ByteBuffer(byte[] data)
{
if (data != null)
{
stream = new MemoryStream(data);
reader = new BinaryReader(stream);
}
else
{
stream = new MemoryStream();
writer = new BinaryWriter(stream);
}
}
public void Close()
{
if (writer != null)
{
writer.Close();
}
if (reader != null)
{
reader.Close();
}
stream.Close();
writer = null;
reader = null;
stream = null;
}
public void WriteByte(byte v)
{
writer.Write(v);
}
public void WriteInt(int v)
{
writer.Write((int)v);
}
public void WriteShort(ushort v)
{
writer.Write((ushort)v);
}
public void WriteLong(long v)
{
writer.Write((long)v);
}
public void WriteFloat(float v)
{
byte[] bytes = BitConverter.GetBytes(v);
Array.Reverse(bytes);
writer.Write(BitConverter.ToSingle(bytes, 0));
}
public void WriteDouble(double v)
{
byte[] bytes = BitConverter.GetBytes(v);
Array.Reverse(bytes);
writer.Write(BitConverter.ToDouble(bytes, 0));
}
public void WriteString(string v)
{
byte[] bytes = Encoding.UTF8.GetBytes(v);
writer.Write((ushort)bytes.Length);
writer.Write(bytes);
}
public void WriteBytes(byte[] v)
{
writer.Write((int)v.Length);
writer.Write(v);
}
public void WriteBytesNoLen(byte[] v)
{
writer.Write(v);
}
public void WriteBuffer(LuaByteBuffer strBuffer)
{
WriteBytes(strBuffer.buffer);
}
public byte ReadByte()
{
return reader.ReadByte();
}
public int ReadInt()
{
return (int)reader.ReadInt32();
}
public ushort ReadShort()
{
return (ushort)reader.ReadInt16();
}
public long ReadLong()
{
return (long)reader.ReadInt64();
}
public float ReadFloat()
{
byte[] bytes = BitConverter.GetBytes(reader.ReadSingle());
Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
public double ReadDouble()
{
byte[] bytes = BitConverter.GetBytes(reader.ReadDouble());
Array.Reverse(bytes);
return BitConverter.ToDouble(bytes, 0);
}
public string ReadString()
{
ushort len = ReadShort();
byte[] buffer = new byte[len];
buffer = reader.ReadBytes(len);
return Encoding.UTF8.GetString(buffer);
}
public byte[] ReadBytes()
{
int len = ReadInt();
return reader.ReadBytes(len);
}
public LuaByteBuffer ReadBuffer()
{
byte[] bytes = ReadBytes();
return new LuaByteBuffer(bytes);
}
public byte[] ToBytes()
{
writer.Flush();
return stream.ToArray();
}
public void Flush()
{
writer.Flush();
}
}
public class SocketClient
{
private TcpClient client = null;
private NetworkStream outStream = null; // Network flow used to send data to the server
private MemoryStream memStream; // Used to process the memory stream that receives data from the server ,
private BinaryReader reader;
private const int HEAD_BASE_LEN = 2;
private const int MAX_READ = 8192;
private byte[] byteBuffer = new byte[MAX_READ];
private int iSocketPort = 0; //Socket Server port
private string iSocketAddress = string.Empty; //Socket Server address
private IAsyncResult onReadAsyncResult;
private static SocketClient _SocketClient;
private int ConnectionStatus = -1;
private readonly static byte[] mConnLock = new byte[0]; // Link lock
public static SocketClient GetSocketClient()
{
return _SocketClient;
}
public static void SetSocketClient(SocketClient socketClient)
{
_SocketClient = socketClient;
}
public SocketClient()
{
memStream = new MemoryStream();
reader = new BinaryReader(memStream);
SocketClient.SetSocketClient(this);
}
~SocketClient()
{
Close();
reader.Close();
memStream.Close();
}
// Connect
public void Connect(string addr, int port)
{
iSocketAddress = addr;
iSocketPort = port;
ReConnect();
}
// Reconnection
private void ReConnect()
{
try
{
IPAddress ipAddress;
if (!ParseIpFromHost(iSocketAddress, out ipAddress))
{
OnDisconnected(Protocal.Exception, string.Format("Parse ip from {0} failed", iSocketAddress));
return;
}
client = new TcpClient(ipAddress.AddressFamily)
{
SendTimeout = 10000,
ReceiveTimeout = 10000,
NoDelay = true
};
// Initiate a link
ConnectionStatus = -1;
client.BeginConnect(iSocketAddress, iSocketPort, new AsyncCallback(OnConnect), null);
// Connection timeout
Thread timeout = new Thread(CheckConnectTimeout);
timeout.IsBackground = true;
timeout.Start();
}
catch (Exception e)
{
Debug.Log("Socket ReConnect");
OnDisconnected(Protocal.Exception, e);
}
}
// Link timeout detection
private void CheckConnectTimeout()
{
lock (mConnLock)
{
Monitor.Wait(mConnLock, 10000);
if (!client.Connected && ConnectionStatus < 0)
{
OnDisconnected(Protocal.Exception, new TimeoutException("Socket Connect Timeout."));
}
}
}
// Exception handling
private void OnDisconnected(int protocal, Exception e)
{
Close();
PostMessage(protocal);
if (e is SocketException)
{
Debug.Log(e.ToString() + " SocketErrorCode: " + ((SocketException)(e)).ErrorCode);
}
else
{
Debug.Log(e.ToString());
}
}
// Exception handling
private void OnDisconnected(int protocal, string msg)
{
Close();
PostMessage(protocal);
Debug.Log("SocketErrorCode: " + msg);
}
private static bool ParseIpFromHost(string host, out IPAddress address)
{
address = null;
try
{
if (!IPAddress.TryParse(host, out address))
{
var entries = Dns.GetHostEntry(host);
var addressLst = entries.AddressList.ToList();
address = addressLst.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetworkV6) ?? addressLst.First();
}
return true;
}
catch (Exception e)
{
Debug.Log(e);
return false;
}
}
private void OnConnect(IAsyncResult asr)
{
memStream.SetLength(0);
try
{
lock (mConnLock)
{
// If CheckConnectTimeout It's overtime , This will turn client It's empty , Don't go down
if (client == null)
{
return;
}
// Connection status
ConnectionStatus = client.Connected ? 1 : 0;
// The connection fails
if (ConnectionStatus == 0)
{
PostMessage(Protocal.Exception); // PostMessage towards Lua End send message
return;
}
// Successful connection
outStream = client.GetStream();
onReadAsyncResult = client.GetStream().BeginRead(byteBuffer, 0, MAX_READ, new AsyncCallback(OnRead), null);
PostMessage(Protocal.Connect);
Monitor.PulseAll(mConnLock);
}
}
catch (Exception e)
{
OnDisconnected(Protocal.Exception, e);
}
}
private void OnRead(IAsyncResult asr)
{
if (client == null || !client.Connected)
{
return;
}
int bytesRead = 0;
try
{
lock (client.GetStream())
{ // Read byte stream to buffer
bytesRead = client.GetStream().EndRead(asr);
}
if (bytesRead < 1)
{ // There is a problem with the size of the bag , Disconnection treatment
OnDisconnected(Protocal.Disconnect, "bytesRead < 1");
return;
}
// Analyze packet content , Throw it to the logic layer
OnReceive(byteBuffer, bytesRead);
// Analysis finished , Listen again for new messages from the server
lock (client.GetStream())
{ // Empty array
Array.Clear(byteBuffer, 0, byteBuffer.Length);
onReadAsyncResult = client.GetStream().BeginRead(byteBuffer, 0, MAX_READ, new AsyncCallback(OnRead), null);
}
}
catch (Exception e)
{
OnDisconnected(Protocal.Exception, e);
}
}
// Send data to the server
private void WriteMessage(byte[] message)
{
if (client == null || !client.Connected)
{
return;
}
try
{
outStream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
}
catch (Exception e)
{
OnDisconnected(Protocal.Exception, e);
}
}
private void OnWrite(IAsyncResult asr)
{
if (client == null || !client.Connected)
{
return;
}
try
{
outStream.EndWrite(asr);
}
catch (Exception e)
{
OnDisconnected(Protocal.Exception, e);
}
}
// send out json Format data . Different types of packages , The data format is different , According to demand , Details refer to PACKET_TYPE
public void Send(int type, int seq, string msg)
{
//Debug.Log("Send type: " + type.ToString() + " seq: " + seq.ToString() + " msg: " + msg);
switch (type)
{
case (int)PACKET_TYPE.REQUSET_TYPE:
SendRequset(type, seq, msg);
break;
case (int)PACKET_TYPE.PING_TYPE:
SendPing(type, seq);
break;
default:
Debug.Log("SocketClientEx ==> The type is not supported " + msg);
return;
}
}
private void SendRequset(int type, int seq, string msg)
{
ByteBuffer buf = new ByteBuffer();
byte[] msgByte = Encoding.UTF8.GetBytes(msg);
buf.WriteShort((ushort)(msgByte.Length + 4)); // Place+Type+Seq = 4
buf.WriteByte(0); // Place Invalid placeholder
buf.WriteByte((byte)type); //1
buf.WriteShort((ushort)seq); //0
buf.WriteBytesNoLen(msgByte);
//Debug.Log(" Total length " + buf.ToBytes().Length + " msg length " + msgByte.Length);
WriteMessage(buf.ToBytes());
}
private void SendPing(int type, int seq)
{
ByteBuffer buf = new ByteBuffer();
buf.WriteShort(3); // Place+Type+Seq = 3
buf.WriteByte(0); // Place Invalid placeholder
buf.WriteByte((byte)type); //3
buf.WriteByte((byte)seq); //0
WriteMessage(buf.ToBytes());
}
// Data analysis
private void OnReceive(byte[] bytes, int length)
{
// Write the read bytes at the end of the stream
memStream.Seek(0, SeekOrigin.End);
memStream.Write(bytes, 0, length);
// The flow pointer is reset to the head
memStream.Seek(0, SeekOrigin.Begin);
// Judge whether the current stream length is greater than the packet length ( Bag length 2 byte )
while (RemainingBytes() >= 2)
{
// front 2 Byte is the header , Indicate the subsequent package length
int messageLen = (int)reader.ReadUInt16();
long curover = RemainingBytes();
//Debug.Log(" Now read the length " + curover + ", The length of the packet " + messageLen);
// When the received data is larger than the packet length , Indicates that the current inclusion data has been received
if (curover >= messageLen)
{
// Temporary memory stream , from reader Extract the required bytes
MemoryStream ms = new MemoryStream();
BinaryWriter writer = new BinaryWriter(ms);
// Only get the data of the current packet length
writer.Write(reader.ReadBytes(messageLen));
ms.Seek(0, SeekOrigin.Begin);
OnReceivedMessage(ms);
}
else
{
// When the received data is incomplete , Ignore this execution , And back out the pointer , Wait for the next callback
memStream.Position = memStream.Position - 2;
break;
}
}
int leftovers = (int)RemainingBytes();
//Debug.Log(" End of analysis , The remaining bytes " + leftovers);
// Take out the remaining bytes , And reset the memory stream , To write
byte[] leftover = reader.ReadBytes(leftovers);
memStream.SetLength(0); //Clear
memStream.Write(leftover, 0, leftover.Length);
}
// The difference between the query stream length and the current stream pointer , It is used to judge whether the current flow meets the needs
private long RemainingBytes()
{
return memStream.Length - memStream.Position;
}
// Parse the packet contents ( Except package length 2 Contents other than bytes )
private void OnReceivedMessage(MemoryStream ms)
{
if (ms.Length >= HEAD_BASE_LEN)
{
BinaryReader r = new BinaryReader(ms);
int place = r.ReadByte(); // Place【1byte】
int type = r.ReadByte(); // Type【1byte】
int seq = 0;
int code = 0;
// response type , Need to parse again 4 Bytes are the body
if (type == (int)PACKET_TYPE.RESPONSE_TYPE)
{
seq = r.ReadUInt16(); // Seq【2byte】
code = r.ReadUInt16(); // Code【2byte】
}
// pong type , Heartbeat back
else if (type == (int)PACKET_TYPE.PONG_TYPE)
{
seq = r.ReadByte(); // Seq【1byte】
}
if (ms.Length - ms.Position > 0)
{
byte[] body = r.ReadBytes((int)(ms.Length - ms.Position));
LuaByteBuffer buffer = new LuaByteBuffer(body);
// The body of the package sent by the server is json, Convert directly to string, It depends on the demand
Debug.Log("Received Msg " + Encoding.UTF8.GetString(buffer.buffer));
PostMessage(0, buffer);
}
}
}
// Callback message to client , undetermined ,cmd Is the command word ,buffer Is the actual data
protected void PostMessage(int cmd, object buffer = null)
{
// This interface is owned by the original project , Used to direct to lua End send message , hold proto2 Antisequential formation lua surface
// MessageCenter.Post(MessageNames.Socket, this, cmd, buffer);
}
public void Close()
{
if (onReadAsyncResult != null)
{
onReadAsyncResult.AsyncWaitHandle.Close();
onReadAsyncResult = null;
}
if (client != null)
{
if (client.Connected)
{
if (client.GetStream() != null)
{
client.GetStream().Dispose();
}
if (client.Client.Connected)
{
client.Client.Shutdown(SocketShutdown.Both);
}
client.Close();
}
client = null;
}
}
}
}
边栏推荐
- TS as a general cache method
- Nacos conformance protocol cp/ap/jraft/distro protocol
- Please supervise the 2022 plan
- [quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)
- Getting started with webgl (4)
- C4D learning notes 1- animation - animation key frames
- 神经网络c语言中的指针是怎么回事
- Limit of total fields [1000] in index has been exceeded
- Summary of knowledge points of xlua hot update solution
- Three. JS introductory learning notes 00: coordinate system, camera (temporarily understood)
猜你喜欢
Whole process analysis of unity3d rendering pipeline
How to create Apple Developer personal account P8 certificate
Steps to create P8 certificate and warehousing account
Numpy --- basic learning notes
星瑞格数据库入围“2021年度福建省信息技术应用创新典型解决方案”
深度之眼(七)——矩阵的初等变换(附:数模一些模型的解释)
postman生成时间戳,未来时间戳
Three. JS introductory learning notes 05: external model import -c4d into JSON file for web pages
C4D learning notes 2- animation - timeline and time function
Ue4/ue5 multi thread development attachment plug-in download address
随机推荐
How to deploy the super signature distribution platform system?
Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)
A JS script can be directly put into the browser to perform operations
[quick start of Digital IC Verification] 23. AHB sramc of SystemVerilog project practice (3) (basic points of AHB protocol)
HPDC smart base Talent Development Summit essay
Detailed explanation of unity hot update knowledge points and introduction to common solution principles
LeetCode2_ Add two numbers
Tkinter after how to refresh data and cancel refreshing
The significance of XOR in embedded C language
C4D learning notes 2- animation - timeline and time function
Vertex shader to slice shader procedure, varying variable
AE learning 01: AE complete project summary
Virtual memory, physical memory /ram what
Spin animation of Cocos performance optimization
Ue4/ue5 multi thread development attachment plug-in download address
[quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)
Shader basic UV operations, translation, rotation, scaling
Three. JS introductory learning notes 07: external model import -c4d to JSON file for web pages -fbx import
Zhongang Mining: Fluorite continues to lead the growth of new energy market
postman生成时间戳,未来时间戳