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; } } } }