| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using UnityEngine;
- /// <summary>
- /// 套接字类(已封装)
- /// </summary>
- public class SocketClient
- {
- //private static SocketClient2 instance { get; set; }
- //ip地址与端口
- string address, port;
- /// <summary>
- /// 有效接收字节
- /// </summary>
- byte[] receiveData = new byte[1024];
- /// <summary>
- /// 套接字对象
- /// </summary>
- Socket clientSocket { get; set; }
- float timerHeart;//心跳包计时器
- /// <summary>
- /// 接收数据队列
- /// </summary>
- public Queue<byte[]> receiveQueue = new Queue<byte[]>();
- public SocketClient(string address, string port)
- {
- this.address = address;
- this.port = port;
- }
- #region socket通讯
- /// <summary>
- /// 连接异步socket
- /// </summary>
- 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());
- }
- }
- /// <summary>
- /// 发送消息
- /// </summary>
- /// <param name="str"></param>
- 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());
-
- }
- }
- /// <summary>
- /// 关闭socket
- /// </summary>
- 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());
-
- }
- }
- /// <summary>
- /// 成功连接后的回调
- /// </summary>
- /// <param name="asyncConnect"></param>
- 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;//当前接收到有效长度
- /// <summary>
- /// 接收信息
- /// </summary>
- /// <param name="ar"></param>
- 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());
- }
- }
-
- /// <summary>
- /// 发送信息回调
- /// </summary>
- /// <param name="ar"></param>
- 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<byte[]> bufferList = new List<byte[]>();//存入缓冲区
- 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);
- }
- /// <summary>
- /// 处理包
- /// </summary>
- /// <param name="data"></param>
- 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];
- }
- /// <summary>
- /// 拆包
- /// </summary>
- /// <param name="data"></param>
- 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);
- }
- }
- }
- }
- /// <summary>
- /// 并包
- /// </summary>
- /// <param name="receiveData"></param>
- 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
- /// <summary>
- /// 心跳包
- /// </summary>
- void Heartbeat()
- {
- //心跳包发送
- if (clientSocket != null)
- {
- timerHeart += Time.deltaTime;
- while (timerHeart >= 0.5f)
- {
- //Send(" ");
- timerHeart = 0;
- }
- }
- }
- }
|