logo资料库

unity socket 看了就学会!!!.docx

第1页 / 共8页
第2页 / 共8页
第3页 / 共8页
第4页 / 共8页
第5页 / 共8页
第6页 / 共8页
第7页 / 共8页
第8页 / 共8页
资料共8页,全文预览结束
SocketHelper 类 主要的通信类,socket 的管理放在这里 下面说一下一些主要的方法 1.连接服务器,这个都写了比较详细的注释,一看就会明白 /// /// 连接服务器 /// /// The connect. /// Server ip. /// Server port. /// Connect callback. /// Connect failed callback. public Connect(string serverIp,int void serverPort,ConnectCallback connectCallback,ConnectCallback connectFailedCallback){ connectDelegate = connectCallback; connectFailedDelegate = connectFailedCallback; //采用 TCP 方式连接 socket new = ProtocolType.Tcp); Socket(AddressFamily.InterNetwork, SocketType.Stream, //服务器 IP 地址 IPAddress address = IPAddress.Parse(serverIp); //服务器端口 IPEndPoint endpoint = new IPEndPoint(address,serverPort); //异步连接,连接成功调用 connectCallback 方法 IAsyncResult result = socket.BeginConnect(endpoint, new AsyncCallback(ConnectedCallback), socket); //这里做一个超时的监测,当连接超过 5 秒还没成功表示超时 bool success = result.AsyncWaitHandle.WaitOne(5000, true); if (!success) { //超时 Closed(); if(connectFailedDelegate != null){ connectFailedDelegate(); }
} else { } } //与 socket 建立连接成功,开启线程接受服务端数据。 isStopReceive = false; Thread thread = new Thread(new ThreadStart(ReceiveSorket)); thread.IsBackground = true; thread.Start(); 2.发送数据 首先从发送队列中取出要发送的数据,然后对数据进行序列化,转为 2 进制数据,然后在数 据最前端加上数据的长度,以便于服务器进行粘包的处理。 private void Send(){ if(socket == null){ return; } if (!socket.Connected) { Closed(); return; } try { Request req = sendDataQueue.Dequeue(); DataStream bufferWriter = new DataStream(true); req.Serialize(bufferWriter); byte[] msg = bufferWriter.ToByteArray(); byte[] buffer = new byte[msg.Length + 4]; DataStream writer = new DataStream(buffer, true); writer.WriteInt32((uint)msg.Length);//增加数据长度 writer.WriteRaw(msg); byte[] data = writer.ToByteArray(); IAsyncResult asyncSend = socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket); bool success = asyncSend.AsyncWaitHandle.WaitOne(5000, true); if (!success)
{ } Closed(); } catch (Exception e) { Debug.Log("send error : " + e.ToString()); } } 3.接收数据 接收数据是一个独立的线程,连接服务器成功后开启的。如果接收到数据,会把数据放进 DataHolder 进行处理,然后根据 DataHolder 判断是否接收到完整数据,以解决粘包等问题。 这里由于接收到数据时并没有在主线程中,所以我们先把数据放入接收队列,在主线程的 update 方法中,对数据进行解析以及分发工作。 private void ReceiveSorket(){ mDataHolder.Reset(); while(!isStopReceive){ if (!socket.Connected) { //与服务器断开连接跳出循环 Debug.Log("Failed to clientSocket server."); socket.Close(); break; } try { //接受数据保存至 bytes 当中 byte[] bytes = new byte[4096]; //Receive 方法中会一直等待服务端回发消息 //如果没有回发会一直在这里等着。 int i = socket.Receive(bytes); if (i <= 0) { socket.Close(); break;
} mDataHolder.PushData(bytes, i); while(mDataHolder.IsFinished()){ dataQueue.Enqueue(mDataHolder.mRecvData); mDataHolder.RemoveFromHead(); } } catch (Exception e) { Debug.Log("Failed to clientSocket error." + e); socket.Close(); break; } } } 这里处理接收和发送队列的数据 //接收到数据放入数据队列,按顺序取出 void Update(){ if(dataQueue.Count > 0){ Resp resp = ProtoManager.Instance.TryDeserialize(dataQueue.Dequeue()); } if(sendDataQueue.Count > 0){ Send(); } } DataHolder 此类用来管理接收到的数据,并处理粘包和丢包的情况,主要就是根据数据长度对数据进行 裁切。 通过此方法保存收到的服务器数据,放入数据缓存中 public void PushData(byte[] data, int length) { if (mRecvDataCache == null) mRecvDataCache = new byte[length]; if (this.Count + length > this.Capacity)//current capacity is not enough, enlarge the cache
{ } byte[] newArr = new byte[this.Count + length]; mRecvDataCache.CopyTo(newArr, 0); mRecvDataCache = newArr; Array.Copy(data, 0, mRecvDataCache, mTail + 1, length); mTail += length; } 此方法判断接收到的数据是否完整,这里需要服务器在数据头 4 位装入数据长度,如果接收 到完整数据,则将数据复制出一份供外部获取。 public bool IsFinished() { } if (this.Count == 0) { //skip if no data is currently in the cache return false; } if (this.Count >= 4) { DataStream reader = new DataStream(mRecvDataCache, true); packLen = (int)reader.ReadInt32(); if (packLen > 0) { if (this.Count - 4 >= packLen) { mRecvData = new byte[packLen]; Array.Copy(mRecvDataCache, 4, mRecvData, 0, packLen); return true; } return false; } return false; } return false; 如果接收到了完整的数据,则调用此方法将完整数据从数据缓存移除
public void RemoveFromHead() { int countToRemove = packLen + 4; if (countToRemove > 0 && this.Count - countToRemove > 0) { Array.Copy(mRecvDataCache, countToRemove, mRecvDataCache, 0, this.Count - countToRemove); } mTail -= countToRemove; } ProtoManager 用来管理所有协议,进行消息的解析以及分发。 所有 Resp 需要在这里注册,以便接收到数据时进行解析 public void AddProtocol(int protocol) where T: Resp, new() { if (mProtocolMapping.ContainsKey(protocol)) { mProtocolMapping.Remove(protocol); } mProtocolMapping.Add(protocol, (stream) => { T data = new T(); data.Deserialize(stream); return data; }); } 这里注册相应协议的代理,在接收到服务器数据后,会调用这里注册过的相应代理 /// /// 添加代理,在接受到服务器数据时会下发数据 /// /// Protocol. /// D. public void AddRespDelegate(int protocol,responseDelegate d){ List dels ; if (mDelegateMapping.ContainsKey(protocol)) {
dels = mDelegateMapping[protocol]; for(int i = 0 ; i < dels.Count ; i ++){ if(dels[i] == d){ return; } } }else{ dels = new List(); mDelegateMapping.Add(protocol,dels); } dels.Add(d); } 接收到服务器数据后,服务器会调用此方法对数据进行解析并调用相应的代理方法 [csharp] view plain copy public Resp TryDeserialize(byte[] buffer) { DataStream stream = new DataStream(buffer, true); int protocol = stream.ReadSInt32(); Resp ret = null; if (mProtocolMapping.ContainsKey(protocol)) { ret = mProtocolMapping[protocol](stream); if(ret != null){ if(mDelegateMapping.ContainsKey(protocol)){ List dels = mDelegateMapping[protocol]; for(int i = 0 ; i < dels.Count ; i ++){ dels[i](ret); } } } }else{ Debug.Log("no register protocol : " + protocol +"!please reg to RegisterR esp."); } return ret; } DataStream 此类进行发送以及接收的数据流的处理,包含了数据大小端的设置以及数据流的读取和写入
方法。 Request 基础的请求消息,所有要发送的请求消息继承此类 子类必须实现协议获取的方法,返回此请求所对应的协议 public virtual int GetProtocol(){ Debug.LogError("can't get Protocol"); return 0; } 序列化方法 public virtual void Serialize(DataStream writer) { writer.WriteSInt32(GetProtocol()); writer.WriteByte(0); } Resp 基础的返回消息,所有服务器返回的消息继承此类 子类必须实现协议获取的方法,返回此请求所对应的协议 public virtual int GetProtocol(){ Debug.LogError("can't get Protocol"); return 0; } 反序列化方法 public virtual void Deserialize(DataStream reader) { } 主要内容就这些,有哪些不明白的地方可以留言,有哪些不足或者可以改进的地方还请多多 指教 之后我会更新一片实际应用的文章,以及服务器端的相关内容。 --------------------- 作者:刘峰 1011 来源:CSDN 原文:https://blog.csdn.net/zgjllf1011/article/details/79213861?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!
分享到:
收藏