[專(zhuān)欄精選]Unity中的HTTP網(wǎng)絡(luò)通信

本文節(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ō):

  1. 協(xié)議部分:該URL的協(xié)議部分為“https:”,這代表網(wǎng)頁(yè)使用的是HTTPS協(xié)議,S代表Secure定铜,安全的阳液,是一種加密傳輸?shù)腍TTP協(xié)議,在"https"后面的“//”為分隔符揣炕。
  2. 域名部分:該URL的域名部分為“www.baidu.com”帘皿。一個(gè)URL中,也可以使用IP地址作為域名使用
  3. 端口部分:端口不是一個(gè)URL必須的部分畸陡,如果省略鹰溜,將采用默認(rèn)端口80。本例中就省略了端口號(hào)丁恭。
  4. 虛擬目錄部分:從域名后的第一個(gè)“/”開(kāi)始到最后一個(gè)“/”為止曹动,是虛擬目錄部分。虛擬目錄也不是一個(gè)URL必須的部分牲览。本例中的虛擬目錄是根目錄“/”
  5. 文件名部分:文件名部分也不是一個(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的朋友润歉,也許能夠幫到他【备В”

推薦閱讀


洪流學(xué)堂公眾號(hào)回復(fù)專(zhuān)欄驱富,查看更多專(zhuān)欄文章。

《大話Unity2019》匹舞,大智帶小新學(xué)Unity2019的有趣經(jīng)歷褐鸥,讓你學(xué)Unity更簡(jiǎn)單。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末策菜,一起剝皮案震驚了整個(gè)濱河市晶疼,隨后出現(xiàn)的幾起案子酒贬,更是在濱河造成了極大的恐慌又憨,老刑警劉巖翠霍,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蠢莺,居然都是意外死亡寒匙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)躏将,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锄弱,“玉大人,你說(shuō)我怎么就攤上這事祸憋』嵯埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蚯窥,是天一觀的道長(zhǎng)掸鹅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)拦赠,這世上最難降的妖魔是什么巍沙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮荷鼠,結(jié)果婚禮上句携,老公的妹妹穿的比我還像新娘。我一直安慰自己允乐,他們只是感情好矮嫉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著牍疏,像睡著了一般敞临。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上麸澜,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天挺尿,我揣著相機(jī)與錄音,去河邊找鬼炊邦。 笑死编矾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的馁害。 我是一名探鬼主播窄俏,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碘菜!你這毒婦竟也來(lái)了凹蜈?” 一聲冷哼從身側(cè)響起限寞,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仰坦,沒(méi)想到半個(gè)月后履植,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悄晃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年玫霎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妈橄。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庶近,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出眷蚓,到底是詐尸還是另有隱情鼻种,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布沙热,位于F島的核電站叉钥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏校读。R本人自食惡果不足惜沼侣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歉秫。 院中可真熱鬧蛾洛,春花似錦、人聲如沸雁芙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兔甘。三九已至谎碍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洞焙,已是汗流浹背蟆淀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澡匪,地道東北人熔任。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像唁情,于是被迫代替她去往敵國(guó)和親疑苔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容