我想使用recv
带有非阻塞标志 MSG_NONBLOCK 的系统调用。但是有了这个标志,系统调用可以在完整请求得到满足之前返回。所以,
- 我可以添加 MSG_WAITALL 标志吗?会不会阻塞?
- 或者我应该如何用非阻塞recv将阻塞recv重写到循环中
我想使用recv
带有非阻塞标志 MSG_NONBLOCK 的系统调用。但是有了这个标志,系统调用可以在完整请求得到满足之前返回。所以,
至少对于 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)。如果满足以下条件,该功能将完成:
对我来说,这似乎是 Linux 中的一个错误,但无论哪种方式,它都不会按照您想要的方式工作。看起来 dec-vt100 的解决方案可以,但是如果您尝试在多个进程或线程中从同一个套接字接收,则会出现竞争条件。
也就是说,在您的线程执行 peek 之后,可能会发生另一个线程/进程的另一个 recv() 调用,从而导致您的线程在第二个 recv() 上阻塞。
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.
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.
这就是我为同样的问题所做的,但我想确认它按预期工作......
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);
}