使用MSG_NONBLOCK和MSG_WAITALL进行recv

osg*_*sgx 9 sockets linux network-programming nonblocking

我想使用recv带有非阻塞标志MSG_NONBLOCK的系统调用.但是使用此标志,syscall可以在满足完整请求之前返回.所以,

  • 我可以添加MSG_WAITALL标志吗?它会无阻塞吗?
  • 或者我应该如何使用非阻塞recv将阻塞recv重写为循环

Mat*_*att 5

至少对于 Linux 上的 IPv4 TCP 接收,如果指定了 MSG_NONBLOCK(或者文件描述符设置为非阻塞),则忽略 MSG_WAITALL。

来自 Linux 内核中 net/ipv4/tcp.c 中的 tcp_recvmsg():

if (copied >= target && !sk->sk_backlog.tail)
        break;

if (copied) {
        if (sk->sk_err ||
            sk->sk_state == TCP_CLOSE ||
            (sk->sk_shutdown & RCV_SHUTDOWN) ||
            !timeo ||
            signal_pending(current))
                break;
Run Code Online (Sandbox Code Playgroud)

如果指定了 MSG_DONTWAIT,则此转换中的目标设置为请求的大小;如果未指定,则设置为某个较小的值(至少 1)。如果出现以下情况,该函数将完成:

  1. 已复制足够的字节
  2. 套接字有错误
  3. 套接字已关闭或关闭
  4. timeo 为 0(套接字设置为非阻塞)
  5. 该进程有一个等待处理的信号

对我来说,这似乎可能是 Linux 中的一个错误,但无论如何它都不会按照你想要的方式工作。看起来 dec-vt100 的解决方案可以,但是如果您尝试在多个进程或线程中从同一个套接字接收数据,则会出现竞争条件。
也就是说,在您的线程执行查看之后,另一个线程/进程可能会发生另一个 recv() 调用,从而导致您的线程在第二个 recv() 上阻塞。


Duc*_*uck 3

编辑:

普通的recv()将返回调用时tcp缓冲区中的任何内容,最多可达请求的字节数。如果套接字上根本没有数据可供读取,MSG_DONTWAIT 只是避免阻塞。MSG_WAITALL 请求阻塞,直到可以读取请求的全部字节数。所以你不会得到“全有或全无”的行为。如果没有数据存在,您最多应该得到 EAGAIN,否则会阻塞直到完整消息可用。

您也许可以使用 FIONREAD(如果您的系统支持它)从 MSG_PEEK 或 ioctl() 中设计一些东西,使其有效地表现得像您想要的那样,但我不知道如何仅使用 recv() 标志来实现您的目标。