Unity中使用HttpListener创建本地Http web服务器教程与完整代码

前言

下方有完整代码和使用方法,急用的请直接拉到最下方

本文可以实现不开新进程在Unity中创建http服务器。
监听自定义ip获取指定目录下的网页或其他资源。如果网页内有其他资源链接也可以正常访问。
可以配合Unity网页浏览器组件使用解决资源打包问题

在Unity中搭建简易http服务主要分为三步

  1. 监听访问请求
  2. 解析请求
  3. 响应请求

1 监听访问请求

监听服务使用的是System.Net库中的HttpListener组件,并使用其Start()方法对相关端口的访问进行监听

using System;
using System.Net;
public class HttpServer : MonoBehaviour
{
    // 服务器对象
    private  HttpListener listener;
    // 要监听的目标地址
    private string localIp = "http://localhost:8081/";
    // 本地web资源地址
    private string localFilePath = "C:\\Users\\Desktop\\html";
}

一般执行监听有三种方式:
第一种就是用whilet(true)循环监听, 这种方式会阻塞当前线程。
第二种是调用threadingpool线程池开一个线程监听请求,是比较常用的方式
第三种是使用异步监听。本文使用的是第三种

listener.BeginGetContext(Response, null);

listener.BeginGetContext会开启异步监听,监听到访问会回调我们自定义的Response方法并传入请求信息,我们对访问请求的数据解析和响应都要在这个方法中完成,并且要在其中再次调用listener.BeginGetContext方法来保持持续监听

    void Start()
    {
        listener = new HttpListener();
        // 定义url
        listener.Prefixes.Add(localIp);
        listener.Start();
        // 使用异步监听Web请求,当客户端的网络请求到来时会自动执行委托
        listener.BeginGetContext(Response, null);
        // 提示信息
        Debug.Log($"服务已启动 {DateTime.Now.ToString()},访问地址:{localIp}");
    }
    private void Response(IAsyncResult ar)
	{
    	// 再次开启异步监听
    	listener.BeginGetContext(Response, null);
    }

2 解析请求

解析请求的目的是根据访问地址来定位文件资源的目录和区分文件不同类型的处理办法。
比如访问的地址是www.baidu.com/folder/index.html,我们首先要得到/folder/index.html这个文件相对地址来定位这个文件在本地磁盘上的路径,然后得到html这个文件的扩展名,来对html文件的处理方式做专门的说明

var context = listener.EndGetContext(ar)用来获取传入的请求数据
var request = context.Request; 用来获取请求信息

private void Response(IAsyncResult ar)
{
    // 再次开启异步监听
    listener.BeginGetContext(Response, null);
    Debug.Log($"{DateTime.Now.ToString()}接到新的请求");

    // 获取context对象
    var context = listener.EndGetContext(ar);
    // 获取请求体
    var request = context.Request;
    
    // 配置响应结构
    try
    {
        // 获取网址中指定的文件名,比如www.baidu.com/show.html, 这里会得到show.如果为空就默认为index.html
        string filename = context.Request.Url.AbsolutePath.Trim('/');
        if (filename == "")
        {
            filename = "index.html";
        }

        // 获取文件扩展名,比如file.jpg,这里拿到的就是jpg
        string[] ext_list = filename.Split('.');
        string ext = ext_list.Length > 1? ext_list[ext_list.Length - 1]:"";
    }
}

3 响应请求

响应请求要做的是,组织好要提供给访问者的资源,然后指明该资源在浏览器中应该怎样呈现
在这一部分中,

我们首先根据解析出来的文件相对路径与设置好的本地资源目录来获得目标资源的绝对路径:string absPath = Path.Combine(localFilePath, filename);

然后为html扩展名的文件设置响应头信息,让他以网页的形式在浏览器中正常展示:switch (ext){ case “html”: context.Response.ContentType = “text/html”;

接着将文件数据转为数据 msg = File.ReadAllBytes(absPath);context.Response.ContentLength64 = msg.Length;

最后将数据发给客户端 using (Stream s = context.Response.OutputStream) { s.Write(msg, 0, msg.Length); }

 private void Response(IAsyncResult ar)
 {
         // 获取网址中指定的文件名,比如www.baidu.com/show.html, 这里会得到show.如果为空就默认为index.html
         string filename = context.Request.Url.AbsolutePath.Trim('/');
         if (filename == "")
         {
             filename = "index.html";
         }

         // 获取文件扩展名,比如file.jpg,这里拿到的就是jpg
         string[] ext_list = filename.Split('.');
         string ext = ext_list.Length > 1? ext_list[ext_list.Length - 1]:"";
         // 根据结合本地资源目录和网址中的文件地址,得到要访问的文件的绝对路径
         string absPath = Path.Combine(localFilePath, filename);
         
         // 设置响应状态,就是网页响应码。ok == 200
         context.Response.StatusCode = (int)HttpStatusCode.OK;
         string expires = DateTime.Now.AddYears(10).ToString("r");

         // 根据文件扩展名配置不同的网页响应头
         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;
         }

         // 组织数据流
         byte[] msg = new byte[0];
         if (msg.Length == 0)
         {
             // 如果目标文件不存在就显示错误页面
             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");
                 }
             }
             // 如果存在就将文件转为byte流
             else
             {
                 msg = File.ReadAllBytes(absPath);
             }
         }
         // 返回字节流
         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)
     {
     }
 }

最后清空数据即可: msg = new byte[0]; GC.Collect();

完整代码及使用方式

使用方式:

  1. 在Unity中创建cs脚本其名为HttpServer.cs,将下方代码copy进去保存
  2. 修改成想要监控的地址:private string localIp = “http://localhost:8081/”;
  3. 修改成允许被访问的本地资源目录:private string localFilePath = “C:\Users\Desktop\html”;
  4. 在Unity场景中创建GameObject,然后为该对象添加HttpServer组件
  5. 运行Unity
  6. 打开浏览器访问http://localhost:8081/

完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net;
using System.Text;
using System.IO;

public class HttpServer : MonoBehaviour
{
    // 服务器对象
    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();
        // 使用异步监听Web请求,当客户端的网络请求到来时会自动执行委托
        listener.BeginGetContext(Response, null);
        // 提示信息
        Debug.Log($"服务已启动 {DateTime.Now.ToString()},访问地址:{localIp}");
    }

    /// <summary>
    ///  处理收到的访问请求
    /// </summary>
    /// <param name="ar">包含请求体的参数</param>
    private void Response(IAsyncResult ar)
    {
        // 再次开启异步监听
        listener.BeginGetContext(Response, null);
        Debug.Log($"{DateTime.Now.ToString()}接到新的请求");

        // 获取context对象
        var context = listener.EndGetContext(ar);
        // 获取请求体
        var request = context.Request;
        // 获取响应结构
        var response = context.Response;

        try
        {
            // 获取网址中指定的文件名,比如www.baidu.com/show.html, 这里会得到show.如果为空就默认为index.html
            string filename = context.Request.Url.AbsolutePath.Trim('/');
            if (filename == "")
            {
                filename = "index.html";
            }

            // 获取文件扩展名,比如file.jpg,这里拿到的就是jpg
            string[] ext_list = filename.Split('.');
            string ext = ext_list.Length > 1? ext_list[ext_list.Length - 1]:"";
            // 根据结合本地资源目录和网址中的文件地址,得到要访问的文件的绝对路径
            string absPath = Path.Combine(localFilePath, filename);
            
            // 设置响应状态,就是网页响应码。ok == 200
            context.Response.StatusCode = (int)HttpStatusCode.OK;
            string expires = DateTime.Now.AddYears(10).ToString("r");

            // 根据文件扩展名配置不同的网页响应头
            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;
            }

            // 组织数据流
            byte[] msg = new byte[0];
            if (msg.Length == 0)
            {
                // 如果目标文件不存在就显示错误页面
                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");
                    }
                }
                // 如果存在就将文件转为byte流
                else
                {
                    msg = File.ReadAllBytes(absPath);
                }
            }
            // 返回字节流
            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)
        {
        }

        // 跨域等设置
        // context.Response.AppendHeader("Access-Control-Allow-Headers", "ID,PW");
        // context.Response.AppendHeader("Access-Control-Allow-Method", "post");
        // context.Response.AppendHeader("Access-Control-Allow-Origin", "*"); // 允许跨域请求
        // context.Response.ContentType = "text/plain;charset=UTF-8"; // 响应类型为UTF-8纯文本格式
        // context.Response.AddHeader("Content-type", "text/plain"); // 添加响应头
        // context.Response.ContentEncoding = Encoding.UTF8;
    }

    private void OnDestroy()
    {
        // httpobj.EndGetContext(null);
    }
}
### 回答1: Unity是一种跨平台的游戏开发引擎,它的网络功能强大,并支持搭建WebSocket服务器。以下是搭建WebSocket服务器的一般步骤: 1. 导入WebSocket库:首先需要在Unity项目导入WebSocket库。可以通过在Unity Asset Store搜索和下载适用于UnityWebSocket库,也可以在GitHub等平台上找到最新的WebSocket库。 2. 创建服务器脚本:在Unity,可以使用C#编写服务器脚本。创建一个新的C#脚本,并将其附加到游戏对象上,作为WebSocket服务器的入口点。 3. 初始化WebSocket服务器:在服务器脚本,需要使用WebSocket库提供的方法来初始化服务器。这通常涉及指定服务器的IP地址和端口号,并设置其他配置选项(如是否使用SSL等)。 4. 监听连接:通过调用WebSocket库提供的方法,可以开始监听连接请求。一旦有客户端连接到服务器,就会触发特定的事件,我们可以在事件处理程序编写处理逻辑。 5. 处理消息:一旦客户端与服务器成功连接,它们可以通过WebSocket发送消息。服务器需要编写逻辑来处理接收到的消息,并根据其内容采取相应的操作。可以使用WebSocket库提供的方法来解析消息并回复客户端。 6. 关闭连接:当客户端断开连接时,服务器也需要相应地关闭连接。可以编写代码来处理关闭连接的事件,并进行清理工作。 7. 测试服务器:最后,可以启动Unity游戏,并使用WebSocket客户端连接到服务器进行测试。可以通过发送和接收消息来验证服务器的功能是否正常。 总的来说,通过以上步骤,我们可以在Unity搭建一个WebSocket服务器服务器脚本负责初始化服务器,监听和处理连接,接收和发送消息,并关闭连接。 ### 回答2: Unity是一款强大的游戏开发引擎,它提供了许多网络功能,包括搭建WebSocket服务器WebSocket是一种全双工通信协议,可以在客户端和服务器之间实现实时通信。 要在Unity搭建WebSocket服务器,可以使用第三方库或插件。以下是一个基本的步骤: 1. 导入WebSocket库:首先,在Unity的Asset Store搜索并导入WebSocket库。例如,可以使用SocketIoClientDotNet或websocket-sharp等库。 2. 创建服务器脚本:在Unity创建一个脚本,用于管理WebSocket服务器的逻辑。这个脚本需要实现WebSocket服务器的初始化、连接管理以及消息处理等功能。 3. 初始化服务器:在脚本使用WebSocket库来初始化服务器。根据所选的库,有关如何进行初始化和设置服务器的示例代码可以在其文档找到。 4. 监听连接:设置服务器监听指定的端口,以便能够接收客户端的连接请求。 5. 处理连接和消息:当客户端连接到服务器时,将触发一个事件。在事件处理程序,可以实现客户端的验证、连接管理以及消息处理等逻辑。 6. 发送和接收消息:通过WebSocket库的API,可以发送和接收消息。根据具体需求,可以实现自定义的消息协议和消息处理逻辑。 7. 关闭服务器:当服务器不再需要时,可以调用相应的API来关闭服务器。 在Unity搭建WebSocket服务器可以实现多种功能,例如实时聊天、多人游戏、实时数据推送等。通过WebSocket的全双工通信特性,可以提供更好的用户体验和交互性。 需要注意的是,搭建WebSocket服务器需要熟悉网络编程和Unity的开发环境。同时,根据项目的需求,可能需要进一步扩展服务器的功能和优化性能。 ### 回答3: Unity是一款多平台游戏开发引擎,虽然它主要用于创建游戏,但也可以用于其他类型的实时应用程序的开发,其包括搭建WebSocket服务器。 在Unity搭建WebSocket服务器可以通过使用第三方库或扩展来实现。以下是一个使用Best HTTP库来搭建WebSocket服务器的简要步骤: 首先,下载并导入Best HTTP库到Unity项目。它是一个功能强大的HTTP库,可以与常见的WebSocket实现一起使用。 接下来,创建一个新的C#脚本,在脚本编写服务器的逻辑。需要使用Best HTTPWebSocket组件。 在脚本,你可以设置WebSocket服务器的IP地址和端口号,并定义WebSocket连接的回调函数。可以在服务器和客户端之间传递消息,并处理各种富文本通讯协议。 在Unity,你可以创建一个空对象并附加刚才编写的脚本。当游戏或应用程序运行时,这个对象将充当WebSocket服务器。 最后,你可以使用网络工具,例如Chrome的WebSocket调试工具或自定义的客户端应用程序,通过WebSocket协议连接到Unity运行的服务器。通过这种方式,你可以在Unity接收和处理客户端发送的消息。 总结起来,Unity可以使用Best HTTP库来搭建WebSocket服务器。你需要导入Best HTTP库,编写服务器逻辑的脚本,并使用WebSocket组件来管理WebSocket连接和处理消息。然后,你可以在Unity运行服务器,并使用网络工具与之建立WebSocket连接。这样,你就可以在Unity实现WebSocket服务器的功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千年奇葩

从来没受过打赏,这玩意好吃吗?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值