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

用于处理 AJAX POST 请求的 HttpListener 服务器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (11投票s)

2013 年 5 月 30 日

CC (ASA 3U)

4分钟阅读

viewsIcon

128031

downloadIcon

8257

一个非常简单的 HttpListener 服务器,实现了处理 AJAX 请求的功能。旨在为您的 Windows Forms 应用程序提供一个 Web 服务端点。

Program Screen

引言

HttpListenerServer 是一个用 C# 编写的、在 Visual Studio 2012 中实现的、多线程的简单 Web 服务器。该项目使用 HttpListener 类 (System.Net) 来创建一个简单的 Web 服务器。此类提供了一个简单的 HTTP 协议监听器。该 Web 服务器能够通过多个域监听多个调用。请求数据 (JSON/Text) 也在 Web 服务器的回调中处理。该 Web 服务器监听本地主机 8000 端口上的请求。

代码还包括用于响应以及在请求中发送和接收自定义标头的注释代码。

背景

Web 服务器和 Web 服务通常托管在 IIS、Apache 或 Tomcat 上。这些通常需要复杂的安装和配置。您可以托管您的 ASP.NET Web 应用程序或 PHP 网站。

使用 ASP.NET Web 应用程序可以轻松创建 ASMX Web 服务。您还可以使用 [WebMethod] 在方法声明之前创建 Web 服务端点。该方法在 Web 页面的代码隐藏文件中必须是 static 方法。

此应用程序是另一个应用程序的一部分,该应用程序需要一个简单独立的 HTTP Web 服务监听器。在 Windows Forms 应用程序中需要一个简单的 Web 服务,而 HttpListener 类满足了我的需求。

Using the Code

该项目是一个简单的 Windows Forms 应用程序,目标为 .NET Framework 4.0。

现在,让我们开始了解代码的工作原理以及如何在您的 .NET C# 应用程序中使用它。

首先,在您的类中定义这些变量

static HttpListener listener;
private Thread listenThread1;

Form 加载事件方法如下

private void Form1_Load(object sender, EventArgs e)
{
    listener = new HttpListener();
    listener.Prefixes.Add("https://:8000/");
    listener.Prefixes.Add("http://127.0.0.1:8000/");
    listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;

    listener.Start();
    this.listenThread1 = new Thread(new ParameterizedThreadStart(startlistener));
    listenThread1.Start();
}

实例化 HttpListener 并添加程序应监听的前缀。如果请求的 URI 与此 URI 前缀匹配,则程序将处理该请求。

listener = new HttpListener();
listener.Prefixes.Add("https://:8000/");
listener.Prefixes.Add("http://127.0.0.1:8000/");

更多关于 URI 前缀的信息 (来自 MSDN)

URI 前缀字符串由 scheme (http 或 https)、host、可选的 port 和可选的 path 组成。完整的 URI 前缀字符串示例是 "http://www.contoso.com:8080/customerData/"。前缀必须以斜杠 ("/") 结尾。具有最接近匹配请求 URI 的前缀的 HttpListener 对象会响应请求。多个 HttpListener 对象不能添加相同的前缀;如果 HttpListener 添加了一个已被使用的前缀,将抛出 Win32Exception 异常。当指定了 port 时,host 元素可以被替换为 "*" 来指示 HttpListener 接受发送到该 port 的请求,前提是请求的 URI 未匹配任何其他前缀。例如,要在请求的 URI 未被任何 HttpListener 处理时接收发送到 port 8080 的所有请求,前缀为 "http://*:8080/"。类似地,要指定 HttpListener 接受发送到 port 的所有请求,可以将 host 元素替换为 "+" 字符,即 "https://+:8080"。 "*" 和 "+" 字符可以出现在包含 path 的前缀中。

HttpListener 可以要求客户端身份验证。下面一行说明了 http 请求不需要身份验证。

listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;

以下代码启动监听器以监听指定的前缀。创建一个新线程并将方法 'startlistener' 传递给它,这是一个阻塞方法,不能在 UI 线程上运行。我们也可以将任何对象传递给线程,但这并非我的要求,所以我没有将任何东西传递给线程的 Start() 方法。

listener.Start();
this.listenThread1 = new Thread(new ParameterizedThreadStart(startlistener));
listenThread1.Start();

线程将一直阻塞,直到客户端向监听器发送请求。当收到请求并需要处理时,将调用 ListenerCalback 函数。

private void startlistener(object s)
{
    while (true)
    {               
        ////blocks until a client has connected to the server
        ProcessRequest();
    }
}

private void ProcessRequest()
{
    var result = listener.BeginGetContext(ListenerCallback, listener);
    result.AsyncWaitHandle.WaitOne();
}

BeginGetContext 异步开始检索传入的请求。我们使用 AsyncWaitHandle.WaitOne() 来防止此线程在异步操作完成之前终止。

我们对 ListenerCallback 的代码如下

private void ListenerCallback(IAsyncResult result)
{
    var context = listener.EndGetContext(result);
    Thread.Sleep(1000);
    var data_text = new StreamReader(context.Request.InputStream, 
    context.Request.ContentEncoding).ReadToEnd();
    
    //functions used to decode json encoded data.
    //JavaScriptSerializer js = new JavaScriptSerializer();
    //var data1 = Uri.UnescapeDataString(data_text);
    //string da = Regex.Unescape(data_text);
    // var unserialized = js.Deserialize(data_text, typeof(String));
    
    var cleaned_data = System.Web.HttpUtility.UrlDecode(data_text);
    
    context.Response.StatusCode = 200;
    context.Response.StatusDescription = "OK";
    
    //use this line to get your custom header data in the request.
    //var headerText = context.Request.Headers["mycustomHeader"];
    
    //use this line to send your response in a custom header
    //context.Response.Headers["mycustomResponseHeader"] = "mycustomResponse";
    
    MessageBox.Show(cleaned_data);
    context.Response.Close();
}

EndGetContext 方法完成一个异步操作以检索传入的客户端请求。该上下文包含一个名为 RequestHttpListenerRequest 对象和一个名为 Response 的 HttpListenerResponse 对象,具体取决于 GET/POST 请求。

POST 数据通过 InputStream 接收。我也指定了使用的编码以简化问题。

var data_text = new StreamReader(context.Request.InputStream, 
context.Request.ContentEncoding).ReadToEnd();

以下函数可用于清理/解码/unescape 数据。我必须使用 UrlDecode 方法来 unescape 数据。为了使其正常工作,您必须在应用程序中添加对 System.Web DLL 的引用。

//functions used to decode json encoded data.
//JavaScriptSerializer js = new JavaScriptSerializer();
//var data1 = Uri.UnescapeDataString(data_text);
//string da = Regex.Unescape(data_text);
// var unserialized = js.Deserialize(data_text, typeof(String));

var cleaned_data = System.Web.HttpUtility.UrlDecode(data_text);

我们发送的是 POST 请求,不需要响应,但您可以使用这些行在标头中发送响应

//use this line to get your custom header data in the request.
//var headerText = context.Request.Headers["mycustomHeader"];

//use this line to send your response in a custom header
//context.Response.Headers["mycustomResponseHeader"] = "mycustomResponse";

您也可以通过 context.Response.OutputStream 发送响应,并使用以下方法刷新数据

System.IO.Stream output = response.OutputStream;
output.Write(buffer,0,buffer.Length);

其中 buffer 是一个包含您想要发送的响应的字节数组。

最后,我们通过以下几行代码发送一个 OK 响应

context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";

您可以在应用程序中调用 listener.Stop() 方法来停止监听器监听指定的端口和前缀。请记住,只有一个监听器可以监听一个端口,否则您将收到一个异常。

关注点

HttpListenerServer 仅实现了一个用于处理 Ajax 请求的简单服务器。您还可以扩展此代码来发送 Web 页面作为响应,或托管您自己的 Web 页面。

软件要求

  • Visual Studio 2012 (可在较低版本上运行)
  • HttpListener 类 (System.Net) 需要管理员权限。

历史

  • 初始发布。v1.0。
© . All rights reserved.