使用MSG_NONBLOCK和MSG_WAITALL进行recv

时间:2022-03-12 05:52:49

I want to use recv syscall with nonblocking flags MSG_NONBLOCK. But with this flag syscall can return before full request is satisfied. So,

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

  • can I add MSG_WAITALL flag? Will it be nonblocking?
  • 我可以添加MSG_WAITALL标志吗?它会无阻塞吗?
  • or how should I rewrite blocking recv into the loop with nonblocking recv
  • 或者我应该如何使用非阻塞recv将阻塞recv重写为循环

3 个解决方案

#1


4  

This is what I did for the same problem, but I'd like some confirmation that this works as expected...

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

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);
}

#2


3  

EDIT:

编辑:

Plain recv() will return whatever is in the tcp buffer at the time of the call up to the requested number of bytes. MSG_DONTWAIT just avoids blocking if there is no data at all ready to be read on the socket. MSG_WAITALL requests blocking until the entire number of bytes requested can be read. So you won't get "all or none" behavior. At best you should get EAGAIN if no data is present and block until the full message is available otherwise.

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

You might be able to fashion something out of MSG_PEEK or ioctl() with a FIONREAD (if your system supports it) that effectively behaves like you want but I am unaware how you can accomplish your goal just using the recv() flags.

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

#3


3  

For IPv4 TCP receives on Linux at least, MSG_WAITALL is ignored if MSG_NONBLOCK is specified (or the file descriptor is set to non-blocking).

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

From tcp_recvmsg() in net/ipv4/tcp.c in the Linux kernel:

来自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;

target in this cast is set to to the requested size if MSG_DONTWAIT is specified or some smaller value (at least 1) if not. The function will complete if:

如果指定了MSG_DONTWAIT,则此强制转换中的目标设置为请求的大小;如果不是,则设置为较小的值(至少为1)。该功能将在以下情况下完成

  1. Enough bytes have been copied
  2. 已复制足够的字节
  3. There's a socket error
  4. 有一个套接字错误
  5. The socket has been closed or shutdown
  6. 插座已关闭或关闭
  7. timeo is 0 (socket is set to non-blocking)
  8. timeo为0(套接字设置为非阻塞)
  9. There's a signal pending for the process
  10. 这个过程有一个待处理的信号

To me this seems like it may be a bug in Linux, but either way it won't work the way you want. It looks like dec-vt100's solution will, but there is a race condition if you try to receive from the same socket in more than one process or thread.
That is, another recv() call by another thread/process could occur after your thread has performed a peek, causing your thread to block on the second recv().

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

#1


4  

This is what I did for the same problem, but I'd like some confirmation that this works as expected...

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

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);
}

#2


3  

EDIT:

编辑:

Plain recv() will return whatever is in the tcp buffer at the time of the call up to the requested number of bytes. MSG_DONTWAIT just avoids blocking if there is no data at all ready to be read on the socket. MSG_WAITALL requests blocking until the entire number of bytes requested can be read. So you won't get "all or none" behavior. At best you should get EAGAIN if no data is present and block until the full message is available otherwise.

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

You might be able to fashion something out of MSG_PEEK or ioctl() with a FIONREAD (if your system supports it) that effectively behaves like you want but I am unaware how you can accomplish your goal just using the recv() flags.

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

#3


3  

For IPv4 TCP receives on Linux at least, MSG_WAITALL is ignored if MSG_NONBLOCK is specified (or the file descriptor is set to non-blocking).

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

From tcp_recvmsg() in net/ipv4/tcp.c in the Linux kernel:

来自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;

target in this cast is set to to the requested size if MSG_DONTWAIT is specified or some smaller value (at least 1) if not. The function will complete if:

如果指定了MSG_DONTWAIT,则此强制转换中的目标设置为请求的大小;如果不是,则设置为较小的值(至少为1)。该功能将在以下情况下完成

  1. Enough bytes have been copied
  2. 已复制足够的字节
  3. There's a socket error
  4. 有一个套接字错误
  5. The socket has been closed or shutdown
  6. 插座已关闭或关闭
  7. timeo is 0 (socket is set to non-blocking)
  8. timeo为0(套接字设置为非阻塞)
  9. There's a signal pending for the process
  10. 这个过程有一个待处理的信号

To me this seems like it may be a bug in Linux, but either way it won't work the way you want. It looks like dec-vt100's solution will, but there is a race condition if you try to receive from the same socket in more than one process or thread.
That is, another recv() call by another thread/process could occur after your thread has performed a peek, causing your thread to block on the second recv().

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