unity3d研究院之使用socket與http連接服務(wù)器傳輸數(shù)據(jù)包_第1頁
unity3d研究院之使用socket與http連接服務(wù)器傳輸數(shù)據(jù)包_第2頁
unity3d研究院之使用socket與http連接服務(wù)器傳輸數(shù)據(jù)包_第3頁
unity3d研究院之使用socket與http連接服務(wù)器傳輸數(shù)據(jù)包_第4頁
unity3d研究院之使用socket與http連接服務(wù)器傳輸數(shù)據(jù)包_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、Unity3D之C#使用 Socket 與 HTTP 連接服務(wù)器傳輸數(shù)據(jù)包(2013-04-29 11:13:17):分類:Unity3dit,代碼:using UnityEngine;using System.Collections; using System;using System.Threading; using System.Text; using System.Net;using System.Net.Sockets;using System.Collections.Generic; using System.IO;using System.Runtime.eropServi; u

2、sing System.Runtime.Serialization;using System.Runtime.Serialization.Formatters.Binary;From:htt/android-497-1.html(只為本人學(xué)習(xí),感謝本文作者) 1.SocketSocket 不要寫在上,如果寫在上場景一旦切換,那么這條會被掉, Socket 會斷開連接。場景切換完畢后需要重新在與服務(wù)器建立 Socket 連接,這樣會很麻 煩。所以需要把 Socket 寫在一個單例的類中,不用繼承 MonoBehaviour。這個例子我模擬一下,主角在中移動,時時向服務(wù)端發(fā)送當(dāng)前坐標(biāo),當(dāng)服務(wù)器返回

3、同步坐標(biāo)時角色開始同步服務(wù)端新角色坐標(biāo)。Socket 在發(fā)送消息的時候采用的是字節(jié)數(shù)組,也就是說無論你的數(shù)據(jù)是float short object 都會將這些數(shù)據(jù)類型先轉(zhuǎn)換成 byte ,目前在處理發(fā)送的地方我使用的是數(shù)據(jù)包也就是把(角色坐標(biāo))結(jié)構(gòu)體 object 轉(zhuǎn)換成 byte發(fā)送,這就牽扯一個問題,如何把結(jié)構(gòu)體轉(zhuǎn)成字節(jié)數(shù)組,如何把字節(jié)數(shù)組回轉(zhuǎn)成結(jié)構(gòu)體。請大家接續(xù)閱讀,就在后面,哇咔咔。直接上代碼JFSocket.cs 該單例類不要綁定在任何對象上/public class JFSocket/Socket 客戶端對象private Socket cntSocket;/JFPackage.W

4、orldPackage 是我封裝的結(jié)構(gòu)體,/在與服務(wù)器交互的時候會傳遞這個結(jié)構(gòu)體/當(dāng)客戶端接到到服務(wù)器返回的數(shù)據(jù)包時,我把結(jié)構(gòu)體 add public List worldpackage;/單例模式存在鏈表中。private sic JFSockettance;tance()public sic JFSocket Getif (tance = null)tance = new JFSocket();returntance;/單例的構(gòu)造函數(shù) JFSocket()/創(chuàng)建 Socket 對象,這里連接類型是 TCPcntSocket = new Socket(AddressFamily.erNetw

5、ork,SocketType.Stre/服務(wù)器 IP 地址rotocolType.Tcp);IPAddress ipAddress = IPAddress.Parse (00);/服務(wù)器端口IPEndPoipEndpo= new IPEndPo(ipAddress, 10060);/這是一個異步的建立連接,當(dāng)連接建立成功時調(diào)用 connectCallback 方法IAsyncResultresult = cntSocket.BeginConnect (ipEndpo,new AsyncCallback(connectCallback),cntSocket);/這里做一個超時的監(jiān)測,當(dāng)連接超過

6、5 秒還沒成功表示超時bool sucs = result.AsyncWaindle.WaitOne( 5000, true );if ( !suc/超時 Closed();s )Debug.Log(connect Time Out);else/與 socket 建立連接成功,開啟線程接受服務(wù)端數(shù)據(jù)。worldpackage = new List();Thread thread = new Thread(new ThreadStart(ReceiveSorket); thread.IsBackground = true;thread.Start();private void connectCa

7、llback(IAsyncResultasyncConnect) Debug.Log(connectSuc;s)private void ReceiveSorket()/在這個線程中接受服務(wù)器返回的數(shù)據(jù) while (true) if(!c)ntSocket.Connected/與服務(wù)器斷開連接跳出循環(huán)Debug.Log(Failed to cntSocket server.);cntSocket.Close();break;try/接受數(shù)據(jù)保存至 bytes 當(dāng)中byte bytes = new byte4096;/Receive 方法中會一直等待服務(wù)端回發(fā)消息/如果沒有回發(fā)會一直在這里等著

8、。i = cntSocket.Receive(bytes); if(i 2) SplitPackage(bytes,0);elseDebug.Log(length is notcatch (Exception e)Debug.Log(Failed to c 2);ntSocket error.+ e);cntSocket.Close();break;private void SplitPackage(byte bytes ,index)/在這里進行拆包,因為一次返回的數(shù)據(jù)包的數(shù)量是不定的/所以需要給數(shù)據(jù)包進行查分。 while(true)/包頭是 2 個字節(jié)byte head = new by

9、te2; headLengthIndex = index + 2;/把數(shù)據(jù)包的前兩個字節(jié)拷貝出來 Array.Copy(bytes,index,head,0,2);/計算包頭的長度short length = BitConverter.To16(head,0);/當(dāng)包頭的長度大于 0 那么需要依次把相同長度的 byte 數(shù)組拷貝出來if(length 0)byte data = new byength;/拷貝出這個包的全部字節(jié)數(shù)Array Copy(bytes headLengthIndex data 0 length);/把數(shù)據(jù)包中的字節(jié)數(shù)組強制轉(zhuǎn)換成數(shù)據(jù)包的結(jié)構(gòu)體/BytesToStruc

10、t()方法就是用來轉(zhuǎn)換的/這里需要和的服務(wù)端程序商量,JFPackage.WorldPackage wp = new JFPackage.WorldPackage();wp = (JFPackage.WorldPackage)BytesToStruct(data,wp.GetType();/把每個包的結(jié)構(gòu)體對象添加至鏈表中。 worldpackage.Add(wp);/將索引指向下一個包的包頭index = headLengthIndex + length;else/如果包頭為 0 表示沒有包了,那么跳出循環(huán)break;/向服務(wù)端發(fā)送一條字符串/一般不會發(fā)送字符串應(yīng)該是發(fā)送數(shù)據(jù)包publicb

11、yteif(!c cvoid SendMessage(string str)msg = Encoding.UTF8.GetBytes(str);ntSocket.Connected)ntSocket.Close(); return;try/i = cntSocked(msg);IAsyncResult asyncSend = cntSocket.Begend(msg,0,msg.Length,SocketFlags.None,new AsyncCallback(sendCallback),cntSocket);bool if ( c;sucs = asyncSend.AsyncWaindle.

12、WaitOne( 5000, true );!sucs )ntSocket.Close()Debug.Log(Failed to SendMessage server.);,catchDebug.Log(send message error );/向服務(wù)端發(fā)送數(shù)據(jù)包,也就是一個結(jié)構(gòu)體對象 public void SendMessage(object obj) if(!cntSocket.Connected) cntSocket.Close(); return;try/先得到數(shù)據(jù)包的長度short size = (short)Marshal.SizeOf(obj);/把數(shù)據(jù)包的長度寫入 byte

13、 數(shù)組中byte head = BitConverter.GetBytes(size);/把結(jié)構(gòu)體對象轉(zhuǎn)換成數(shù)據(jù)包,也就是字節(jié)數(shù)組 byte data = StructToBytes(obj);/此時就有了兩個字節(jié)數(shù)組,一個是標(biāo)記數(shù)據(jù)包的長度字節(jié)數(shù)組,一個是數(shù)據(jù)包字節(jié)數(shù)組/同時把這兩個字節(jié)數(shù)組合并成一個字節(jié)數(shù)組byte newByte = new bytehead.Length + data.Length; Array.Copy(head,0,newByte,0,head.Length); Array.Copy(data,0,newByte,head.Length, data.Length);

14、/計算出新的字節(jié)數(shù)組的長度length = Marshal.SizeOf(size) + Marshal.SizeOf(obj);/向服務(wù)端異步發(fā)送這個字節(jié)數(shù)組IAsyncResult asyncSend = cntSocket.Begend (newByte,0,length,SocketFlags.None,new AsyncCallback (sendCallback),cntSocket);/監(jiān)測超時bool sucs = asyncSend.AsyncWaindle.WaitOne( 5000, true ); if ( !sucs )cntSocket.Close();Debug.

15、Log(Time Out !);catch (Exception e)Debug.Log(send message error: + e );/結(jié)構(gòu)體轉(zhuǎn)字節(jié)數(shù)組public byte StructToBytes(object structObj)size = Marshal.SizeOf(structObj);Ptr buffer = Marshal.AllocHGlobal(size); try Marshal.StructureToPtr(structObj,buffer,false); byte bytes = new bytesize; Marshal.Copy(buffer, byt

16、es,0,size); return bytes;finally Marshal.FreeHGlobal(buffer);/字節(jié)數(shù)組轉(zhuǎn)結(jié)構(gòu)體public object BytesToStruct(byte bytes, Type strcutType)size = Marshal.SizeOf(strcutType);Ptr buffer = Marshal.AllocHGlobal(size); try Marshal.Copy(bytes,0,buffer,size);return Marshal.PtrToStructure(buffer, strcutType);finally為了與服

17、務(wù)端達成默契,判斷數(shù)據(jù)包是否完成。需要在數(shù)據(jù)包中定義包頭,包頭一般把兩個數(shù)據(jù)類型 short是這個數(shù)據(jù)包的長度,也就是結(jié)構(gòu)體對象的長度。正如代碼中和 object 合并成一個新的字節(jié)數(shù)組。然后是數(shù)據(jù)包結(jié)構(gòu)體的定義,需要注意如果你在做 IOS 和 Android 的話數(shù)據(jù)包中不要包含數(shù)組,不然在結(jié)構(gòu)體轉(zhuǎn)換 byte 數(shù)組的時候會出錯。Marshal.StructureToPtr () error : Attempting to JIT compile methodJFPackage.cs代碼:usingUnityEngine;usingSystem.Collections;usingSystem

18、.Runtime.eropServi;public class JFPackageMarshal.FreeHGlobal(buffer);private void sendCallback (IAsyncResult asyncSend)/關(guān)閉 Socketpublic void Closed()if(cntSocket != null & cntSocket.Connected) cntSocket.Shutdown(SocketShutdown.Both); cntSocket.Close();cntSocket = null;/結(jié)構(gòu)體序列化System.Serializable/4 字節(jié)

19、對齊和 android 上可以 1 字節(jié)對齊StructLayout(LayoutKind.Sequential,Pack=4)public structWorldPackagepublicbytemEqui;publicbytemAnimationID;publicbytemHP;publicshortmx;publicshortmy;publicshortmz;publicshortmRosx;publicshortmRosy;publicshortmRosz;public WorldPackage(shortx,shorty,shortz, shortrosx,shortrosy,sho

20、rt rosz,byte equi,byteanimationID,byte hp)mx=x;my=y;mz=z;mRosx=rosx;mRosy=rosy;mRosz=rosz;mEqui= equi;mAnimationID = animationID;mHP = hp;中執(zhí)行發(fā)送數(shù)據(jù)包的動作,在 Start 方法中得到 Socket 對象。在代碼:public JFSocket mJFsorket; void Start () mJFsorket =JFSocket.Gettance(); 讓角色發(fā)生移動的時候,調(diào)用該方法向服務(wù)端發(fā)送數(shù)據(jù)。代碼:void SendPlayerWorldM

21、essage() /組成新的結(jié)構(gòu)體對象,包括主角坐標(biāo)旋轉(zhuǎn)等。 Vector3 PlayerTransform = transform.localition; Vector3PlayerRoion = transform.localRoion.eulerAngles; /用 short 的話是 2 字節(jié),為了節(jié)省包的長度。這里乘以 100 避免使用 float 4 字節(jié)。當(dāng)服務(wù)器接受到的時候小數(shù)點向前移動兩位就是真實的 float 數(shù)據(jù) short px =(short)(PlayerTransform.x*100);shortpy=(short)(PlayerTransform.y*100);

22、shortpz=(short)(PlayerTransform.z*100);shortrx=(short)(PlayerRoion.x*100);short ry =(short)(PlayerRoion.y*100);short rz =(short)(PlayerRoion.z*100);byte equi= 1; byte animationID =9;byte hp = 2; JFPackage.WorldPackage wordPackage = newJFPackage.WorldPackage(px,py,pz,rx,ry,rz,equi,animationID,hp); /通

23、過 Socket 發(fā)送結(jié)構(gòu)體對象 mJFsorkedMessage(wordPackage); 接著就是客戶端同步服務(wù)器的數(shù)據(jù),目前是測試階段所以寫的比較簡陋,不過原理都是一樣的。代碼:/上次同步時間 private float mSynchronous; void Update () mSynchronous +=Time.delime; /在 Update 中每 0.5s 的時候同步一次if(mSynchronous 0.5f) count = mJFsorket.worldpackage.Count;/當(dāng)接受到的數(shù)據(jù)包長度大于0 開始同步 if(count 0) /遍歷數(shù)據(jù)包中每個點的坐

24、標(biāo) foreach(JFPackage.WorldPackage wp inmJFsorket.worldpackage) float x = (float)(wp.mx / 100.0f); floaty = (float)(wp.my/100.0f); float z =(float)(wp.mz /100.0f);Debug.Log(x = + x+ y = + y+ z = + z); /同步主角的新坐標(biāo)mPlayer.transform.ition = new Vector3(x,y,z); /清空數(shù)據(jù)包鏈表mJFsorket.worldpackage.Clear(); mSynch

25、ronous = 0; 主角移動的同時,通過 Socket 時時同步坐標(biāo)喔。有沒有感覺這個牛頭人非常帥氣 哈哈哈。對于 Socket 的使用,我相信沒有比 MSDN 更加詳細(xì)的了。 有關(guān) Socket 同步請求異步請求的地方可以參照 MSDN地址給出來了,好好學(xué)習(xí)吧,嘿嘿。 HYPERLINK http:/m/ http:/m/library/.sockets.socket.aspx上述代碼中我使用的是 Thread() 沒有使用協(xié)同任務(wù) StartCoroutine() ,原因是協(xié)同任務(wù)必需要繼承 MonoBehaviour,并且該要綁定在對象身上。問題綁定在對象身上,那么 Socket 肯

26、定會斷開連接,所以我需要用切換場景的時候這個必然會Thread,并且協(xié)同任務(wù)它并不是嚴(yán)格意義上的多線程。2.HTTPHTTP 請求在 Unity 我相信用的會更少一些,因為 HTTP 會比 SOCKET 慢很多,因為它每次請求完都會斷開。廢話不說了,我用 HTTP 請求制作用戶的登錄。用 HTTP 請求直接使用 Unity 自帶的 www 類就可以, 為 HTTP 請求只有登錄才會有,所以我就在中來完成,使用 www 類 和 協(xié)同任務(wù) StartCoroutine()。代碼:using UnityEngine; using System.Collections; usingSystem.Collections.Generic; public class LoginGlobe : MonoBehaviour void Start () /GET請求StartCoroutine(GET(htt/); void Update () v

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論