【发布时间】:2011-02-24 19:02:36
【问题描述】:
.NET Socket 和 NetworkStream 都提供 Read 方法,当没有可用数据时,该方法会阻塞。问题是,如果客户端不知道消息的大小,则无法可靠地了解服务器何时完成发送消息。
我的问题是 - 更高级别的库(如 HTTP 级别的 WebRequest)使用什么原则来停止从套接字读取并向客户端呈现完整消息,而不诉诸任何类型的读取超时?似乎 HTTP 协议不提供任何“EOF”令牌...
【问题讨论】:
.NET Socket 和 NetworkStream 都提供 Read 方法,当没有可用数据时,该方法会阻塞。问题是,如果客户端不知道消息的大小,则无法可靠地了解服务器何时完成发送消息。
我的问题是 - 更高级别的库(如 HTTP 级别的 WebRequest)使用什么原则来停止从套接字读取并向客户端呈现完整消息,而不诉诸任何类型的读取超时?似乎 HTTP 协议不提供任何“EOF”令牌...
【问题讨论】:
HTTP 1.1 通过使用两种机制之一来管理这一点。在标头中发送content-length,或者使用chunked transfer encoding。分块传输编码是 HTTP 1.1 相对于 HTTP 1.0 的一大优势,在没有发送内容长度的情况下,无法可靠地检测到传输结束,从而导致传输问题与有效负载的实际结束之间存在歧义.在某些情况下(例如 HTTP 上的音频流),没有可能指示传输结束的内容长度或其他编码,但在这种情况下,它对客户端来说并不重要。
【讨论】:
有一些方法可以检查套接字是否有待处理的数据。一种是简单检查每个socket的Pending属性,只有有数据才调用Receive。但是,这根本不能很好地扩展,我只建议在具有单个连接的应用程序中使用它。
Socket.Select 函数可以获取一个 IList of Sockets,并在短时间内将这个列表缩减为仅包含有待处理数据的套接字。然后,您可以安全地调用接收它们而不会阻塞。
另一种首选方法是使用异步套接字 API。 (在 NetworkStream 上致电 BeginReceive 或 BeginRead)。在这些情况下,调用不会阻塞,您可以在调用后继续执行代码。您将函数作为参数传递,当数据在套接字上挂起时执行。这是在 .NET 线程池中的后台线程中处理的,因此这里涉及到并发问题,与前两种方法不同。异步工作在 OS 中的较低级别完成,通过使用中断来告诉 CPU 数据何时在网络上等待,而不是像 Select 那样反复轮询数据。
【讨论】:
Socket.Select 不在 Windows 中使用 select 调用? :( 也就是说,除非它是一个非常愚蠢的实现,否则它不应该“反复轮询”(它在我所知道的任何其他语言/库中都没有这样做)。
Begin/End 的建议。
在 HTTP 中,content-length HTTP 标头定义了何时可以关闭套接字。
在 TCP 中,这是任意的,通常客户端或服务器会发送一条特殊消息来通知连接正在终止。
【讨论】: