HTTP 服務器是一種遵循超文本傳輸協議(HTTP)的服務器,用于在網絡上傳輸和處理網頁及其他相關資源。以下是關于它的詳細介紹:
工作原理
- HTTP 服務器監聽指定端口(通常是 80 端口用于 HTTP,443 端口用于 HTTPS),等待客戶端(如瀏覽器)發送請求。當客戶端發送請求時,服務器解析請求,根據請求的 URL 和其他信息,找到對應的資源(如 HTML 文件、圖片、腳本等),然后將這些資源封裝在 HTTP 響應消息中,發送回客戶端。
搭建HTTP服務器
這里我們使用別人做好的HTTP服務器:hfs
HTTP的關鍵類
利用Head類型請求資源的可用性
? ? 在 C# 里,HTTP 的HEAD
請求方法是一種特殊的 HTTP 請求類型。它和GET
請求類似,區別在于HEAD
請求僅要求服務器返回 HTTP 響應頭信息,而不返回請求資源的具體內容。這種方法在很多場景中都很有用,比如檢查資源是否存在、獲取資源的元數據(像文件大小、修改時間),同時避免傳輸大量數據。
#region 知識點一 檢測資源的可用性try{//利用Head類型請求類型,獲取信息//1.創建Http通訊用連接對象HttpWebRequest對象HttpWebRequest req = HttpWebRequest.Create("http://172.41.2.6/HTTP_Server/測試圖片.png") as HttpWebRequest;//2.設置請求類型,或其他相關設置參數req.Method = WebRequestMethods.Http.Head;req.Timeout = 2000;//3.發送請求,獲取響應結果HttpWebResponse對象HttpWebResponse res = req.GetResponse() as HttpWebResponse;if (res.StatusCode == HttpStatusCode.OK){print("文件存在且可用");print(res.ContentLength);print(res.ContentType);res.Close();}elseprint("文件不能用" + res.StatusCode);}catch (WebException w){print("獲取出錯"+w.Message +w.Status);}#endregion
利用Get類型下載服務器中的資源
在 C# 里,GET
是 HTTP 協議里最常用的請求方法之一,其作用是從指定的服務器獲取資源
#region 下載資源try{//利用GET請求類型,下載資源//1.創建HTTP通訊用連接對象HttpWebRequest對象HttpWebRequest req = HttpWebRequest.Create(new Uri("http://172.41.2.6/HTTP_Server/測試圖片.png")) as HttpWebRequest;//2.設置請求類型,或其他相關操作req.Method = WebRequestMethods.Http.Get;req.Timeout = 3000;//3.發送請求,獲取響應結果HttpWebResponse對象HttpWebResponse res = req.GetResponse() as HttpWebResponse;//4.獲取響應數據流,寫入本地路徑if (res.StatusCode == HttpStatusCode.OK){print(Application.persistentDataPath);using (FileStream file = File.Create(Application.persistentDataPath + "/httpDowmload.png")){Stream downLoadStream = res.GetResponseStream();byte[] bytes = new byte[2048];int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);while (contentLength != 0){file.Write(bytes, 0, contentLength);contentLength = downLoadStream.Read(bytes, 0, bytes.Length);}file.Close();downLoadStream.Close();res.Close();}print("下載成功");}elseprint("下載失敗");}catch (WebException w){print("下載出錯了" + w.Message + w.Status);}#endregion
利用Post類型給服務器上傳資源
在 C# 里,POST
請求方法用于向服務器提交數據,通常用于創建新資源,比如用戶注冊、表單提交等場景
Post的學前準備
Get和Post的區別是什么
語義和用途
- GET:設計目的是從服務器獲取資源。例如,當你在瀏覽器地址欄輸入網址或者點擊超鏈接時,瀏覽器通常會發送
GET
請求來獲取對應的網頁、圖片、文件等資源。 - POST:主要用于向服務器提交數據,通常用于創建、更新服務器上的資源。像用戶注冊、登錄、提交表單數據等場景,往往會使用
POST
請求。
數據傳輸方式
- GET:請求參數會附加在 URL 后面,以鍵值對的形式出現,多個參數之間用
&
符號分隔。例如:https://example.com/search?keyword=apple&category=fruits
。 - POST:請求參數會放在 HTTP 請求體中進行傳輸,不會顯示在 URL 里。這樣可以傳輸大量數據,并且更適合傳輸敏感信息。
數據長度限制
- GET:由于請求參數會附加在 URL 后面,而不同的瀏覽器和服務器對 URL 的長度有一定限制,因此
GET
請求所能攜帶的數據量有限。 - POST:請求參數放在請求體中,理論上對數據長度沒有限制,但服務器可能會對請求體的大小進行限制。
安全性
- GET:請求參數會暴露在 URL 中,因此不適合傳輸敏感信息,如密碼、信用卡號等。此外,
GET
請求還可能被緩存,存在一定的安全風險。 - POST:請求參數在請求體中,不會暴露在 URL 里,相對更安全。不過,如果不使用 HTTPS 協議進行加密傳輸,請求體中的數據仍可能被截獲。
緩存機制
- GET:通常會被瀏覽器緩存,這意味著如果多次發送相同的
GET
請求,瀏覽器可能會直接從本地緩存中獲取響應,而不會再次向服務器發送請求。 - POST:默認情況下不會被緩存,每次發送
POST
請求都會向服務器發送新的請求。
冪等性
- GET:是冪等的,即多次執行相同的
GET
請求,得到的結果是相同的,不會對服務器上的資源產生額外的影響。 - POST:不是冪等的,多次執行相同的
POST
請求可能會在服務器上創建多個相同的資源,或者對資源進行多次更新。
Post如何攜帶額外參數
#region 知識點二 Post如何攜帶額外參數//關鍵點:將Content-Type設置為 application/x-www-form-urlencoded鍵值對類型HttpWebRequest req = HttpWebRequest.Create("http://172.41.2.6/HTTP_Server") as HttpWebRequest;req.Method = WebRequestMethods.Http.Post;req.Timeout = 2000;//設置上傳內容的類型req.ContentType = "application/x-www-form-urlencoded";//我們要上傳的數據string str = "Name=DamnF&ID=2";byte[] bytes = Encoding.UTF8.GetBytes(str);//我們在上傳之前一定要設置內容的長度req.ContentLength = bytes.Length;//上傳數據Stream stream = req.GetRequestStream();stream.Write(bytes, 0, bytes.Length);stream.Close();//發送數據得到響應結果HttpWebResponse res= req.GetResponse()as HttpWebResponse;print(res.StatusCode);#endregion
Content-Type中重要的類型
#region 知識點四 ContentType中對于我們重要的類型//1.通用的2進制類型//application/octet-stream//2.通用文本類型//text/plain//3.鍵值對參數//application/x-www-form-urlencoded//4.復合類型(傳遞的信息由多種信息組成,比如有鍵值對參數,文件信息等,上傳資源服務器時需要用到該類型)//multipart//form-data#endregion
開始使用Post上傳資源
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using UnityEngine;public class Lesson27 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){#region 知識點一 上傳文件到HTTP資源服務器需要遵守的規則//上傳文件內容必須遵守的規則//1:ContentType="multipart/form-data;boundary=邊界字符串";//2:上傳的格式必須按照格式寫入流中// ---邊界字符串//Content-Disposition:form-data;name="file";filename="傳到服務器上使用的文件名";//Content-Type:application/octet-stream(由于我們上傳2進制文件,所以這里使用2進制)//(這里直接寫入傳入的內容)//--邊界字符串--//3:保證服務器允許上傳//4:寫入流之前需要先設置ContentLength內容長度#endregion#region 知識點二 上傳文件//1.創建HttpWebRequest對象HttpWebRequest req = HttpWebRequest.Create("http://172.41.2.6/HTTP_Server/") as HttpWebRequest;//2.相關設置(請求類型,內容類型,超時,身份驗證等)req.Method = WebRequestMethods.Http.Post;req.ContentType = "multipart/form-data;boundary=DamnF";req.Timeout = 500000;req.Credentials = new NetworkCredential("DamnF", "123");req.PreAuthenticate = true;//先驗證身份,再上傳數據//3.按格式拼接字符串并且轉換為字節數組之后用于上傳//3-1.文件數據前的頭部信息// --邊界字符串--//Content-Disposition:form-data;name="字段名字,之后寫入的文件2進制數據和該字段名對應";filename="傳到服務器上使用的文件名";//Content-Type:application/octet-stream(由于我們上傳2進制文件,所以這里使用2進制)//空一行string head = "--DamnF\r\n" +"Content-Disposition:form-data;name=\"file\";filename=\"http上傳文件.png\"\r\n" +"Content-Type:application/octet-stream\r\n\r\n";//頭部拼接字符串規則信息的字節數組byte[] headBytes = Encoding.UTF8.GetBytes(head);//3-2結束的邊界信息// --邊界字符串--byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--DamnF--\r\n");//4.寫入上傳流using (FileStream localFileStream=File .OpenRead(Application .streamingAssetsPath +"/test.png")){//4-1設置上傳長度//總長度=前部分字符串長度+文件本身長度+后部分邊界字符串req.ContentLength = headBytes.Length + localFileStream.Length + endBytes.Length;//用于上傳的流Stream upLoadStream = req.GetRequestStream();//4-2先寫入前部分頭部信息upLoadStream.Write(headBytes, 0, headBytes.Length);//4-3再寫入文件數據byte[] bytes = new byte[2048];int contentLength = localFileStream.Read(bytes, 0, bytes.Length);while (contentLength !=0){upLoadStream.Write(bytes, 0, contentLength);contentLength = localFileStream.Read(bytes, 0, bytes.Length);}//4-4再寫入結束的邊界信息upLoadStream.Write(endBytes, 0, endBytes.Length);upLoadStream.Close();localFileStream.Close();}//上傳數據,獲取響應HttpWebResponse res = req.GetResponse() as HttpWebResponse;if (res.StatusCode == HttpStatusCode.OK)print("上傳成功");elseprint("上傳失敗"+res.StatusCode);#endregion}// Update is called once per framevoid Update(){}
}
將Get和Post用單例模式封裝到一個HTTPMgr模塊中(異步)
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;public class HttpMgr
{private static HttpMgr instance=new HttpMgr ();public static HttpMgr Instance => instance;//http服務器地址private string HTTP_PATH = "http://172.41.2.6/HTTP_Server/";//Http服務器賬號和密碼private string HTTP_ID = "DamnF";private string HTTP_PASSWORD = "123";public async void DownLoadFile(string fileName,string localPath,UnityAction<bool>action=null){await Task.Run(()=>{try{HttpWebRequest req = HttpWebRequest.Create(new Uri(HTTP_PATH + fileName)) as HttpWebRequest;req.Method = WebRequestMethods.Http.Get;req.Timeout = 3000;HttpWebResponse res= req.GetResponse()as HttpWebResponse;if (res.StatusCode == HttpStatusCode.OK){using (FileStream fileStream = File.Create(localPath)){Stream downLoadStream = res.GetResponseStream();byte[] bytes = new byte[2048];int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);while (contentLength != 0){fileStream.Write(bytes, 0, contentLength);contentLength = downLoadStream.Read(bytes, 0, bytes.Length);}Debug.Log("下載文件成功");action?.Invoke(true);fileStream.Close();downLoadStream.Close();}res.Close();}elseDebug.Log("下載文件失敗");}catch (WebException w){Debug.Log("下載文件出錯" + w.Message + w.Status);action?.Invoke(false);}});}public async void UpLoadFile(string fileName, string localFileName,UnityAction <bool>action=null){await Task.Run(() =>{try{HttpWebRequest req = HttpWebRequest.Create(HTTP_PATH) as HttpWebRequest;req.Method = WebRequestMethods.Http.Post;req.ContentType = "multipart/form-data;boundary=DamnF";req.Timeout = 500000;req.Credentials = new NetworkCredential(HTTP_ID, HTTP_PASSWORD);req.PreAuthenticate = true;string head = "--DamnF\r\n" +"Content-Disposition:form-data;name=\"file\";filename=\"{0}\"\r\n" +"Content-Type:application/octet-stream\r\n\r\n";head = string.Format(head, fileName);byte[] headBytes = Encoding.UTF8.GetBytes(head);byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--DamnF--\r\n");using (FileStream localFileStream=File .OpenRead (localFileName)){req.ContentLength = headBytes.Length + localFileStream.Length + endBytes.Length;Stream upLoadStream = req.GetRequestStream();upLoadStream.Write(headBytes, 0, headBytes.Length);byte[] bytes = new byte[2048];int contentLength= localFileStream.Read(bytes, 0, bytes.Length);while (contentLength !=0){upLoadStream.Write(bytes, 0, contentLength);contentLength = localFileStream.Read(bytes, 0, bytes.Length);}upLoadStream.Write(endBytes, 0, endBytes.Length);upLoadStream.Close();localFileStream.Close();}HttpWebResponse res= req.GetResponse()as HttpWebResponse;if(res.StatusCode ==HttpStatusCode.OK ){action?.Invoke(true);}else{action?.Invoke(false);}}catch (WebException e){Debug.Log("上傳出錯了" + e.Message + e.Status);}});}
}
測試HTTPMgr模塊
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Lesson25_Test : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){print(Application.persistentDataPath);HttpMgr.Instance.DownLoadFile("/測試圖片.png", Application.persistentDataPath + "/downTest.png", (result) =>{print(result ? "下載成功調用委托函數" : "下載失敗調用委托函數");});HttpMgr.Instance.UpLoadFile("http上傳測試文件.png",Application.streamingAssetsPath + "/test.png", (result) =>{print(result ? "文件上傳成功" : "文件上傳失敗");});}// Update is called once per framevoid Update(){}
}
這樣就實現了對Http服務器的操作了