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; //连接服务器
public const int Exception = -2; //异常掉线
public const int Disconnect = -3; //正常断线
}
enum PACKET_TYPE
{ // 包格式(由服务器定下的规矩,客户端严格按照格式发送/接收),PackLen用于标识后续包长,Place为无效占位符,Type为包的类型(PACKET_TYPE),Seq作用未知默认为0,Code为错误码,Body为包正文
PUSH_TYPE = 0, // PackLen【2byte】,Place【1byte】,Type【1byte】,Body【最大4096byte】
REQUSET_TYPE, // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Body【最大4096byte】
RESPONSE_TYPE, // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Code(错误码)【2byte】,Body【最大4096byte】
PING_TYPE, // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS:心跳发送
PONG_TYPE // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS:心跳回包
};
//让byte[] 压入成为lua string 而不是数组 userdata
//也可以使用LuaByteBufferAttribute来标记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数据结构体,需要注意每次数据写入为有序的
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; // 用于向服务器发送数据的网络流
private MemoryStream memStream; // 用于处理从服务器接收数据的内存流,
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服务器端口
private string iSocketAddress = string.Empty; //Socket服务器地址
private IAsyncResult onReadAsyncResult;
private static SocketClient _SocketClient;
private int ConnectionStatus = -1;
private readonly static byte[] mConnLock = new byte[0]; //链接锁
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();
}
// 连接
public void Connect(string addr, int port)
{
iSocketAddress = addr;
iSocketPort = port;
ReConnect();
}
// 重连
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
};
// 发起链接
ConnectionStatus = -1;
client.BeginConnect(iSocketAddress, iSocketPort, new AsyncCallback(OnConnect), null);
// 链接超时
Thread timeout = new Thread(CheckConnectTimeout);
timeout.IsBackground = true;
timeout.Start();
}
catch (Exception e)
{
Debug.Log("Socket ReConnect");
OnDisconnected(Protocal.Exception, e);
}
}
// 链接超时检测
private void CheckConnectTimeout()
{
lock (mConnLock)
{
Monitor.Wait(mConnLock, 10000);
if (!client.Connected && ConnectionStatus < 0)
{
OnDisconnected(Protocal.Exception, new TimeoutException("Socket Connect Timeout."));
}
}
}
// 异常统一处理
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());
}
}
// 异常统一处理
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)
{
//如果CheckConnectTimeout超时了,这会把client置空了,就别往下走了
if (client == null)
{
return;
}
// 连接状态
ConnectionStatus = client.Connected ? 1 : 0;
// 连接失败
if (ConnectionStatus == 0)
{
PostMessage(Protocal.Exception); // PostMessage向Lua端发送消息
return;
}
// 连接成功
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())
{ //读取字节流到缓冲区
bytesRead = client.GetStream().EndRead(asr);
}
if (bytesRead < 1)
{ //包尺寸有问题,断线处理
OnDisconnected(Protocal.Disconnect, "bytesRead < 1");
return;
}
//分析数据包内容,抛给逻辑层
OnReceive(byteBuffer, bytesRead);
//分析完,再次监听服务器发过来的新消息
lock (client.GetStream())
{ //清空数组
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);
}
}
// 向服务端发送数据
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);
}
}
// 发送json格式数据。不同类型的包,数据格式不同,根据需求而定,详情参考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 ==> 不支持该类型 " + 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无效占位符
buf.WriteByte((byte)type); //1
buf.WriteShort((ushort)seq); //0
buf.WriteBytesNoLen(msgByte);
//Debug.Log("总长度 " + buf.ToBytes().Length + " msg长度 " + 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无效占位符
buf.WriteByte((byte)type); //3
buf.WriteByte((byte)seq); //0
WriteMessage(buf.ToBytes());
}
// 数据解析
private void OnReceive(byte[] bytes, int length)
{
// 在流的末端写入读出的字节
memStream.Seek(0, SeekOrigin.End);
memStream.Write(bytes, 0, length);
// 流指针复位至头部
memStream.Seek(0, SeekOrigin.Begin);
// 判断当前流长度是否大于包长(包长2字节)
while (RemainingBytes() >= 2)
{
// 前2字节为包头,指明后续的包长
int messageLen = (int)reader.ReadUInt16();
long curover = RemainingBytes();
//Debug.Log("此时读出长度 " + curover + ",数据包应有的长度 " + messageLen);
// 当接收的数据大于包长时,表明当前包体数据已接收完毕
if (curover >= messageLen)
{
// 临时内存流,从reader提取需要的字节
MemoryStream ms = new MemoryStream();
BinaryWriter writer = new BinaryWriter(ms);
// 只取当前包长的数据
writer.Write(reader.ReadBytes(messageLen));
ms.Seek(0, SeekOrigin.Begin);
OnReceivedMessage(ms);
}
else
{
//当接收数据不完全时,忽略这次执行,并对流指针进行回退操作,等待下次回调
memStream.Position = memStream.Position - 2;
break;
}
}
int leftovers = (int)RemainingBytes();
//Debug.Log("解析完毕,剩余字节 " + leftovers);
//取出剩余字节,并重置内存流,重新写入
byte[] leftover = reader.ReadBytes(leftovers);
memStream.SetLength(0); //Clear
memStream.Write(leftover, 0, leftover.Length);
}
// 查询流长度与当前流指针的差异,用于判断当前流是否满足需要
private long RemainingBytes()
{
return memStream.Length - memStream.Position;
}
//解析数据包内容(除包长2字节以外的内容)
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 类型,需要再次解析4个字节才是正文
if (type == (int)PACKET_TYPE.RESPONSE_TYPE)
{
seq = r.ReadUInt16(); // Seq【2byte】
code = r.ReadUInt16(); // Code【2byte】
}
// pong 类型,心跳回包
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);
// 服务端发送包正文为json,直接转换成string,由需求而定
Debug.Log("Received Msg " + Encoding.UTF8.GetString(buffer.buffer));
PostMessage(0, buffer);
}
}
}
// 向客户端回调消息,待定,cmd为命令字,buffer为则是实际数据
protected void PostMessage(int cmd, object buffer = null)
{
// 此接口为原项目工程所有,用于向lua端发送消息,把proto2反序列化成lua表
// 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;
}
}
}
}