我是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 ...并且log只添加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非常糟糕,但这只是我的测试 .
此外,我需要处理并行请求
那么,如何以正确的方式做到这一点?关于在google收到的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循环中出现任何问题
1 回答
你真的想要使用异步API - 实际上,这几乎就是它的设计目的 .
基本代码相对简单:
如果您对
await
模式没有任何经验,您可能需要仔细阅读详细信息 . 当然,这绝对不是 生产环境 代码,只是一个样本 . 您需要为初学者添加更多错误处理 .另外值得注意的是,因为没有等待
HandleRequest
,所以你无法在StartListener
方法中处理它的异常 - 你要么必须添加一个继续来处理它们,要么你应该直接捕获HandleRequest
中的异常 .基本思路很简单 - 通过不等待
HandleRequest
方法,我们立即回到等待新请求(通过异步I / O,即无线程) . 如果HttpListener
真的意味着以这种方式使用(我相信它是,虽然我还没有测试过),它将允许您并行处理许多请求,包括异步I / O和CPU工作 .ConfigureAwait
确保您不会同步到SynchronizationContext
. 这不是任何同步上下文,但无论如何我倾向于明确地写出来 . 因此,GetContextAsync
方法的延续发布到不同的任务调度程序 - 默认情况下是线程池任务调度程序 .在代码中实现它时,您只需在
ReadLine
之前使用Main
代码OnStart
,然后使用OnStop
之后的代码 .task.Wait()
用于确保您干净地关闭 - 实际关闭所有内容可能需要一段时间 . 此外,StartListener
本身的任何异常都将被Wait
重新抛出,因此您也希望在那里记录一些错误 .