版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
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 不要寫在上,如果寫在上場(chǎng)景一旦切換,那么這條會(huì)被掉, Socket 會(huì)斷開連接。場(chǎng)景切換完畢后需要重新在與服務(wù)器建立 Socket 連接,這樣會(huì)很麻 煩。所以需要把 Socket 寫在一個(gè)單例的類中,不用繼承 MonoBehaviour。這個(gè)例子我模擬一下,主角在中移動(dòng),時(shí)時(shí)向服務(wù)端發(fā)送當(dāng)前坐標(biāo),當(dāng)服務(wù)器返回
3、同步坐標(biāo)時(shí)角色開始同步服務(wù)端新角色坐標(biāo)。Socket 在發(fā)送消息的時(shí)候采用的是字節(jié)數(shù)組,也就是說無論你的數(shù)據(jù)是float short object 都會(huì)將這些數(shù)據(jù)類型先轉(zhuǎn)換成 byte ,目前在處理發(fā)送的地方我使用的是數(shù)據(jù)包也就是把(角色坐標(biāo))結(jié)構(gòu)體 object 轉(zhuǎn)換成 byte發(fā)送,這就牽扯一個(gè)問題,如何把結(jié)構(gòu)體轉(zhuǎn)成字節(jié)數(shù)組,如何把字節(jié)數(shù)組回轉(zhuǎn)成結(jié)構(gòu)體。請(qǐng)大家接續(xù)閱讀,就在后面,哇咔咔。直接上代碼JFSocket.cs 該單例類不要綁定在任何對(duì)象上/public class JFSocket/Socket 客戶端對(duì)象private Socket cntSocket;/JFPackage.W
4、orldPackage 是我封裝的結(jié)構(gòu)體,/在與服務(wù)器交互的時(shí)候會(huì)傳遞這個(gè)結(jié)構(gòu)體/當(dāng)客戶端接到到服務(wù)器返回的數(shù)據(jù)包時(shí),我把結(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 對(duì)象,這里連接類型是 TCPcntSocket = new Socket(AddressFamily.erNetw
5、ork,SocketType.Stre/服務(wù)器 IP 地址rotocolType.Tcp);IPAddress ipAddress = IPAddress.Parse (00);/服務(wù)器端口IPEndPoipEndpo= new IPEndPo(ipAddress, 10060);/這是一個(gè)異步的建立連接,當(dāng)連接建立成功時(shí)調(diào)用 connectCallback 方法IAsyncResultresult = cntSocket.BeginConnect (ipEndpo,new AsyncCallback(connectCallback),cntSocket);/這里做一個(gè)超時(shí)的監(jiān)測(cè),當(dāng)連接超過
6、5 秒還沒成功表示超時(shí)bool sucs = result.AsyncWaindle.WaitOne( 5000, true );if ( !suc/超時(shí) 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()/在這個(gè)線程中接受服務(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 方法中會(huì)一直等待服務(wù)端回發(fā)消息/如果沒有回發(fā)會(huì)一直在這里等著
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)/在這里進(jìn)行拆包,因?yàn)橐淮畏祷氐臄?shù)據(jù)包的數(shù)量是不定的/所以需要給數(shù)據(jù)包進(jìn)行查分。 while(true)/包頭是 2 個(gè)字節(jié)byte head = new by
9、te2; headLengthIndex = index + 2;/把數(shù)據(jù)包的前兩個(gè)字節(jié)拷貝出來 Array.Copy(bytes,index,head,0,2);/計(jì)算包頭的長(zhǎng)度short length = BitConverter.To16(head,0);/當(dāng)包頭的長(zhǎng)度大于 0 那么需要依次把相同長(zhǎng)度的 byte 數(shù)組拷貝出來if(length 0)byte data = new byength;/拷貝出這個(gè)包的全部字節(jié)數(shù)Array Copy(bytes headLengthIndex data 0 length);/把數(shù)據(jù)包中的字節(jié)數(shù)組強(qiáng)制轉(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();/把每個(gè)包的結(jié)構(gòu)體對(duì)象添加至鏈表中。 worldpackage.Add(wp);/將索引指向下一個(gè)包的包頭index = headLengthIndex + length;else/如果包頭為 0 表示沒有包了,那么跳出循環(huán)break;/向服務(wù)端發(fā)送一條字符串/一般不會(huì)發(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ù)包,也就是一個(gè)結(jié)構(gòu)體對(duì)象 public void SendMessage(object obj) if(!cntSocket.Connected) cntSocket.Close(); return;try/先得到數(shù)據(jù)包的長(zhǎng)度short size = (short)Marshal.SizeOf(obj);/把數(shù)據(jù)包的長(zhǎng)度寫入 byte
13、 數(shù)組中byte head = BitConverter.GetBytes(size);/把結(jié)構(gòu)體對(duì)象轉(zhuǎn)換成數(shù)據(jù)包,也就是字節(jié)數(shù)組 byte data = StructToBytes(obj);/此時(shí)就有了兩個(gè)字節(jié)數(shù)組,一個(gè)是標(biāo)記數(shù)據(jù)包的長(zhǎng)度字節(jié)數(shù)組,一個(gè)是數(shù)據(jù)包字節(jié)數(shù)組/同時(shí)把這兩個(gè)字節(jié)數(shù)組合并成一個(gè)字節(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、/計(jì)算出新的字節(jié)數(shù)組的長(zhǎng)度length = Marshal.SizeOf(size) + Marshal.SizeOf(obj);/向服務(wù)端異步發(fā)送這個(gè)字節(jié)數(shù)組IAsyncResult asyncSend = cntSocket.Begend (newByte,0,length,SocketFlags.None,new AsyncCallback (sendCallback),cntSocket);/監(jiān)測(cè)超時(shí)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ù)端達(dá)成默契,判斷數(shù)據(jù)包是否完成。需要在數(shù)據(jù)包中定義包頭,包頭一般把兩個(gè)數(shù)據(jù)類型 short是這個(gè)數(shù)據(jù)包的長(zhǎng)度,也就是結(jié)構(gòu)體對(duì)象的長(zhǎng)度。正如代碼中和 object 合并成一個(gè)新的字節(jié)數(shù)組。然后是數(shù)據(jù)包結(jié)構(gòu)體的定義,需要注意如果你在做 IOS 和 Android 的話數(shù)據(jù)包中不要包含數(shù)組,不然在結(jié)構(gòu)體轉(zhuǎn)換 byte 數(shù)組的時(shí)候會(huì)出錯(cuò)。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、對(duì)齊和 android 上可以 1 字節(jié)對(duì)齊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ù)包的動(dòng)作,在 Start 方法中得到 Socket 對(duì)象。在代碼:public JFSocket mJFsorket; void Start () mJFsorket =JFSocket.Gettance(); 讓角色發(fā)生移動(dòng)的時(shí)候,調(diào)用該方法向服務(wù)端發(fā)送數(shù)據(jù)。代碼:void SendPlayerWorldM
21、essage() /組成新的結(jié)構(gòu)體對(duì)象,包括主角坐標(biāo)旋轉(zhuǎn)等。 Vector3 PlayerTransform = transform.localition; Vector3PlayerRoion = transform.localRoion.eulerAngles; /用 short 的話是 2 字節(jié),為了節(jié)省包的長(zhǎng)度。這里乘以 100 避免使用 float 4 字節(jié)。當(dāng)服務(wù)器接受到的時(shí)候小數(shù)點(diǎn)向前移動(dòng)兩位就是真實(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)體對(duì)象 mJFsorkedMessage(wordPackage); 接著就是客戶端同步服務(wù)器的數(shù)據(jù),目前是測(cè)試階段所以寫的比較簡(jiǎn)陋,不過原理都是一樣的。代碼:/上次同步時(shí)間 private float mSynchronous; void Update () mSynchronous +=Time.delime; /在 Update 中每 0.5s 的時(shí)候同步一次if(mSynchronous 0.5f) count = mJFsorket.worldpackage.Count;/當(dāng)接受到的數(shù)據(jù)包長(zhǎng)度大于0 開始同步 if(count 0) /遍歷數(shù)據(jù)包中每個(gè)點(diǎn)的坐
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; 主角移動(dòng)的同時(shí),通過 Socket 時(shí)時(shí)同步坐標(biāo)喔。有沒有感覺這個(gè)牛頭人非常帥氣 哈哈哈。對(duì)于 Socket 的使用,我相信沒有比 MSDN 更加詳細(xì)的了。 有關(guān) Socket 同步請(qǐng)求異步請(qǐng)求的地方可以參照 MSDN地址給出來了,好好學(xué)習(xí)吧,嘿嘿。 HYPERLINK http:/m/ http:/m/library/.sockets.socket.aspx上述代碼中我使用的是 Thread() 沒有使用協(xié)同任務(wù) StartCoroutine() ,原因是協(xié)同任務(wù)必需要繼承 MonoBehaviour,并且該要綁定在對(duì)象身上。問題綁定在對(duì)象身上,那么 Socket 肯
26、定會(huì)斷開連接,所以我需要用切換場(chǎng)景的時(shí)候這個(gè)必然會(huì)Thread,并且協(xié)同任務(wù)它并不是嚴(yán)格意義上的多線程。2.HTTPHTTP 請(qǐng)求在 Unity 我相信用的會(huì)更少一些,因?yàn)?HTTP 會(huì)比 SOCKET 慢很多,因?yàn)樗看握?qǐng)求完都會(huì)斷開。廢話不說了,我用 HTTP 請(qǐng)求制作用戶的登錄。用 HTTP 請(qǐng)求直接使用 Unity 自帶的 www 類就可以, 為 HTTP 請(qǐng)求只有登錄才會(huì)有,所以我就在中來完成,使用 www 類 和 協(xié)同任務(wù) StartCoroutine()。代碼:using UnityEngine; using System.Collections; usingSystem.Collections.Generic; public class LoginGlobe : MonoBehaviour void Start () /GET請(qǐng)求StartCoroutine(GET(htt/); void Update () v
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- YC/T 617-2024卷煙質(zhì)量市場(chǎng)反饋信息分析與處理規(guī)范
- 2025版借款墊資風(fēng)險(xiǎn)控制合作協(xié)議范本3篇
- 2025年度智能電網(wǎng)項(xiàng)目可研咨詢服務(wù)協(xié)議正范文本3篇
- 學(xué)?;S池維修工程協(xié)議
- 2025版文化旅游項(xiàng)目建議書編制及運(yùn)營(yíng)管理合同3篇
- 徒步班組施工合同
- 保險(xiǎn)服務(wù)標(biāo)準(zhǔn)化管理辦法
- 通信設(shè)備招投標(biāo)法規(guī)解析
- 電子產(chǎn)品采購(gòu)招投標(biāo)改進(jìn)策略
- 商業(yè)廣場(chǎng)施工合作協(xié)議
- 豬場(chǎng)配懷工作安排方案設(shè)計(jì)
- GB/T 2-2016緊固件外螺紋零件末端
- GB/T 12467.5-2009金屬材料熔焊質(zhì)量要求第5部分:滿足質(zhì)量要求應(yīng)依據(jù)的標(biāo)準(zhǔn)文件
- GB 17740-1999地震震級(jí)的規(guī)定
- 安全生產(chǎn)事故舉報(bào)獎(jiǎng)勵(lì)制度
- 冠心病健康教育完整版課件
- 永久避難硐室安裝施工組織措施
- 元旦節(jié)前安全教育培訓(xùn)-教學(xué)課件
- 國(guó)家開放大學(xué)《理工英語(yǔ)1》單元自測(cè)8試題答案
- 芯片工藝流程課件1
- 人教版八年級(jí)下冊(cè)生物期末測(cè)試卷帶答案
評(píng)論
0/150
提交評(píng)論