using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
///
/// 套接字类(已封装)
///
public class SocketClient
{
//private static SocketClient2 instance { get; set; }
//ip地址与端口
string address, port;
///
/// 有效接收字节
///
byte[] receiveData = new byte[1024];
///
/// 套接字对象
///
Socket clientSocket { get; set; }
float timerHeart;//心跳包计时器
///
/// 接收数据队列
///
public Queue receiveQueue = new Queue();
public SocketClient(string address, string port)
{
this.address = address;
this.port = port;
}
#region socket通讯
///
/// 连接异步socket
///
public void OnConnectAsync()
{
try
{
//192.168.0.114:6000 本地
//192.168.0.168:3889 外网
//192.168.0.139 电脑
//int _port = 2346;
// _ip = "192.168.0.114";
//string _ip = "192.168.0.168";
//创建客户端Socket,获得远程ip和端口号
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
/*IPHostEntry hostinfo = Dns.GetHostEntry("baidu.com");
IPAddress[] aryIP = hostinfo.AddressList;
IPAddress address = aryIP[0];*///域名连接
IPAddress ip = IPAddress.Parse(address);
IPEndPoint point = new IPEndPoint(ip, int.Parse(port));
IAsyncResult result = clientSocket.BeginConnect(point, new AsyncCallback(ConnectCallback), clientSocket);
bool success = result.AsyncWaitHandle.WaitOne(5000, true);
if (!success)
{
//Debug.Log("服务器连接失败,是否重新连接?");
}
}
catch (Exception e)
{
//Debug.Log(e.ToString());
}
}
///
/// 发送消息
///
///
public void Send(string str)
{
if (clientSocket != null && !clientSocket.Connected)
{
//Debug.Log("socket is not connecting || socket is not existed");
return;
}
byte[] msg = Encoding.UTF8.GetBytes(str);
int mesgLength = msg.Length;
try
{
SocketError socketError;
IAsyncResult ar = clientSocket.BeginSend(msg, 0, msg.Length, SocketFlags.None, out socketError, SendCallback, clientSocket);
}
catch (Exception e)
{
//Debug.Log(e.ToString());
}
}
///
/// 关闭socket
///
public void Closed()
{
try
{
if (clientSocket != null && clientSocket.Connected)
{
//先停止接收和发送的服务
clientSocket.Shutdown(SocketShutdown.Both);
//关闭socket资源
clientSocket.Close();
//取消socket缓存
if (clientSocket != null)
{
//clientSocket.Dispose();
}
clientSocket = null;
}
}
//必须加上,客户端使用轮询处理断线重连时候有可能多次调用,异步导致报错使程序直接卡死
catch (Exception e)
{
//Debug.Log(e.ToString());
}
}
///
/// 成功连接后的回调
///
///
void ConnectCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
socket.EndConnect(ar);
//Debug.Log(socket.Connected);
//异步挂载,等待数据接收
socket.BeginReceive(receiveData, 0, receiveData.Length, SocketFlags.None, ReceiveCallback, socket);
}
catch (Exception e)
{
//Debug.Log(e.ToString());
}
}
int length;//当前接收到有效长度
///
/// 接收信息
///
///
void ReceiveCallback(IAsyncResult ar)
{
try
{
if (clientSocket == null || !clientSocket.Connected)
{
return;
}
Socket socket = (Socket)ar.AsyncState;
//接收长度
length = socket.EndReceive(ar);
//Debug.Log("实际接收字节: " + length);
if (length <= 0)//连接失败
{
//Debug.Log("没有数据接收,数据长度为0 服务端被断掉,需要断线重连");
Closed();
}
else
{
lock (receiveData)
{
string str = Encoding.UTF8.GetString(receiveData, 0, length);
//Debug.Log("服务器发送的数据:" + str);
//本地默认receiveData为1024,避免夹入空包情况,需要复制传送过来的有效数据长度
byte[] data = new byte[length];
Array.Copy(receiveData, 0, data, 0, length);
HandleData(data);
socket.BeginReceive(receiveData, 0, receiveData.Length, SocketFlags.None, ReceiveCallback, socket);
}
}
}
catch (Exception e)
{
//Debug.Log(e.ToString());
//Debug.LogError(e.ToString());
}
}
///
/// 发送信息回调
///
///
private void SendCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
int length = socket.EndSend(ar);
//Debug.Log("已发送字节:" + length);
}
catch (Exception e)
{
//Debug.Log("e :" + e.ToString());
}
}
#endregion
#region 包处理
public List bufferList = new List();//存入缓冲区
int surPlusBodyLen = 0;//剩余接收包体长度
byte[] useData = new byte[0];//解析的数据
byte[] saveData = new byte[0];//保存的数据
int idx = 0;//处理数据次数
int headLen = 32;//包头固定长度
byte[] bufferFlag = new byte[2];//标志位
byte[] bufferSno = new byte[8];//序列号
byte[] bufferLenBody = new byte[4];//包体长度(业务数据)
byte[] bufferIdx = new byte[2];//分包索引
byte[] bufferTag = new byte[4];//业务标志
byte[] bufferCode = new byte[4];//识别码
byte[] bufferPrm = new byte[8];//传输少量参数
byte[] bufferBody = new byte[0];//真正的业务数据
string GetString(byte[] buffer)
{
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
}
///
/// 处理包
///
///
void HandleData(byte[] data)
{
//string ab = GetString(data);
//Debug.Log("fdsadf aefaef: " + ab);
if (idx == 0)//第一次处理
{
idx++;
//Debug.Log("进入处理");
SetData(data);
}
else
{
idx++;
//可重复上述第一二三种情况处理
if (surPlusBodyLen == 0)
{
//Debug.Log("进入处理");
SetData(data);
}
else if (surPlusBodyLen < 0)//长度不明确
{
useData = new byte[bufferList[0].Length + data.Length];
Array.Copy(bufferList[0], 0, useData, 0, bufferList[0].Length);
Array.Copy(data, 0, useData, bufferList[0].Length, data.Length);
//string a = GetString(useData);
bufferList.Clear();
// Debug.Log("因为长度不足而存入缓冲区,然后再跟当前的数据合并为:" + a);
SetData(useData);
}
else
{
//还有业务数据还没传过来
if (surPlusBodyLen > data.Length)
{
byte[] s_Data = data;
bufferList.Add(s_Data);
//string a = GetString(data);
surPlusBodyLen = surPlusBodyLen - data.Length;
//Debug.Log(string.Format("直接保存数据:{0},并剩余接收数据长度为:{1}", a, surPlusBodyLen));
}
//业务数据已经全部接收完,可以做并包处理了
else if (surPlusBodyLen == data.Length)
{
surPlusBodyLen = 0;
//Debug.Log(string.Format("直接并包处理,剩余接收数据长度为:{0}", surPlusBodyLen));
MegreData(data);
}
//业务数据已经接收完,但也有粘包情况
else if (surPlusBodyLen < data.Length)
{
//拆包
useData = new byte[surPlusBodyLen];
Array.Copy(data, 0, useData, 0, surPlusBodyLen);
//string a = GetString(useData);
MegreData(useData);
//Debug.Log(string.Format("剩余数据内容为:{0}", a));
//粘包中要保存的数据
saveData = new byte[data.Length - surPlusBodyLen - 2];
Array.Copy(data, surPlusBodyLen + 2, saveData, 0, saveData.Length);
//Debug.Log("savedata.length: " + saveData.Length);
//数据包为零就直接中断当前操作
if (saveData.Length <= 0)
{
surPlusBodyLen = 0;
return;
}
//空包情况
byte[] isData = new byte[1];
Array.Copy(saveData, 0, isData, 0, 1);
if (!GetString(isData).Contains("a"))//根据分隔符判断
{
surPlusBodyLen = 0;
//Debug.Log("剩余字节为空包情况");
return;
}
//又再次按照一二三情况处理
SetData(saveData);
}
}
}
receiveData = new byte[1024];
}
///
/// 拆包
///
///
void SetData(byte[] data)
{
//Debug.Log("data:" + GetString(data));
if (data.Length < headLen)//长度不足 直接保存
{
// Debug.Log("data:" + GetString(data));
saveData = data;
bufferList.Add(saveData);
data = null;
//string a = GetString(saveData);
surPlusBodyLen = -1;
//Debug.Log("包头长度不足,直接保存:" + a + "剩余长度为:" + surPlusBodyLen);
}
else
{
//获取包体(业务数据)长度
bufferLenBody = new byte[4];
Array.Copy(data, (bufferFlag.Length + bufferSno.Length), bufferLenBody, 0, 4);
//string b1 = GetString(bufferLenBody);
// Debug.Log("bodyLEn:" + b1);
//第一种情况:业务数据长度 等于 包体长度
if (int.Parse(GetString(bufferLenBody)) == data.Length - headLen)
{
useData = new byte[data.Length];
Array.Copy(data, 0, useData, 0, data.Length);
//string a = GetString(useData);
surPlusBodyLen = 0;
// Debug.Log("直接解析:" + a);
//存入队列
receiveQueue.Enqueue(useData);
}
//第二种情况:业务数据长度 大于 包体长度
else if (int.Parse(GetString(bufferLenBody)) > data.Length - headLen)
{
byte[] s_Data = data;
bufferList.Add(s_Data);
//string a = GetString(data);
surPlusBodyLen = int.Parse(GetString(bufferLenBody)) - (data.Length - headLen);
//Debug.Log(string.Format("直接保存数据:{0},并剩余接收数据长度为:{1}", a, surPlusBodyLen));
}
//第三种情况:业务数据长度 小于 包体长度
else if (int.Parse(GetString(bufferLenBody)) < data.Length - headLen)
{
useData = new byte[int.Parse(GetString(bufferLenBody)) + headLen];
Array.Copy(data, 0, useData, 0, useData.Length);
//string a = GetString(useData);
//Debug.Log("能够解析的数据:" + a);
//存入队列
receiveQueue.Enqueue(useData);
saveData = new byte[data.Length - headLen - int.Parse(GetString(bufferLenBody)) - 2];
//数据包为零就直接中断当前操作
if (saveData.Length <= 0)
{
surPlusBodyLen = 0;
return;
}
byte[] isData = new byte[2];
Array.Copy(data, useData.Length + 2, isData, 0, 2);
//Debug.Log("isData:" + GetString(isData));
if (!GetString(isData).Contains("a"))//根据分隔符判断
{
surPlusBodyLen = 0;
//Debug.Log("剩余字节为空包情况");
return;
}
if (data.Length - headLen - int.Parse(GetString(bufferLenBody)) <= headLen)//包头长度不超过32
{
Array.Copy(data, headLen + int.Parse(GetString(bufferLenBody)) + 2, saveData, 0, saveData.Length);
bufferList.Add(saveData);
//string b = GetString(saveData);
surPlusBodyLen = -1;
//Debug.Log("包头长度不足,直接保存:" + b);
}
else //再重新按照上述的一 二 三种情况处理
{
Array.Copy(data, headLen + int.Parse(GetString(bufferLenBody)) + 2, saveData, 0, saveData.Length);
//Debug.Log("进入");
SetData(saveData);
}
}
}
}
///
/// 并包
///
///
void MegreData(byte[] data)
{
int lastTotalLength = 0;//缓冲区数据的总长度
//计算总长度
for (int i = 0; i < bufferList.Count; i++)
{
lastTotalLength += bufferList[i].Length;
}
//Debug.Log("缓冲区的总长度:" + lastTotalLength);
//完整包体
byte[] bufferAll = new byte[lastTotalLength + data.Length];
//Debug.Log("缓冲区和接收收据的总长度:" + bufferAll.Length);
int tempLen = 0;//每增加一次的长度
//合并数据
for (int i = 0; i < bufferList.Count; i++)
{
//Debug.Log("tempLen:" + tempLen);
bufferList[i].CopyTo(bufferAll, tempLen);
//string b = GetString(bufferAll);
// Debug.Log("bufferAll:" + b);
tempLen += bufferList[i].Length;
}
//Debug.Log("tempLen:" + tempLen);
//最后合并接收的数据
//Debug.Log("最后的数据:" + GetString(data));
Array.Copy(data, 0, bufferAll, tempLen, data.Length);
string a = GetString(bufferAll);//解析完整的包体
bufferList.Clear();//清空缓冲区
//Debug.Log("合并后的包体:" + a);
//Debug.Log("清空后,缓冲区的数据:" + bufferList.Count);
//存入队列
receiveQueue.Enqueue(bufferAll);
}
#endregion
///
/// 心跳包
///
void Heartbeat()
{
//心跳包发送
if (clientSocket != null)
{
timerHeart += Time.deltaTime;
while (timerHeart >= 0.5f)
{
//Send(" ");
timerHeart = 0;
}
}
}
}