65.9K
CodeProject 正在变化。 阅读更多。
Home

微型 Web 服务器 2

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.80/5 (11投票s)

2019 年 9 月 8 日

CPOL

3分钟阅读

viewsIcon

22441

downloadIcon

659

一个在 .NET 中基于 Core 和 DNF 的微型动态 Home Web 服务器(第二版)

引言

所以,我的第一次尝试不太理想。我严重误解了我的应用程序在何处阻塞,尽管使用了所有异步 I/O,但仍然只是同步服务,这在 Web 服务器中是一个真正的问题,即使是像这样的为家庭网络设计的。

说到这里,不要让它面向互联网。它不具备可伸缩性。它适用于小型应用程序和有限数量的连接,适用于您不想携带完整 ASP.NET 堆栈的东西,例如在 .NET Core 运行但没有 Apache 的独立系统上。或者,也许您只是想在自己的 WinForms 应用程序中创建一个基于 HTML 的应用程序,因此您想为自己提供服务并使用 Web 浏览器控件(尽管我现在不推荐这样做)。也许你想要一个小工具来和你的 IoT 小工具通信,并且需要修改headers,或者其他什么。也许你只是对这类东西感兴趣。

背景

HTTP 很丑陋,但它有效。这个项目所做的大部分工作都是通过 headers 进行破解并修改响应以进行分块。其余的只是提供异步服务,这在 .NET 中非常容易,除非你像上次那样把它搞砸了。

我们所做的是创建一个监听端口,然后在池化的线程上接受请求,我们在那里处理请求。现在,它在大约一半的 I/O 端是异步的,并且线程处理仍然发生的任何阻塞,尽管SocketUtility中存在比现在正在使用的更多的异步支持。我不想让源代码比现在更复杂。

该代码执行最少的验证,而且我没有花很多时间使其健壮。它更多的是一个例子而不是其他。

Using the Code

SocketUtility是所有 HTTP 和套接字 I/O 的基础,通常通过Socket上的扩展方法实现。

基本上,这个类的大部分只是 HTTP 协议的东西,以及异步套接字 I/O,比如套接字可等待适配器,它将奇怪的异步套接字 API 修改成更基于 async/await 的东西(感谢 MSDN,我没有编写那个小适配器类 - 我 使用好的东西! - 源代码中的链接)。

它主要使用 ServeHttp() 方法处理 HTTP,接受一个已经绑定的监听套接字,并在其上阻塞,所以从另一个线程调用它 - 最好是一个线程池。我发现即使使用可等待的异步方法,仍然需要线程,否则它会阻塞。我明白为什么,但只是现在。基本上,它们是通过等待线程,然后在回调时唤醒来实现的,或者至少它看起来是这样在抽象下运行的,但那不是我需要的。所以我只是把它转换成阻塞,并把它设置在ThreadPool上。我怀疑在任何情况下这都是正确的,即使我们使用某种ServeHttpAsync()方法 - 我还没有编写这个方法。

var listener = new Socket(SocketType.Stream, ProtocolType.Tcp);
var endPoint = new IPEndPoint(IPAddress.Any,8080));
listener.Bind(endPoint);
listener.Listen(10);
ThreadPool.QueueUserWorkItem((l) => { 
       listener.ServeHttp((request, response) => {
               response.WriteLine("Hello World!");
           });
   }, listener);
   // execute wait here as the above doesn't block

这基本上是设置服务器的样子。但是,WebServer组件利用SocketUtility来为您处理此操作。

您所要做的就是设置属性并启动。你会注意到周围的代码比实际的 Web 服务部分更广泛。它超级简单。在 WinForms 上,它是一个组件,所以它可以出现在 WinForm 上的设计器中,你可以只设置它的两个属性,连接事件然后启动。请参见 TinyWebDemo。

static void Main(string[] args)
{
    var w3s = new WebServer();
    if (0 < args.Length)
    {
        w3s.EndPoint = _ToEndPoint(args[0]);
    }
    else
    {
        Console.Error.WriteLine("Usage: w3serv <ip>:<port>");
        Console.Error.WriteLine("\t<ip> can be \"*\"");
        return;
    }
    w3s.IsStarted = true;
    w3s.ProcessRequest += W3s_ProcessRequest;
    Console.Error.WriteLine("Press any key to stop serving...");
    Console.ReadKey();
    w3s.Dispose(); // shut down - in production you'd use the "using" directive or try/finally
}

private static void W3s_ProcessRequest(object sender, ProcessRequestEventArgs args)
{
    // default is text/plain
    args.Response.ContentType = "text/html";
    args.Response.WriteLine("<html><body><h1>Hello World</h1></body>");
}

它没有提供服务文件的机制,只有动态内容,您有责任处理请求路径并提供适当的内容。您还需要设置 Content-Type。目前,它不缓冲,但可以很容易地更新,以便能够像 ASP.NET 一样缓冲输出。我只是懒得这么做,而且我喜欢流式传输。

历史

  • 2019 年 9 月 8 日 - 首次提交
© . All rights reserved.