前言
下方有完整代碼和使用方法,急用的請(qǐng)直接拉到最下方
本文可以實(shí)現(xiàn)不開新進(jìn)程在Unity中創(chuàng)建http服務(wù)器。
監(jiān)聽自定義ip獲取指定目錄下的網(wǎng)頁(yè)或其他資源。如果網(wǎng)頁(yè)內(nèi)有其他資源鏈接也可以正常訪問(wèn)。
可以配合Unity網(wǎng)頁(yè)瀏覽器組件使用解決資源打包問(wèn)題
在Unity中搭建簡(jiǎn)易http服務(wù)主要分為三步
- 監(jiān)聽訪問(wèn)請(qǐng)求
- 解析請(qǐng)求
- 響應(yīng)請(qǐng)求
1 監(jiān)聽訪問(wèn)請(qǐng)求
監(jiān)聽服務(wù)使用的是System.Net庫(kù)中的HttpListener組件,并使用其Start()方法對(duì)相關(guān)端口的訪問(wèn)進(jìn)行監(jiān)聽
using System;
using System.Net;
public class HttpServer : MonoBehaviour
{
// 服務(wù)器對(duì)象
private HttpListener listener;
// 要監(jiān)聽的目標(biāo)地址
private string localIp = "http://localhost:8081/";
// 本地web資源地址
private string localFilePath = "C:\\Users\\Desktop\\html";
}
一般執(zhí)行監(jiān)聽有三種方式:
第一種就是用whilet(true)循環(huán)監(jiān)聽, 這種方式會(huì)阻塞當(dāng)前線程。
第二種是調(diào)用threadingpool線程池開一個(gè)線程監(jiān)聽請(qǐng)求,是比較常用的方式
第三種是使用異步監(jiān)聽。本文使用的是第三種
listener.BeginGetContext(Response, null);
listener.BeginGetContext會(huì)開啟異步監(jiān)聽,監(jiān)聽到訪問(wèn)會(huì)回調(diào)我們自定義的Response方法并傳入請(qǐng)求信息,我們對(duì)訪問(wèn)請(qǐng)求的數(shù)據(jù)解析和響應(yīng)都要在這個(gè)方法中完成,并且要在其中再次調(diào)用listener.BeginGetContext方法來(lái)保持持續(xù)監(jiān)聽
void Start()
{
listener = new HttpListener();
// 定義url
listener.Prefixes.Add(localIp);
listener.Start();
// 使用異步監(jiān)聽Web請(qǐng)求,當(dāng)客戶端的網(wǎng)絡(luò)請(qǐng)求到來(lái)時(shí)會(huì)自動(dòng)執(zhí)行委托
listener.BeginGetContext(Response, null);
// 提示信息
Debug.Log($"服務(wù)已啟動(dòng) {DateTime.Now.ToString()},訪問(wèn)地址:{localIp}");
}
private void Response(IAsyncResult ar)
{
// 再次開啟異步監(jiān)聽
listener.BeginGetContext(Response, null);
}
2 解析請(qǐng)求
解析請(qǐng)求的目的是根據(jù)訪問(wèn)地址來(lái)定位文件資源的目錄和區(qū)分文件不同類型的處理辦法。
比如訪問(wèn)的地址是www.baidu.com/folder/index.html,我們首先要得到/folder/index.html這個(gè)文件相對(duì)地址來(lái)定位這個(gè)文件在本地磁盤上的路徑,然后得到html這個(gè)文件的擴(kuò)展名,來(lái)對(duì)html文件的處理方式做專門的說(shuō)明
var context = listener.EndGetContext(ar)用來(lái)獲取傳入的請(qǐng)求數(shù)據(jù)
var request = context.Request; 用來(lái)獲取請(qǐng)求信息
private void Response(IAsyncResult ar)
{
// 再次開啟異步監(jiān)聽
listener.BeginGetContext(Response, null);
Debug.Log($"{DateTime.Now.ToString()}接到新的請(qǐng)求");
// 獲取context對(duì)象
var context = listener.EndGetContext(ar);
// 獲取請(qǐng)求體
var request = context.Request;
// 配置響應(yīng)結(jié)構(gòu)
try
{
// 獲取網(wǎng)址中指定的文件名,比如www.baidu.com/show.html, 這里會(huì)得到show.如果為空就默認(rèn)為index.html
string filename = context.Request.Url.AbsolutePath.Trim('/');
if (filename == "")
{
filename = "index.html";
}
// 獲取文件擴(kuò)展名,比如file.jpg,這里拿到的就是jpg
string[] ext_list = filename.Split('.');
string ext = ext_list.Length > 1? ext_list[ext_list.Length - 1]:"";
}
}
3 響應(yīng)請(qǐng)求
響應(yīng)請(qǐng)求要做的是,組織好要提供給訪問(wèn)者的資源,然后指明該資源在瀏覽器中應(yīng)該怎樣呈現(xiàn)
在這一部分中,
我們首先根據(jù)解析出來(lái)的文件相對(duì)路徑與設(shè)置好的本地資源目錄來(lái)獲得目標(biāo)資源的絕對(duì)路徑:string absPath = Path.Combine(localFilePath, filename);
然后為html擴(kuò)展名的文件設(shè)置響應(yīng)頭信息,讓他以網(wǎng)頁(yè)的形式在瀏覽器中正常展示:switch (ext){ case “html”: context.Response.ContentType = “text/html”;
接著將文件數(shù)據(jù)轉(zhuǎn)為數(shù)據(jù) msg = File.ReadAllBytes(absPath);context.Response.ContentLength64 = msg.Length;
最后將數(shù)據(jù)發(fā)給客戶端 using (Stream s = context.Response.OutputStream) { s.Write(msg, 0, msg.Length); }
private void Response(IAsyncResult ar)
{
// 獲取網(wǎng)址中指定的文件名,比如www.baidu.com/show.html, 這里會(huì)得到show.如果為空就默認(rèn)為index.html
string filename = context.Request.Url.AbsolutePath.Trim('/');
if (filename == "")
{
filename = "index.html";
}
// 獲取文件擴(kuò)展名,比如file.jpg,這里拿到的就是jpg
string[] ext_list = filename.Split('.');
string ext = ext_list.Length > 1? ext_list[ext_list.Length - 1]:"";
// 根據(jù)結(jié)合本地資源目錄和網(wǎng)址中的文件地址,得到要訪問(wèn)的文件的絕對(duì)路徑
string absPath = Path.Combine(localFilePath, filename);
// 設(shè)置響應(yīng)狀態(tài),就是網(wǎng)頁(yè)響應(yīng)碼。ok == 200
context.Response.StatusCode = (int)HttpStatusCode.OK;
string expires = DateTime.Now.AddYears(10).ToString("r");
// 根據(jù)文件擴(kuò)展名配置不同的網(wǎng)頁(yè)響應(yīng)頭
switch (ext)
{
case "html":
case "htm":
context.Response.ContentType = "text/html";
break;
case "js":
context.Response.ContentType = "application/x-javascript";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "css":
context.Response.ContentType = "text/css";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "jpg":
case "jpeg":
case "jpe":
context.Response.ContentType = "image/jpeg";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "png":
context.Response.ContentType = "image/png";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "gif":
context.Response.ContentType = "image/gif";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "ico":
context.Response.ContentType = "application/x-ico";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "txt":
context.Response.ContentType = "text/plain";
break;
case "do":
context.Response.AddHeader("Access-Control-Allow-Origin", "*");
context.Response.ContentType = "text/plain;charset=utf-8";
break;
default:
context.Response.ContentType = "";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
}
// 組織數(shù)據(jù)流
byte[] msg = new byte[0];
if (msg.Length == 0)
{
// 如果目標(biāo)文件不存在就顯示錯(cuò)誤頁(yè)面
if (!File.Exists(absPath))
{
context.Response.ContentType = "text/html";
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
if (File.Exists(localFilePath + "error.html"))
{
msg = File.ReadAllBytes(localFilePath + "error.html");
}
else
{
msg = Encoding.Default.GetBytes("404");
}
}
// 如果存在就將文件轉(zhuǎn)為byte流
else
{
msg = File.ReadAllBytes(absPath);
}
}
// 返回字節(jié)流
context.Response.ContentLength64 = msg.Length;
using (Stream s = context.Response.OutputStream)
{
s.Write(msg, 0, msg.Length);
}
msg = new byte[0];
GC.Collect();
}
catch (Exception ex)
{
}
}
最后清空數(shù)據(jù)即可: msg = new byte[0]; GC.Collect();
完整代碼及使用方式
使用方式:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-781609.html
- 在Unity中創(chuàng)建cs腳本其名為HttpServer.cs,將下方代碼copy進(jìn)去保存
- 修改成想要監(jiān)控的地址:private string localIp = “http://localhost:8081/”;
- 修改成允許被訪問(wèn)的本地資源目錄:private string localFilePath = “C:\Users\Desktop\html”;
- 在Unity場(chǎng)景中創(chuàng)建GameObject,然后為該對(duì)象添加HttpServer組件
- 運(yùn)行Unity
- 打開瀏覽器訪問(wèn)http://localhost:8081/
完整代碼:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-781609.html
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net;
using System.Text;
using System.IO;
public class HttpServer : MonoBehaviour
{
// 服務(wù)器對(duì)象
private HttpListener listener;
private string localIp = "http://localhost:8081/";
// 本地web資源地址
private string localFilePath = "C:\\Users\\Desktop\\html";
void Start()
{
listener = new HttpListener();
// 定義url
listener.Prefixes.Add(localIp);
listener.Start();
// 使用異步監(jiān)聽Web請(qǐng)求,當(dāng)客戶端的網(wǎng)絡(luò)請(qǐng)求到來(lái)時(shí)會(huì)自動(dòng)執(zhí)行委托
listener.BeginGetContext(Response, null);
// 提示信息
Debug.Log($"服務(wù)已啟動(dòng) {DateTime.Now.ToString()},訪問(wèn)地址:{localIp}");
}
/// <summary>
/// 處理收到的訪問(wèn)請(qǐng)求
/// </summary>
/// <param name="ar">包含請(qǐng)求體的參數(shù)</param>
private void Response(IAsyncResult ar)
{
// 再次開啟異步監(jiān)聽
listener.BeginGetContext(Response, null);
Debug.Log($"{DateTime.Now.ToString()}接到新的請(qǐng)求");
// 獲取context對(duì)象
var context = listener.EndGetContext(ar);
// 獲取請(qǐng)求體
var request = context.Request;
// 獲取響應(yīng)結(jié)構(gòu)
var response = context.Response;
try
{
// 獲取網(wǎng)址中指定的文件名,比如www.baidu.com/show.html, 這里會(huì)得到show.如果為空就默認(rèn)為index.html
string filename = context.Request.Url.AbsolutePath.Trim('/');
if (filename == "")
{
filename = "index.html";
}
// 獲取文件擴(kuò)展名,比如file.jpg,這里拿到的就是jpg
string[] ext_list = filename.Split('.');
string ext = ext_list.Length > 1? ext_list[ext_list.Length - 1]:"";
// 根據(jù)結(jié)合本地資源目錄和網(wǎng)址中的文件地址,得到要訪問(wèn)的文件的絕對(duì)路徑
string absPath = Path.Combine(localFilePath, filename);
// 設(shè)置響應(yīng)狀態(tài),就是網(wǎng)頁(yè)響應(yīng)碼。ok == 200
context.Response.StatusCode = (int)HttpStatusCode.OK;
string expires = DateTime.Now.AddYears(10).ToString("r");
// 根據(jù)文件擴(kuò)展名配置不同的網(wǎng)頁(yè)響應(yīng)頭
switch (ext)
{
case "html":
case "htm":
context.Response.ContentType = "text/html";
break;
case "js":
context.Response.ContentType = "application/x-javascript";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "css":
context.Response.ContentType = "text/css";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "jpg":
case "jpeg":
case "jpe":
context.Response.ContentType = "image/jpeg";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "png":
context.Response.ContentType = "image/png";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "gif":
context.Response.ContentType = "image/gif";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "ico":
context.Response.ContentType = "application/x-ico";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
case "txt":
context.Response.ContentType = "text/plain";
break;
case "do":
context.Response.AddHeader("Access-Control-Allow-Origin", "*");
context.Response.ContentType = "text/plain;charset=utf-8";
break;
default:
context.Response.ContentType = "";
context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
context.Response.AddHeader("expires", expires);
break;
}
// 組織數(shù)據(jù)流
byte[] msg = new byte[0];
if (msg.Length == 0)
{
// 如果目標(biāo)文件不存在就顯示錯(cuò)誤頁(yè)面
if (!File.Exists(absPath))
{
context.Response.ContentType = "text/html";
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
if (File.Exists(localFilePath + "error.html"))
{
msg = File.ReadAllBytes(localFilePath + "error.html");
}
else
{
msg = Encoding.Default.GetBytes("404");
}
}
// 如果存在就將文件轉(zhuǎn)為byte流
else
{
msg = File.ReadAllBytes(absPath);
}
}
// 返回字節(jié)流
context.Response.ContentLength64 = msg.Length;
using (Stream s = context.Response.OutputStream)
{
s.Write(msg, 0, msg.Length);
}
msg = new byte[0];
GC.Collect();
}
catch (Exception ex)
{
}
// 跨域等設(shè)置
// context.Response.AppendHeader("Access-Control-Allow-Headers", "ID,PW");
// context.Response.AppendHeader("Access-Control-Allow-Method", "post");
// context.Response.AppendHeader("Access-Control-Allow-Origin", "*"); // 允許跨域請(qǐng)求
// context.Response.ContentType = "text/plain;charset=UTF-8"; // 響應(yīng)類型為UTF-8純文本格式
// context.Response.AddHeader("Content-type", "text/plain"); // 添加響應(yīng)頭
// context.Response.ContentEncoding = Encoding.UTF8;
}
private void OnDestroy()
{
// httpobj.EndGetContext(null);
}
}
到了這里,關(guān)于Unity中使用HttpListener創(chuàng)建本地Http web服務(wù)器教程與完整代碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!