我是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
- 有时它会停滞不前,我等着等待。在那段时间我可以看到&#34; 1&#34;在日志中,但不是&#34; 2&#34;。如果我等待,没有任何事情发生。但是,如果我再次加载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非常糟糕,但这只是我的测试。
另外,我需要处理并行请求
那么,如何以正确的方式做到这一点?我无法找到有关在谷歌接收的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之后处理。它总比没有好,但我想并行处理请求。此外,非常奇怪的事情发生了:我已经用一些路线创建了简单的网络服务器,但经过一段时间的使用后,它就会挂起。在我重新安装(不重启!)服务之前没有任何改变。
所以,我还在等待任何真正好的实现,包含
答案 0 :(得分:6)
你真的想要使用异步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
方法的延续发布到不同的任务调度程序 - 默认情况下,是一个线程池任务调度程序。
在您的代码中实现此功能时,您只需使用Main
之前的ReadLine
代码OnStart
以及OnStop
之后的代码。 task.Wait()
用于确保您干净地关闭 - 实际关闭所有内容可能需要一段时间。此外,StartListener
本身的任何异常都会被Wait
重新抛出,因此您也希望在那里记录一些错误。