当前位置:网站首页>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;
}
}
}
}
边栏推荐
- [quick start of Digital IC Verification] 25. AHB sramc of SystemVerilog project practice (5) (AHB key review, key points refining)
- Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)
- 航天宏图信息中标乌鲁木齐某单位数据库系统研发项目
- 航运船公司人工智能AI产品成熟化标准化规模应用,全球港航人工智能/集装箱人工智能领军者CIMC中集飞瞳,打造国际航运智能化标杆
- Learn good-looking custom scroll bars in 1 minute
- Wireless sensor networks -- ZigBee and 6LoWPAN
- Getting started with webgl (2)
- 强化实时数据管理,英方软件助力医保平台安全建设
- [Lanzhou University] information sharing of postgraduate entrance examination and re examination
- 2022山东智慧养老展,适老穿戴设备展,养老展,山东老博会
猜你喜欢
LeetCode1_ Sum of two numbers
Vite path alias @ configuration
SPI master rx time out中断
Virtual memory, physical memory /ram what
Yunxiaoduo software internal test distribution test platform description document
一大波开源小抄来袭
20th anniversary of agile: a failed uprising
Cut ffmpeg as needed, and use emscripten to compile and run
Annexb and avcc are two methods of data segmentation in decoding
OpenGL's distinction and understanding of VAO, VBO and EBO
随机推荐
讲师征集令 | Apache SeaTunnel(Incubating) Meetup 分享嘉宾火热招募中!
XMIND frame drawing tool
How to deploy the super signature distribution platform system?
Vertex shader to slice shader procedure, varying variable
Cut ffmpeg as needed, and use emscripten to compile and run
深度之眼(七)——矩阵的初等变换(附:数模一些模型的解释)
Mesh merging under ue4/ue5 runtime
When opening the system window under UE4 shipping, the problem of crash is attached with the plug-in download address
Use of SVN
Numpy -- data cleaning
AE learning 02: timeline
[quickstart to Digital IC Validation] 20. Basic syntax for system verilog Learning 7 (Coverage Driven... Including practical exercises)
【微信小程序】Chapter(5):微信小程序基础API接口
LeetCode1_ Sum of two numbers
[quick start of Digital IC Verification] 26. Ahb-sramc of SystemVerilog project practice (6) (basic points of APB protocol)
招标公告:福建省农村信用社联合社数据库审计系统采购项目(重新招标)
How does geojson data merge the boundaries of regions?
【数字IC验证快速入门】20、SystemVerilog学习之基本语法7(覆盖率驱动...内含实践练习)
用手机在通达信上开户靠谱吗?这样炒股有没有什么安全隐患
Write sequence frame animation with shader