当前位置:网站首页>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;
}
}
}
}
边栏推荐
- Gd32 F3 pin mapping problem SW interface cannot be burned
- L'application à l'échelle de la normalisation mature des produits ai des compagnies maritimes, cimc, leader mondial de l'intelligence artificielle portuaire et maritime / intelligence artificielle des
- Shader Language
- Three. JS introductory learning notes 15: threejs frame animation module
- Ue4/ue5 multi thread development attachment plug-in download address
- HPDC smart base Talent Development Summit essay
- Starting from 1.5, build a microservice framework link tracking traceid
- Yunxiaoduo software internal test distribution test platform description document
- Getting started with webgl (1)
- 15. Using the text editing tool VIM
猜你喜欢

【数字IC验证快速入门】19、SystemVerilog学习之基本语法6(线程内部通信...内含实践练习)

Three. JS introductory learning notes 11:three JS group composite object

Numpy -- epidemic data analysis case

一大波开源小抄来袭

webgl_ Enter the three-dimensional world (2)

【数字IC验证快速入门】18、SystemVerilog学习之基本语法5(并发线程...内含实践练习)

Three. JS introductory learning notes 05: external model import -c4d into JSON file for web pages
![[Lanzhou University] information sharing of postgraduate entrance examination and re examination](/img/06/df5a64441814c9ecfa2f039318496e.jpg)
[Lanzhou University] information sharing of postgraduate entrance examination and re examination

过度依赖补助,大客户收款难,冲刺“国产数据库第一股”的达梦后劲有多足?

2022 all open source enterprise card issuing network repair short website and other bugs_ 2022 enterprise level multi merchant card issuing platform source code
随机推荐
Detailed explanation of Cocos creator 2.4.0 rendering process
Three. JS introductory learning notes 03: perspective projection camera
Keil5 does not support online simulation of STM32 F0 series
Three. JS introductory learning notes 08:orbitcontrols JS plug-in - mouse control model rotation, zoom in, zoom out, translation, etc
OpenGL's distinction and understanding of VAO, VBO and EBO
招标公告:福建省农村信用社联合社数据库审计系统采购项目(重新招标)
The significance of XOR in embedded C language
Annexb and avcc are two methods of data segmentation in decoding
How to deploy the super signature distribution platform system?
Gd32 F3 pin mapping problem SW interface cannot be burned
AB package details in unity (super detail, features, packaging, loading, manager)
15. Using the text editing tool VIM
LeetCode1_ Sum of two numbers
航运船公司人工智能AI产品成熟化标准化规模应用,全球港航人工智能/集装箱人工智能领军者CIMC中集飞瞳,打造国际航运智能化标杆
webgl_ Enter the three-dimensional world (1)
Cocos uses custom material to display problems
The download button and debug button in keil are grayed out
There are many ways to realize the pause function in JS
./ Functions of configure, make and make install
TS as a general cache method