当前位置:网站首页>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;
}
}
}
}
边栏推荐
- 保证接口数据安全的10种方案
- Getting started with webgl (4)
- Vite path alias @ configuration
- Points for attention in porting gd32 F4 series programs to gd32 F3 series
- Using eating in cocos Creator
- Three. JS introductory learning notes 05: external model import -c4d into JSON file for web pages
- Use of SVN
- AE learning 02: timeline
- Cut ffmpeg as needed, and use emscripten to compile and run
- 深度之眼(七)——矩阵的初等变换(附:数模一些模型的解释)
猜你喜欢
HPDC smart base Talent Development Summit essay
Three. JS introductory learning notes 05: external model import -c4d into JSON file for web pages
15. Using the text editing tool VIM
postman生成时间戳,未来时间戳
讲师征集令 | Apache SeaTunnel(Incubating) Meetup 分享嘉宾火热招募中!
【数字IC验证快速入门】19、SystemVerilog学习之基本语法6(线程内部通信...内含实践练习)
强化实时数据管理,英方软件助力医保平台安全建设
Use of SVN
Super signature principle (fully automated super signature) [Yun Xiaoduo]
Vite path alias @ configuration
随机推荐
TS as a general cache method
numpy--数据清洗
Three. JS introductory learning notes 10:three JS grid
Learn good-looking custom scroll bars in 1 minute
Spin animation of Cocos performance optimization
OpenGL's distinction and understanding of VAO, VBO and EBO
Nacos conformance protocol cp/ap/jraft/distro protocol
How to understand that binary complement represents negative numbers
Three. JS introductory learning notes 0: illustration of how webgl and threejs work
【數字IC驗證快速入門】20、SystemVerilog學習之基本語法7(覆蓋率驅動...內含實踐練習)
nodejs package. JSON version number ^ and~
LeetCode3_ Longest substring without duplicate characters
航天宏图信息中标乌鲁木齐某单位数据库系统研发项目
Use moviepy Editor clips videos and intercepts video clips in batches
Wireless sensor networks -- ZigBee and 6LoWPAN
[quickstart to Digital IC Validation] 20. Basic syntax for system verilog Learning 7 (Coverage Driven... Including practical exercises)
Three. JS introductory learning notes 00: coordinate system, camera (temporarily understood)
Getting started with webgl (2)
Async and await
Cocos creator collision and collision callback do not take effect