本文節(jié)選自洪流學(xué)堂公眾號(hào)技術(shù)專(zhuān)欄《大話Unity2019》糙申,未經(jīng)允許不可轉(zhuǎn)載。
洪流學(xué)堂公眾號(hào)回復(fù)專(zhuān)欄
船惨,查看更多專(zhuān)欄文章柜裸。
洪流學(xué)堂,讓你快人幾步粱锐。你好疙挺,我是鄭洪智。
大智:“小新怜浅,今天開(kāi)始铐然,我們來(lái)學(xué)習(xí)Unity中的HTTP通信《褡”
小新:“為啥不先學(xué)Tcp和Udp呢搀暑?這倆不是基礎(chǔ)么?”
大智:“雖然這倆是基礎(chǔ)跨琳,但是難度也更大自点。Http是應(yīng)用層協(xié)議,使用起來(lái)會(huì)比較方便脉让,可以快速入門(mén)桂敛。”
Unity中的HTTP編程
Unity中的HTTP通信主要依賴(lài)的是Unity自帶的UnityWebRequest類(lèi)(雖然也有一些第三方插件溅潜,但一般使用這個(gè)就足夠了)术唬。
小新:“UnityWebRequest類(lèi)我們之前學(xué)過(guò)呀,是不是復(fù)習(xí)一下就行啦”
大智:“之前我們使用UnityWebRequest類(lèi)主要用file協(xié)議來(lái)加載本地的文件滚澜,和真正的HTTP請(qǐng)求還有所不同粗仓。”
我們用一個(gè)最簡(jiǎn)單的HTTP請(qǐng)求,來(lái)學(xué)習(xí)整個(gè)請(qǐng)求的流程借浊。
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class UnityPageRequest : MonoBehaviour
{
// Start is called before the first frame update
IEnumerator Start()
{
var url = "https://www.baidu.com";
var www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if (www.isHttpError || www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
Debug.Log(www.downloadHandler.text);
}
}
}
HTTP是請(qǐng)求-響應(yīng)模型眶掌,所以通信的過(guò)程主要分為請(qǐng)求部分和響應(yīng)部分。
HTTP請(qǐng)求(Request)
客戶(hù)端發(fā)送一個(gè)HTTP請(qǐng)求到服務(wù)器的請(qǐng)求消息包括以下格式:
請(qǐng)求行(request line)巴碗、請(qǐng)求頭部(header)朴爬、空行和請(qǐng)求數(shù)據(jù)四個(gè)部分組成。
HTTP請(qǐng)求方法
本例中我們使用了UnityWebRequest.Get(url)
來(lái)獲取對(duì)應(yīng)的信息橡淆。
這里的Get方法對(duì)應(yīng)了HTTP方法中的GET召噩。
什么是HTTP方法呢?
根據(jù)HTTP標(biāo)準(zhǔn)逸爵,HTTP請(qǐng)求可以使用多種請(qǐng)求方法具滴。
HTTP1.0定義了三種請(qǐng)求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五種請(qǐng)求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法师倔。
- GET 請(qǐng)求指定的頁(yè)面信息构韵,并返回實(shí)體主體。
- POST 向指定資源提交數(shù)據(jù)進(jìn)行處理請(qǐng)求(例如提交表單或者上傳文件)趋艘。數(shù)據(jù)被包含在請(qǐng)求體中疲恢。POST請(qǐng)求可能會(huì)導(dǎo)致新的資源的建立和/或已有資源的修改。
- HEAD 類(lèi)似于get請(qǐng)求瓷胧,只不過(guò)返回的響應(yīng)中沒(méi)有具體的內(nèi)容显拳,用于獲取報(bào)頭
- PUT 從客戶(hù)端向服務(wù)器傳送的數(shù)據(jù)取代指定的文檔的內(nèi)容。
- DELETE 請(qǐng)求服務(wù)器刪除指定的頁(yè)面搓萧。
- CONNECT HTTP/1.1協(xié)議中預(yù)留給能夠?qū)⑦B接改為管道方式的代理服務(wù)器杂数。
- OPTIONS 允許客戶(hù)端查看服務(wù)器的性能。
- TRACE 回顯服務(wù)器收到的請(qǐng)求瘸洛,主要用于測(cè)試或診斷揍移。
通常我們用的比較多的是GET和POST方法。GET一般用來(lái)獲取信息反肋,POST一般用來(lái)向服務(wù)器上傳信息那伐,比如表單、文件等囚玫。
UnityWebRequest類(lèi)中封裝了幾種HTTP方法的高級(jí)操作:
- GET對(duì)應(yīng)
UnityWebRequest.Get
- POST對(duì)應(yīng)
UnityWebRequest.Post
- HEAD對(duì)應(yīng)
UnityWebRequest.Head
- PUT對(duì)應(yīng)
UnityWebRequest.Put
- DELETE對(duì)應(yīng)
UnityWebRequest.Delete
此外喧锦,還能使用底層的API來(lái)手動(dòng)創(chuàng)建UnityWebRequest读规。
URL
上面的幾行代碼請(qǐng)求了https://www.baidu.com
這一URL對(duì)應(yīng)的資源抓督,返回的數(shù)據(jù)是一個(gè)html文件的文本內(nèi)容。
URL我們上一節(jié)中學(xué)過(guò)束亏,它是一個(gè)資源地址铃在,就本例來(lái)說(shuō):
- 協(xié)議部分:該URL的協(xié)議部分為“https:”,這代表網(wǎng)頁(yè)使用的是HTTPS協(xié)議,S代表Secure定铜,安全的阳液,是一種加密傳輸?shù)腍TTP協(xié)議,在"https"后面的“//”為分隔符揣炕。
- 域名部分:該URL的域名部分為“www.baidu.com”帘皿。一個(gè)URL中,也可以使用IP地址作為域名使用
- 端口部分:端口不是一個(gè)URL必須的部分畸陡,如果省略鹰溜,將采用默認(rèn)端口80。本例中就省略了端口號(hào)丁恭。
- 虛擬目錄部分:從域名后的第一個(gè)“/”開(kāi)始到最后一個(gè)“/”為止曹动,是虛擬目錄部分。虛擬目錄也不是一個(gè)URL必須的部分牲览。本例中的虛擬目錄是根目錄“/”
- 文件名部分:文件名部分也不是一個(gè)URL必須的部分墓陈,可以在服務(wù)端配置和開(kāi)發(fā)。
如果你不參與服務(wù)端開(kāi)發(fā)的話第献,一般只需要知道URL地址是什么就可以了贡必。
HTTP headers(頭部)
請(qǐng)求頭部經(jīng)常用來(lái)設(shè)置請(qǐng)求的一些屬性參數(shù)。
比如我們想告訴服務(wù)器給我們返回json格式的數(shù)據(jù)庸毫,通常要設(shè)置:
Content-type:application/json
在Unity中需要這么設(shè)置:
www.SetRequestHeader("Content-Type", "application/json");
請(qǐng)求數(shù)據(jù)
有些請(qǐng)求方法如POST和PUT還可以攜帶請(qǐng)求數(shù)據(jù)份氧,通常用于上傳文件。
在Unity中懈涛,請(qǐng)求數(shù)據(jù)通常以這種方式添加端仰,例如上傳一張圖片:
IEnumerator UploadTexture()
{
var tex = new Texture2D(1,1);
tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
tex.Apply();
// tex.Apply() 對(duì)性能影響較大,故等待一幀再執(zhí)行
yield return null;
var bytes = tex.EncodeToPNG();
var form = new WWWForm();
form.AddBinaryData("screenshot", bytes);
var www = UnityWebRequest.Post("server url", form);
yield return www.SendWebRequest();
// ...
}
HTTP響應(yīng)(Response)
Http請(qǐng)求的結(jié)果都是字節(jié)流盒揉。
那么為什么我們能看到在瀏覽器上有的能呈現(xiàn)文字晋被,還有圖片、視頻呢刚盈?
這是因?yàn)轫憫?yīng)結(jié)果中都有Content-Type報(bào)頭羡洛,瀏覽器會(huì)根據(jù)報(bào)頭來(lái)解析接收到的數(shù)據(jù),顯示為不同的內(nèi)容藕漱。
HTTP狀態(tài)碼
HTTP響應(yīng)的第一行會(huì)顯示使用的協(xié)議以及狀態(tài)碼欲侮。
狀態(tài)代碼有三位數(shù)字組成,第一個(gè)數(shù)字定義了響應(yīng)的類(lèi)別肋联,共分五種類(lèi)別:
- 1xx:指示信息--表示請(qǐng)求已接收威蕉,繼續(xù)處理
- 2xx:成功--表示請(qǐng)求已被成功接收、理解橄仍、接受
- 3xx:重定向--要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作
- 4xx:客戶(hù)端錯(cuò)誤--請(qǐng)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無(wú)法實(shí)現(xiàn)
- 5xx:服務(wù)器端錯(cuò)誤--服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求
常見(jiàn)狀態(tài)碼:
- 200 OK //客戶(hù)端請(qǐng)求成功
- 400 Bad Request //客戶(hù)端請(qǐng)求有語(yǔ)法錯(cuò)誤韧涨,不能被服務(wù)器所理解
- 401 Unauthorized //請(qǐng)求未經(jīng)授權(quán)牍戚,這個(gè)狀態(tài)代碼必須和WWW-Authenticate報(bào)頭域一起使用
- 403 Forbidden //服務(wù)器收到請(qǐng)求,但是拒絕提供服務(wù)
- 404 Not Found //請(qǐng)求資源不存在虑粥,eg:輸入了錯(cuò)誤的URL
- 500 Internal Server Error //服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤
- 503 Server Unavailable //服務(wù)器當(dāng)前不能處理客戶(hù)端的請(qǐng)求如孝,一段時(shí)間后可能恢復(fù)正常
在Unity中可以使用如下代碼獲取狀態(tài)碼:
Debug.Log(www.responseCode);
這個(gè)狀態(tài)碼也決定了www.isHttpError的返回值
isHttpError = reponseCode >= 400;
Unity中的HTTP響應(yīng)
但是在Unity中,UnityWebRequest并沒(méi)有自動(dòng)幫我們處理這個(gè)過(guò)程娩贷,我們需要手動(dòng)來(lái)處理第晰。
通常我們使用:
var text = www.downloadHandler.text; // 文本信息,使用UTF8編碼解析
var bytes = www.downloadHandler.data; // 字節(jié)數(shù)組
注意www.downloadHandler.text實(shí)際上是自動(dòng)使用UTF8編碼解析了字節(jié)數(shù)組彬祖,如果發(fā)送的數(shù)據(jù)不是采用UTF8編碼但荤,需要獲取字節(jié)數(shù)組自行解析。
另外在Unity中涧至,Unity結(jié)合游戲開(kāi)發(fā)中的常見(jiàn)需求腹躁,也加入了請(qǐng)求圖片、音頻請(qǐng)求的接口南蓬。
請(qǐng)求圖片:
IEnumerator RequestTexture()
{
var url = "https://www.baidu.com/img/bd_logo1.png";
var www = UnityWebRequestTexture.GetTexture(url);
yield return www.SendWebRequest();
Debug.Log("status code:" + www.responseCode);
if (www.isHttpError || www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
var tex = DownloadHandlerTexture.GetContent(www);
}
}
請(qǐng)求音頻:
IEnumerator RequestMedia()
{
var url = "https://www.xxx.com/music.ogg";
var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.OGGVORBIS);
yield return www.SendWebRequest();
Debug.Log("status code:" + www.responseCode);
if (www.isHttpError || www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
var audio = DownloadHandlerAudioClip.GetContent(www);
}
}
請(qǐng)求視頻:
目前Unity的VideoPlayer中支持直接從file或http協(xié)議直接播放視頻纺非,如果你想將這個(gè)視頻下載到本地,可以使用UnityWebRequest.Get方法獲取到所有字節(jié)再將字節(jié)保存到本地赘方。
請(qǐng)求Assetbundle:
IEnumerator InstantiateObject(string assetBundleName)
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, 0);
yield return request.SendWebRequest();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");
Instantiate(cube);
Instantiate(sprite);
}
總結(jié)
大智:“Unity有一個(gè)舊的Web請(qǐng)求API是WWW烧颖,但是對(duì)HTTP請(qǐng)求不完善。建議你還是學(xué)習(xí)新的UnityWebRequest類(lèi)窄陡,擁有更好的性能和更完善的HTTP請(qǐng)求炕淮。”
今日思考題
大智:“試一試本文中的這些例子跳夭⊥吭玻”
小新:“好嘞!”
大智:“收獲別忘了分享出來(lái)币叹!也別忘了分享給你學(xué)Unity的朋友润歉,也許能夠幫到他【备В”
推薦閱讀
- UnityWebRequest詳解
- Unity 2019.1 中文更新日志速覽版
- Unity中編碼Encoding脫坑指南
- Unity中的Git最佳實(shí)踐
- Unity2019更新規(guī)劃速覽踩衩,將有官方的可視化編程!
- Unity運(yùn)行時(shí)更新帶來(lái)了什么贩汉?
- Unity2018.3新功能 | Prefab嵌套和變體
- Unity3d中的百度語(yǔ)音識(shí)別及語(yǔ)音合成
洪流學(xué)堂公眾號(hào)回復(fù)專(zhuān)欄
驱富,查看更多專(zhuān)欄文章。
《大話Unity2019》匹舞,大智帶小新學(xué)Unity2019的有趣經(jīng)歷褐鸥,讓你學(xué)Unity更簡(jiǎn)單。