使用 C# HttpListener 处理多个请求

Posted

技术标签:

【中文标题】使用 C# HttpListener 处理多个请求【英文标题】:Handling multiple requests with C# HttpListener 【发布时间】:2012-02-20 12:38:05 【问题描述】:

我有一个 .NET Windows 服务,它产生一个基本上只是充当HttpListener 的线程。这在同步模式示例中运行良好...

private void CreateLListener()

    HttpListenerContext context = null;
    HttpListener listener = new HttpListener();
    bool listen = true;

    while(listen)
    
        try
        
            context = listener.GetContext();
        
        catch (...)
        
            listen = false;
        
        // process request and make response
    

我现在遇到的问题是我需要它来处理多个请求并让它们同时响应或至少以重叠的方式响应。

进一步解释 - 客户端是一个媒体播放器应用程序,它首先通过请求标头属性Range bytes=0- 请求媒体文件。据我所知,它这样做是为了弄清楚媒体容器是什么。

在它读取一个“块”(或者如果它已经读取到足以确定媒体类型)之后,它会发出另一个请求(来自不同的客户端套接字号)Range bytes=X-Y。在这种情况下,Y 是第一个响应中返回的 Content-Length,X 比它少 250000 字节(使用 IIS 作为测试发现)。在这个阶段,它正在获取最后一个“块”,看看它是否可以获得媒体时间戳来测量长度。

读完之后,它会向Range bytes=0-(来自另一个套接字号)发出另一个请求,以开始正确地流式传输媒体文件。

但在任何时候,如果客户端的用户执行“跳过”操作,它会发送另一个请求(来自另一个套接字号)Range bytes=Z-,其中 Z 是在媒体文件中跳转到的位置。

我对 HTTP 的东西不是很好,但据我所知,我需要使用多个线程来处理每个请求/响应,同时允许原始 HttpListener 返回监听。我做了很多搜索,但找不到合适的模型。

编辑:

感谢 Rick Strahl 提供的以下示例,我能够根据自己的需要进行调整...

Add a Web Server to your .NET 2.0 app with a few lines of code

【问题讨论】:

【参考方案1】:

如果您从未来来到这里并尝试使用 async/await 使用单个线程处理多个并发请求..

public async Task Listen(string prefix, int maxConcurrentRequests, CancellationToken token)

    HttpListener listener = new HttpListener();
    listener.Prefixes.Add(prefix);
    listener.Start();

    var requests = new HashSet<Task>();
    for(int i=0; i < maxConcurrentRequests; i++)
        requests.Add(listener.GetContextAsync());

    while (!token.IsCancellationRequested)
    
        Task t = await Task.WhenAny(requests);
        requests.Remove(t);

        if (t is Task<HttpListenerContext>)
        
            var context = (t as Task<HttpListenerContext>).Result;
            requests.Add(ProcessRequestAsync(context));
            requests.Add(listener.GetContextAsync());
        
    


public async Task ProcessRequestAsync(HttpListenerContext context)

    ...do stuff...

【讨论】:

如果您关心 ProcessRequestAsync 中的异常,并且您应该关心,请不要忘记在 else 分支中调用 t.Wait()。总而言之,我喜欢这种方法 +1。 另一个问题是只有在WhenAny任务之一完成时才检查while条件。 您能否详细说明为什么只检查WhenAny 是一个问题@frast? @LightLabyrinth 取消仅在收到请求后发生。如果没有请求,当没有请求进来时,WhenAny 可以永远阻塞。 您可以使用 token.ThrowIfCancellationRequested(); 添加 try/catch在while循环内。然后处理 OperationCanceledException,之后,由于请求取消,while 循环应该退出。【参考方案2】:

如果您需要一个更简单的 BeginGetContext 替代方案,您可以仅在 ThreadPool 中排队作业,而不是在主线程上执行它们。像这样:

private void CreateLListener() 
    //....
    while(true) 
        ThreadPool.QueueUserWorkItem(Process, listener.GetContext());    
    

void Process(object o) 
    var context = o as HttpListenerContext;
    // process request and make response

【讨论】:

完美的方法,但是掌握异步编程会更好地为您服务【参考方案3】:

您需要使用异步方法才能处理多个请求。所以你会使用 e BeginGetContextEndGetContext 方法。

看看here。

如果您的应用程序应该阻塞,则同步模型是合适的 在等待客户请求时,如果您只想处理 一个 *一次请求*。使用同步模型,调用 GetContext 方法,等待客户端发送请求。方法返回 一个 HttpListenerContext 对象,供您在发生时进行处理。

【讨论】:

异步与同步的工作方式不同(显然)。开始的调用将立即返回,因此您需要处理它,因此大多数应用程序都有某种等待。但这不会阻止。您在调用 begin 时指定的回调将在新线程上执行。在那里你叫 end 然后再次 begin 继续听然后你做你的工作。新请求将在新线程上处理,直到您决定停止侦听。 @MisterSquonk 很高兴它对您有用。我们这里不给鱼,而是教钓鱼。 :)

以上是关于使用 C# HttpListener 处理多个请求的主要内容,如果未能解决你的问题,请参考以下文章

使用 C# HttpListener 时无法设置 MIME 类型

C# HttpListener 和 Windows 防火墙

C#使用HTTPListener异步实现HTTP服务端,请求执行两次

使用 C# HttpListener 提供大文件

支持 HTTPS 的 Httplistener

如何在 C# 中的随机端口上创建 HttpListener 类?