使用MSG_NONBLOCK和MSG_WAITALL进行recv

时间:2010-05-31 12:13:57

标签: linux sockets network-programming nonblocking

我想将recv系统调用与非阻塞标志MSG_NONBLOCK一起使用。但是使用此标志,syscall可以在满足完整请求之前返回。所以,

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

3 个答案:

答案 0 :(得分:4)

这就是我为同样的问题所做的,但我想要一些确认,这可以按预期工作......

ssize_t recv_allOrNothing(int socket_id, void *buffer, size_t buffer_len, bool block = false)
{
    if(!block)
    {
        ssize_t bytes_received = recv(socket_id, buffer, buffer_len, MSG_DONTWAIT | MSG_PEEK);

        if (bytes_received == -1)
            return -1;

        if ((size_t)bytes_received != buffer_len)
            return 0;
    }

    return recv(socket_id, buffer, buffer_len, MSG_WAITALL);
}

答案 1 :(得分:3)

编辑:

Plain recv()将在调用时返回tcp缓冲区中所需的字节数。如果没有数据准备在套接字上读取,MSG_DONTWAIT只是避免阻塞。 MSG_WAITALL请求阻塞,直到可以读取所请求的整个字节数。所以你不会得到“全部或全部”行为。如果没有数据存在,你应该获得EAGAIN并阻塞,直到完整的消息可用为止。

您可以使用FIONREAD(如果您的系统支持它)来制作MSG_PEEK或ioctl()中的某些东西,它有效地表现得像您想要但我不知道如何使用recv()标志来实现目标

答案 2 :(得分:3)

对于至少在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;
如果指定了MSG_DONTWAIT,则

此演员表中的目标设置为所请求的大小;如果不指定,则设置为较小的值(至少为1)。如果出现以下情况,该功能将完成:

  1. 已复制足够的字节
  2. 存在套接字错误
  3. 套接字已关闭或关闭
  4. timeo为0(套接字设置为非阻塞)
  5. 有一个待处理的信号
  6. 对我而言,这似乎可能是Linux中的一个错误,但无论哪种方式,它都无法按照您想要的方式运行。它看起来像dec-vt100的解决方案,但是如果你尝试从多个进程或线程中的同一个套接字接收,则存在竞争条件。
    即,另一个线程/进程的另一个recv()调用可以在你的线程执行一次窥视后发生,导致你的线程阻塞第二个recv()。