gpt4 book ai didi

http - 如何使用 Windows 服务接收 http 请求

转载 作者:可可西里 更新时间:2023-11-01 16:06:37 29 4
gpt4 key购买 nike

我是 winservice 开发的新手。我需要创建服务,它将包含一些功能和 api:它应该在一些请求到来时做一些事情。

据我了解,我需要使用 HttpListener 类。

这里描述了这个问题的基础:How to create a HTTP request listener Windows Service in .NET但它还没有完成代码,我还有更多问题。

这是我的代码:

partial class MyService
{
private System.ComponentModel.IContainer components = null;
private System.Diagnostics.EventLog eventLog1;
private HttpListener listener;


protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

private void InitializeComponent()
{
this.eventLog1 = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();

this.ServiceName = Globals.ServiceName;
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();

HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:1111/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);

}
}

public partial class MyService : ServiceBase
{
public MyService()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource(
"MySource", "MyNewLog");
}
eventLog1.Source = "MySource";
eventLog1.Log = "MyNewLog";
}

// smth about onstart and onstop

private void OnRequestReceive(IAsyncResult result)
{
eventLog1.WriteEntry("Connection catched!");

HttpListener listener = (HttpListener)result.AsyncState;
eventLog1.WriteEntry("1");
HttpListenerContext context = listener.GetContext();
eventLog1.WriteEntry("2");
HttpListenerRequest request = context.Request;
eventLog1.WriteEntry("3");

HttpListenerResponse response = context.Response;
eventLog1.WriteEntry("4");

string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;


Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
}

因此,我添加了一些日志记录以了解正在发生的事情:

当我重新安装并启动服务时,我打开 http://localhost:1111/ 和- 有时一切正常,我在浏览器中看到 HelloWorld,在 MyLog 中看到 1234- 有时它会卡住,我等啊等啊等。那时我可以在日志中看到“1”,但看不到“2”。如果我只是等待,什么也不会发生。但是如果我再次加载 localhost(或按 f5),我可以看到 Hello World...并且日志只添加 234,它是 1234 而不是 11234 摘要..

在那之后,它就卡住了,日志中没有任何变化,直到我重新启动服务。

所以,我明白了,这个问题可能与尚未使用的 listener.EndGetContext 有关。

我在代码中测试了这个变化(最后两行):

private void OnRequestReceive(IAsyncResult result)
{
eventLog1.WriteEntry("Connection catched!");

HttpListener listener = (HttpListener)result.AsyncState;
eventLog1.WriteEntry("1");
HttpListenerContext context = listener.GetContext();
eventLog1.WriteEntry("2");
HttpListenerRequest request = context.Request;
eventLog1.WriteEntry("3");

HttpListenerResponse response = context.Response;
eventLog1.WriteEntry("4");

string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;


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

listener.EndGetContext(result);
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}

这个糟糕的代码有效:当显示 HelloWorld 时,我可以重新加载页面并再次看到它(并且日志多次显示 1234)。但我仍然对 1......234 有问题(对于一些加载的 HelloWorld,它出现,对于一些没有)

当然,在回调的最后使用BeginGetContext是非常糟糕的,但这只是我的测试。

另外,我需要处理并行请求

那么,如何以正确的方式做到这一点?我找不到任何关于在谷歌中接收 winservices 请求的有用信息。只是关于 HttpListener 的问题。

更新:

我尝试了新的方式:

HttpListener listener = new HttpListener();
listener.Prefixes.Add(Globals.URL);
listener.Start();

listenerThread = new Thread(() =>
{
while (listener.IsListening)
{
HandleRequest(listener.GetContext());
}

});
listenerThread.Start();

几乎没问题,它会在上下文消失时等待,使用它并再次等待。但是这种方式会创建一个请求队列。如果一次有1000个请求进来,最后一个会在999之后处理。总比没有好,但我想并行处理请求。此外,还发生了非常奇怪的事情:我创建了带有一些路由的简单 Web 服务器,但在使用一段时间后,它挂起了。在我重新安装(不是重启!)服务之前没有任何变化..

所以,我还在等待任何真正好的实现,它将包含

  • 并行处理传入请求
  • 用于理解回调等的清晰简单的代码
  • 停止服务不应该对 while 循环造成任何问题

最佳答案

您真的想为此使用异步 API - 事实上,这几乎正是它的设计目的。

基本代码比较简单:

void Main()
{
var cts = new CancellationTokenSource();

var task = StartListener(cts.Token);

Console.ReadLine();

cts.Cancel();

task.Wait();
}

async Task StartListener(CancellationToken token)
{
var listener = new HttpListener();
listener.Start();

token.Register(() => listener.Abort());

while (!token.IsCancellationRequested)
{
HttpListenerContext context;

try
{
context = await listener.GetContextAsync().ConfigureAwait(false);

HandleRequest(context); // Note that this is *not* awaited
}
catch
{
// Handle errors
}
}
}

async Task HandleRequest(HttpListenerContext context)
{
// Handle the request, ideally in an asynchronous way.
// Even if not asynchronous, though, this is still run
// on a different (thread pool) thread
}

如果您没有使用过 await 模式,您可能需要阅读一些细节。当然,这绝对不是生产代码,只是一个示例。您需要为初学者添加更多错误处理。

还需要注意的是,因为未等待 HandleRequest,所以您无法在 StartListener 方法中处理它的异常 - 您要么必须为处理这些,或者您应该直接在 HandleRequest 中捕获异常。

基本思路很简单 - 通过不等待 HandleRequest 方法,我们将立即返回等待新请求(通过异步 I/O,即无线程)。如果 HttpListener 确实打算以这种方式使用(我相信是这样,虽然我还没有测试过),它将允许您并行处理许多请求,同时使用异步 I/O和 CPU 工作。

ConfigureAwait 确保您不会同步到 SynchronizationContext。这在服务/控制台应用程序中并不是绝对必要的,因为它们通常没有任何同步上下文,但无论如何我倾向于明确地写出来。 GetContextAsync 方法的延续因此被发布到不同的任务计划程序 - 默认情况下,线程池任务计划程序。

在您的代码中实现它时,您只需使用 ReadLine 之前的 Main 代码作为 OnStart,之后的代码用于 停止task.Wait() 用于确保您干净地关闭 - 实际关闭所有内容可能需要一段时间。此外,StartListener 本身中的任何异常都将由 Wait 重新抛出,因此您也希望在那里记录一些错误。

关于http - 如何使用 Windows 服务接收 http 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26548445/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com