【发布时间】:2013-01-10 01:43:36
【问题描述】:
当我从 msdn link 查看 setsockopt 时。我遇到了一个参数 SO_RCVTIMEO,它的描述是“设置超时,以毫秒为单位,用于阻止接收调用。”我认为套接字侦听操作是事件驱动的,这意味着当内核从 NIC 卡中耗尽帧时通知我的程序套接字,那么阻塞是怎么回事?
【问题讨论】:
标签: c++ setsockopt
当我从 msdn link 查看 setsockopt 时。我遇到了一个参数 SO_RCVTIMEO,它的描述是“设置超时,以毫秒为单位,用于阻止接收调用。”我认为套接字侦听操作是事件驱动的,这意味着当内核从 NIC 卡中耗尽帧时通知我的程序套接字,那么阻塞是怎么回事?
【问题讨论】:
标签: c++ setsockopt
recv 和 WSARecv 函数是阻塞的。它们不是事件驱动的(至少不是在调用级别)。即使阻塞有超时(通过SO_RECTIMEO 选项设置),就您的代码而言,它们不是事件驱动的。在这种情况下,它们只是伪阻塞(可以说是非阻塞,具体取决于超时时间有多短)。
当你调用 WSARecv 时,它会一直等到数据准备好被读取。虽然数据尚未准备好读取,但它只是等待。这就是它被视为阻塞的原因。
您说得对,网络的核心是事件驱动的。在引擎盖下,计算机本质上是事件驱动的。这是硬件的工作方式。硬件中断本质上是事件。你是对的,在低级别发生的事情是你的 NIC 卡告诉操作系统它已经准备好被读取。在那个级别上,它确实是基于事件的。
问题在于 WSARecv 等待该事件。
这是一个希望清楚的类比。想象一下,由于某种原因你不能离开你的房子。现在想象你的朋友 F 住在隔壁。此外,假设您的另一个朋友 G 在您家。
现在想象一下,你给 G 一张纸,上面有一个问题,让他把它交给 F。
发送问题后,假设您发送 G 以获取 F 的回复。这就像 recv 调用。 G 会等到 F 写下他的回答,然后他会把它带给你。如果F还没写,G不会马上转身回来。
这就是差距的来源。 G确实知道“F写了!”事件,但你不是。你不是直接看那张纸。
设置超时意味着您告诉 G 在放弃并返回之前最多等待一段时间。在这种情况下,G 仍在等待 F 写入,但如果 F 在 x 毫秒内没有写入,则 G 转身空手而归。
recv的伪代码基本上是这样的:
1) is data available?
1a) Yes: read it and return
1b) No: GOTO 2
2) Wait until an event is received
2a) GOTO 1
我知道这是一个非常复杂的解释,但我的主要观点是:recv 与事件交互,而不是与您的代码交互。 recv 阻塞,直到收到这些事件之一。如果设置了超时,它会阻塞,直到收到这些事件之一,或达到超时。
【讨论】:
默认情况下,套接字不是事件驱动的。您必须编写额外的代码来启用它。相反,套接字最初是在阻塞模式下创建的。这意味着对send()、recv() 或accept() 的调用将默认无限期地阻塞调用线程,直到请求的操作完成。
对于recv(),这意味着调用线程被阻塞,直到至少有 1 个字节可用于从套接字的接收缓冲区读取,或者直到发生套接字错误,以先发生者为准。 SO_RCVTIMEO 允许您在阻塞读取上设置超时,因此如果在超时过去之前没有可用的传入数据,recv() 将退出并返回 WSAETIMEDOUT 错误。
实现超时的另一种方法是通过ioctlsocket(FIONBIO) 将套接字设置为非阻塞模式,然后在超时时调用select(),然后仅在select() 报告时调用recv() 或accept()表明套接字处于可读状态,并且 send() 仅当 select() 报告套接字处于可写状态时。但这需要更多代码来管理套接字进入阻塞状态的情况,从而导致操作失败并出现WSAEWOULDBLOCK 错误。
【讨论】: