【问题标题】:Sockets vs HttpListener套接字与 HttpListener
【发布时间】:2017-10-26 22:25:06
【问题描述】:

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

你什么时候更喜欢 HttpListener 而不是 Sockets。各有什么好处?

【问题讨论】:

  • 使用 HttpListener 解析/修改 HTTP 标头、读取内容、处理分块传输等要简单得多。
  • 您将对 HttpRequest 进行什么样的修改?
  • 我将更改 HostHeader,重定向一些请求等

标签: c# .net sockets tcp


【解决方案1】:
  • HttpListener 的优点
    • 允许与使用 http.sys 的其他进程共享端口(包括使用 HttpListener 或 IIS 的进程,只要前缀是唯一的)
    • Http.sys 为您使用 I/O 完成端口;不使用 TcpListener 很难自己设置
    • HttpListener 解析 HTTP 标头并为响应创建 HTTP 标头
    • 为您提供一组非常类似于 ASP.Net 中公开标头、cookie 等的类。
  • HttpListener 的缺点
    • 主要 - 与 Windows 上的 TcpListener 或 Mono 的 HttpListener 相比,Localhost 客户端通过 HTTP 发送大型有效负载会导致这些有效负载的多个额外副本。如果需要每个 CPU 的最大吞吐量,则应避免在同一台机器上的进程之间发送大型(例如超过 5 MB)POST 正文。考虑改用内存映射文件(本质上是共享内存)。当客户端和服务器位于不同的机器上时,这些额外的副本不是问题。
    • 不允许明确控制 TCP 套接字打开的时间; http.sys 会为您管理(这通常不是一个很大的缺点,但需要注意)
    • 头文件/cookie 类中的任何错误都会对您不利;例如设置多个 cookie 将导致 HttpListener 返回一个包含多个 cookie 的单个 Set-Cookie 标头的问题,IE 将处理但 Chrome 将完全忽略(然后您必须手动滚动 cookie 并添加您自己的 Set-解决此问题的 Cookie 标头)
  • TcpListener 的优点
    • 与 HttpListener 一样,它为您实现 I/O 完成端口
    • 如果您想对连接执行一次操作(例如检查身份验证),您可以执行此操作,因为您知道套接字何时打开;但是,由于其他原因,您可能不想依赖它(代理和负载平衡器可能通过同一个套接字从多个用户发送请求)
  • TcpListener 与 HttpListener 的缺点
    • 您必须提供 HTTP 标头解析器
    • 您必须验证 HTTP 请求是否有效
  • 自行滚动 100% 的优势
    • 与使用 TcpListener 相比,我想不到
  • 自行滚动 100% 的缺点
    • 您可能会在网络层代码中引入大量不必要/错误的锁,这在一次测试单个请求的性能时不会造成任何问题,但会导致负载下的性能真正受到影响。找到并修复它们需要一段时间。
    • 我们确实在一个项目中自行推出了此功能,并且我们获得了与 HttpListener 相同的性能,但需要花费更多的时间进行编码,并且生成的代码库无法维护。我们删除了这一点,并用 HttpListener 替换了所有自定义代码,而不会对性能产生任何负面影响,并且需要维护的代码也大大减少。

2016 年 11 月更新:HttpListener 在接收来自远程机器的流量时效果很好。但是,当客户端位于 localhost 上时,对于大型有效负载,从性能角度来看,HttpListener 是一个糟糕的选择。造成这种情况的部分原因是 HttpListener 使用 http.sys,它是一个内核模块,并且显然该实现导致需要制作几个额外的主体数据副本。例如,在 C# 中将 100 MB 从一个数组复制到另一个数组需要 35 毫秒,而将 100 MB 从本地客户端发布到 HttpListener 需要 350 毫秒(是的,长 10 倍)。在这种情况下切换到 Mono 的 HttpListener 会将时间减少到 250 毫秒。将 TcpListener 与 Socket 客户端一起使用需要 180 毫秒。使用 MemoryMappedFiles 将套接字的使用换成 CircularBuffer 需要 55 毫秒。因此,当从本地机器访问时,HttpListener 不适用于大型有效负载。注意:如果您尝试重现此测试,则需要注意测量时间差。 Stern 开始发送数据,并且当数组完成填充在接收过程中,因为一些 Send/Write 方法几乎立即返回而不发送任何数据(您可以通过发送一个零数组然后从其中写入一个来证明这一点数组的背面朝向前面;您将收到几乎所有的,证明数据直到函数返回后很久才发送)。

我的一位同事在此处提供了有关 HttpListener 与 Mono 的 HttpListener 的本地机器性能不足的更多详细信息: https://www.linkedin.com/pulse/http-inefficiency-dominika-blach

【讨论】:

  • 不错的答案。就我而言,我想知道为什么我的客户在数百次连接后停止发送消息。我为服务器使用 HttpListener 为客户端使用 WebRequest。过了一会儿,他们只是简单地停止,没有错误,看起来没有可用的资源。玩ServicePointManager.DefaultConnectionLimitServicePointManager.MaxServicePoints 并没有帮助,所以在几百次连接后我得到了同样的错误。我试过 KeepAlive 但没有运气。任何想法?谢谢。
  • 很可能您没有在一端或两端正确关闭请求和响应,或者您在客户端使用了线程池(例如通过使用计时器)并且您正在抛出异常该线程池线程。一旦线程池线程执行此操作,就永远不会重新创建它。一旦你用完了线程池线程,任何使用它们的东西(例如 Timer 对象)都不再工作。
  • 检查我发布的 HttpListener 示例代码作为对以下问题的回答。查看您是否以相同的方式处理请求或使用它来创建服务器的新版本并查看问题是否仍然存在。 stackoverflow.com/questions/10485985/…
  • 明白了!发现未处理Ouputstream 的情况。希望这能解决我的问题。谢谢。
猜你喜欢
  • 2014-01-06
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 2013-03-18
  • 2012-07-27
  • 1970-01-01
  • 2012-02-28
  • 2012-04-03
相关资源
最近更新 更多