套接字 vs HttpListener

8

我正在创建一个代理,它在特定端口上侦听传入的连接,这些连接通常是Http请求(GET / POST)。我无法决定是否应该选择HttpListener还是Sockets。我将修改代理中的HttpRequests,然后将其转发到最终目标。

您何时更喜欢使用HttpListener而不是Sockets?每种方法有哪些优点?


使用HttpListener解析/修改HTTP头、读取内容、处理分块传输等操作都更加简单。 - L.B
你将对 HttpRequests 进行哪些修改? - mj_
我将更改HostHeader,重定向一些请求等。 - NewUnhandledException
1个回答

22
  • HttpListener的优势
    • 使用http.sys允许与其他进程共享端口(包括使用HttpListener或IIS的进程,只要前缀是唯一的)
    • Http.sys为您使用I/O完成端口;如果不使用TcpListener自己设置,这很难完成
    • HttpListener解析HTTP标头并创建响应的HTTP标头
    • 提供一组非常类似于ASP.Net的类,公开标头、Cookie等
  • HttpListener的缺点
    • 主要 - 通过HTTP发送大负载至localhost客户端会导致与TcpListener或Windows上的Mono的HttpListener相比产生几个额外的负载拷贝。如果需要每CPU的最大吞吐量,请避免在同一台机器上的进程之间发送大型(例如超过5 MB)POST正文。考虑使用内存映射文件(基本上是共享内存)。当客户端和服务器在不同的机器上时,这些额外的副本不是问题。
    • 不允许明确控制TCP套接字保持打开多长时间;http.sys为您管理此项工作(通常不是一个巨大的缺点,但这只是需要注意的事项)
    • 任何标题/ Cookie类中的错误都会咬你;例如,设置多个Cookie会导致HttpListener返回一个带有多个Cookie的单个Set-Cookie标头,IE将处理此问题,但Chrome将完全忽略它(您必须手工滚动Cookie并添加自己的Set-Cookie标头来解决此问题)
  • TcpListener的优势
    • 与HttpListener一样,它为您实现了I/O完成端口
    • 如果您想要对连接执行一次操作(例如检查身份验证),则可以执行此操作,因为您知道何时打开套接字;但是,出于其他原因,您可能不想依赖于此(代理和负载均衡器可能通过同一套接字发送多个用户的请求)
  • TcpListener vs. HttpListener的缺点
    • 您必须提供一个HTTP标头解析器
  • 您需要验证HTTP请求是否有效
  • 自行开发的优点
    • 与使用TcpListener相比,我想不到任何优点
  • 自行开发的缺点
    • 您可能会在网络层代码中引入大量不必要/错误的锁定,这在单个请求的性能测试时不会有任何问题,但在负载下性能将受到严重影响。找到并修复这些问题需要一段时间。
    • 我们曾经在一个项目中自行开发过,花费了很多月的时间,最终生成的代码难以维护,而且性能与HttpListener相同。然后我们将所有自定义代码替换为HttpListener,没有负面影响,并且需要维护的代码明显减少。
  • 2016年11月更新:HttpListener在接收来自远程计算机的流量时效果非常好。但是,当客户端在本地主机上且负载较大时,HttpListener在性能方面表现不佳。部分原因是HttpListener使用了内核模块http.sys,该实现似乎会导致对消息体数据进行多次附加复制。例如,在C#中将100 MB从一个数组复制到另一个数组需要35毫秒,而从本地客户端POST 100 MB到HttpListener则需要350毫秒(是35毫秒的10倍)。 在此情况下转换为Mono的HttpListener可以将时间缩短至250毫秒。使用TcpListener与Socket客户端需要180毫秒。使用MemoryMappedFiles的CircularBuffer替换套接字使用需要55毫秒。因此,从本地访问时,HttpListener的大负载并不起作用。注意:如果您尝试重现此测试,则需注意测量时间差。在开始发送数据和在接收过程中的数组完全填充之间存在一些Send/Write方法几乎立即返回而未发送任何数据的情况(您可以通过发送零数组,然后从数组的后面向前写入1来证明这一点;您将接收到几乎所有1,证明数据直到函数返回后很长时间才被发送)。

    有关HttpListener在本地机器上性能低于Mono的HttpListener的更多详细信息,请参见我的同事在此处发布: https://www.linkedin.com/pulse/http-inefficiency-dominika-blach


    不错的回答。在我的情况下,我想知道为什么我的客户端在连接数达到几百个后停止发送消息。我使用HttpListener作为服务器和WebRequest作为客户端。一段时间后,它们就会突然停止,没有错误,看起来好像没有可用的资源。尝试调整ServicePointManager.DefaultConnectionLimitServicePointManager.MaxServicePoints并没有帮助,所以在几百个连接后我得到了相同的错误。我已经尝试过KeepAlive但没有成功。有什么建议吗?谢谢。 - GBrian
    2
    很可能你没有正确关闭请求和响应,或者在客户端使用了线程池(例如使用计时器),并且在该线程池线程中抛出了异常。一旦线程池线程这样做,它就永远不会被重新创建。一旦你用完了线程池线程,任何使用它们的东西,如计时器对象,都将不再工作。 - huntharo
    请查看我在下面问题的答案中发布的HttpListener示例代码。看看您是否以相同的方式处理请求,或者使用它创建服务器的新版本,并查看问题是否仍然存在。http://stackoverflow.com/questions/10485985/bad-performance-when-offering-files-to-download-with-httplistener/19202175#19202175 - huntharo
    找到了!发现了一个Outputstream没有被处理的情况。希望这能解决我的问题。谢谢。 - GBrian

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接