Unity简单HTTP服务

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

/// <summary>
/// Http服务
/// </summary>
public class HttpServer : MonoBehaviour
{
    public static HttpServer I;

    HttpListener listener = new HttpListener();

    public Action<string> receivedEvent;

    int port = 8011;

    private void Awake()
    {
        I = this;
        listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
        listener.Prefixes.Add(string.Format("http://*:{0}/", port));
        listener.Start();

        ReceiveAsync();
        Debug.Log("启动监听!");
    }

    private async Task ReceiveAsync()
    {
        HttpListenerContext context;
        while (true)
        {
            context = await listener.GetContextAsync();

            Dispather(context);
        }
    }

	//注意这里是异步:回调中不能调用Unity一些API,需要自行处理	
    private void Dispather(HttpListenerContext context)
    {
        HttpListenerRequest request = context.Request;
        HttpListenerResponse response = context.Response;        
        response.AddHeader("Access-Control-Allow-Origin", "*"); //跨域
        var rawUrl = request.RawUrl;

        Debug.Log(rawUrl);
        //下面这段是自己的逻辑,根据需要修改       
        if (rawUrl.StartsWith("/update"))
        {
            rawUrl = rawUrl.Remove(0, rawUrl.IndexOf('=') + 1);
            receivedEvent?.Invoke(rawUrl);           

            //using (Stream body = request.InputStream)
            //{
            //    Encoding encoding = request.ContentEncoding;

            //    using (StreamReader reader = new StreamReader(body, encoding))
            //    {
            //        string s = reader.ReadToEnd();                    

            //        receivedEvent?.Invoke(s);
            //    }
            //}
        }
		
		//下面是对请求者的回复
        Stream ss = response.OutputStream;
        byte[] buffer = Encoding.UTF8.GetBytes("{\"code\":0}");
        ss.Write(buffer, 0, buffer.Length);
        ss.Close();
        response.Close();
    }
}

说一下可能碰到的问题:
如果在收到消息后触发事件,如果绑定在这个事件上的逻辑处理时间较长,会导致无法及时回复给对方,导致对方会出错,比如程序卡死

if (rawUrl.StartsWith("/update"))
{    
     receivedEvent?.Invoke(rawUrl);  	
}

例如:耗时操作在回应对方前面或后面执行都会导致一样的问题

if (rawUrl.StartsWith("/update"))
{    
	//回应对方
	Response(response);
	//模拟耗时操作
	for (int i = 0; i < 999999999; i++)
	{
	    float f = 545456 / 34234f;
	    f = f * f;
	}  	
}

private void Response(HttpListenerResponse response)
{
    response.AddHeader("Access-Control-Allow-Origin", "*");

    Stream ss = response.OutputStream;

    byte[] buffer = Encoding.UTF8.GetBytes("{\"code\":0}");
    ss.Write(buffer, 0, buffer.Length);
    ss.Close();
}

解决方案:1.用异步

if (rawUrl.StartsWith("/update"))
{    
	//回应对方
	Response(response);
	
	//耗时操作用异步,有些UnityApi只能在主线程执行
	 Task.Run(() =>
     {
          for (int i = 0; i < 999999999; i++)
          {
              float f = 545456 / 34234f;
          }
      });
}

2.放到主线程里做

if (rawUrl.StartsWith("/update"))
{    
	//回应对方
	Response(response);
	
	//耗时操作放到主线程		
    mainTheard=true;
}

 bool mainTheard= false;
 private void Update()
  {
      if (mainTheard)
      {
          mainTheard= false;
          //如果太耗时,会卡主线程
		 for (int i = 0; i < 999999999; i++)
          {
              float f = 545456 / 34234f;
          }
      }
  }

同样如果这个Http服务不是用于Unity程序也需注意这个问题

更新:添加视频请求,1.下载视频文件,2.在线播放

比如:请求的url规则是:http://xxxip:port/getfile?filename=xxx.mp4

在解析里添加如下代码:
//1.视频下载
if (rawUrl.StartsWith("/getfile"))
{
    var fileName = rawUrl.Split('=')[1];     
    if (fileName.EndsWith(".mp4"))
    {
        response.ContentType = "video/mp4";
        response.Headers.Add("Content-Disposition", $"attachment;filename={fileName}");      
        var bytes = File.ReadAllBytes(Application.streamingAssetsPath + "/" + fileName);
        long videoLength = bytes.Length; // 视频文件总长度   
        response.ContentLength64 = videoLength; // 设置响应内容长度               
        response.OutputStream.Write(bytes, 0, bytes.Length); // 将视频块数据写入响应输出流并发送给客户端
        response.Close(); // 关闭响应流  
        return;
     }
}

//2.视频在线播放,边缓冲边播放
if (rawUrl.StartsWith("/getfile"))
{
    var fileName = rawUrl.Split('=')[1];     
    if (fileName.EndsWith(".mp4"))
    {
         var bytes = File.ReadAllBytes(Application.streamingAssetsPath + "/" + fileName);
         long videoLength = bytes.Length; // 视频文件总长度                     
         // 设置响应头信息,指定内容范围和类型等  
         response.StatusCode = 206; // Partial Content 状态码,测试不加也可以
         response.AddHeader("Content-Range", "bytes " + 0 + "-" + (videoLength - 1) + "/" + videoLength);
         response.AddHeader("Accept-Ranges", "bytes");
         response.ContentType = "video/mp4"; // 视频文件类型,根据实际情况设置  
         response.ContentLength64 = videoLength; // 设置响应内容长度           
         response.OutputStream.Write(bytes, 0, bytes.Length);// 将视频块数据写入响应输出流并发送给客户端  
         response.Close(); // 关闭响应流  
         return;
    }   
}


//3.视频在线播放,边缓冲边播放,同时处理分片与不分片请求,和第二条没发现有啥区别
if (rawUrl.StartsWith("/getfile"))
{
    var fileName = rawUrl.Split('=')[1];     
    if (fileName.EndsWith(".mp4"))
    {
        string rangeHeader = request.Headers["Range"]; // 获取请求的 Range 头部信息  
        string videoPath = Application.streamingAssetsPath + "/" + fileName; // 视频文件路径  
        FileInfo videoFile = new FileInfo(videoPath);
        long videoLength = videoFile.Length; // 视频文件总长度  
        // 解析 Range 头部信息,获取请求的视频块范围  
        long start = 0, end = videoLength - 1;	//如果请求头没有Range,则视频不分片,全部发送
        if (!string.IsNullOrEmpty(rangeHeader)) //分片处理
        {
           string[] ranges = rangeHeader.Replace("bytes=", "").Split('-');
           start = long.Parse(ranges[0]);
           if (ranges.Length > 1)
                 if (!long.TryParse(ranges[1], out end))
				 {
				     end = videoLength - 1;
				 }
        }
        // 计算视频块长度和内容范围  
        long contentLength = end - start + 1;
        byte[] videoChunk = new byte[contentLength];
        using (FileStream fileStream = videoFile.OpenRead())
        {
            fileStream.Seek(start, SeekOrigin.Begin);
            fileStream.Read(videoChunk, 0, (int)contentLength);
        }
        // 设置响应头信息,指定内容范围和类型等  
        response.StatusCode = 206; // Partial Content 状态码  
        response.AddHeader("Content-Range", "bytes " + start + "-" + end + "/" + videoLength);
        response.AddHeader("Accept-Ranges", "bytes");
        response.ContentType = "video/mp4"; // 视频文件类型,根据实际情况设置  
        response.ContentLength64 = contentLength; // 设置响应内容长度           
        response.OutputStream.Write(videoChunk, 0, videoChunk.Length);// 将视频块数据写入响应输出流并发送给客户端 
        response.Close(); // 关闭响应流  
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值