len*_*den 3 windows-services http httplistener
我是winservice开发的新手.我需要创建服务,它将包含一些函数和api:它应该在某些请求到来时执行某些操作.
据我了解,我需要使用HttpListener类.
此处描述了此问题的基础:如何在.NET中创建HTTP请求侦听器Windows服务但是它还没有完成代码,我还有更多问题.
这是我的代码:
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服务器,但经过一段时间的使用后,它就会挂起.在我重新安装(不重启!)服务之前没有任何改变.
所以,我还在等待任何真正好的实现,包含
你真的想要使用异步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,因此您希望在那里记录一些错误.
| 归档时间: | 
 | 
| 查看次数: | 7313 次 | 
| 最近记录: |