C++有以下函数可以从套接字中接收字节,它可以使用MSG_PEEK
标志检查可用的字节数。使用MSG_PEEK
,'recv'返回的值是套接字中可用字节数:
#include <sys/socket.h>
ssize_t recv(int socket, void *buffer, size_t length, int flags);
我需要获取套接字中可用的字节数,但不想创建buffer
(不想为buffer
分配内存)。这是否可能,如何实现?
C++有以下函数可以从套接字中接收字节,它可以使用MSG_PEEK
标志检查可用的字节数。使用MSG_PEEK
,'recv'返回的值是套接字中可用字节数:
#include <sys/socket.h>
ssize_t recv(int socket, void *buffer, size_t length, int flags);
我需要获取套接字中可用的字节数,但不想创建buffer
(不想为buffer
分配内存)。这是否可能,如何实现?
ioctl(fd,FIONREAD,&bytes_available)
,在Windows下使用 ioctlsocket(socket,FIONREAD,&bytes_available)
。char buf[4096];
ssize_t bytes_read;
do {
bytes_read = recv(socket, buf, sizeof(buf), 0);
if (bytes_read > 0) {
/* do something with buf, such as append it to a larger buffer or
* process it */
}
} while (bytes_read > 0);
如果你不想坐在那里等待数据,你应该研究一下select
或epoll
,以确定何时可以读取数据,如果你想确保永远不会在接收上被阻塞,那么使用套接字的O_NONBLOCK
标志非常方便。
使用 FIONREAD
时要小心!使用 ioctl(fd, FIONREAD, &available)
的问题在于,在某些系统上它将始终返回套接字缓冲区中可读取的总字节数。
对于流式套接字(TCP)来说,这没有问题,但对于数据报套接字(UDP)来说却会误导人。对于数据报套接字的读取请求被限制为缓冲区中第一个数据报的大小,当读取少于第一个数据报大小时,该数据报的所有未读字节仍将被丢弃。因此,您最好只了解缓冲区中下一个数据报的大小。
例如,在 macOS/iOS 上有文档记录指出,FIONREAD
总是返回总量(请参阅有关 SO_NREAD
的注释)。要仅获取下一个数据报的大小(对于流式套接字则为总大小),您可以使用以下代码:
int available;
socklen_t optlen = sizeof(readable);
int err = getsockopt(soc, SOL_SOCKET, SO_NREAD, &available, &optlen);
FIONREAD
文档 只返回UDP套接字中下一个数据报的大小。ioctlsocket(socket, FIONREAD, &available)
文档始终提供总大小:FIONREAD
标志的ioctlsocket()
函数来查询套接字可用的字节数,而无需读取/查看实际的字节。返回的值是最小的recv()
函数可以不阻塞地返回的字节数。在您实际调用recv()
函数时,可能会有更多的字节到达。FIONREAD
并不返回从recv
获取而没有阻塞的字节数。在Windows上,recv
可以从较低层(例如过滤器和带有自己缓冲区的队列)拉取数据,而FIONREAD
则不会这样做,只检查顶层。参见第12项。 - David Schwartzs
读取... FIONREAD
返回可以在单个调用recv
函数中读取的数据量,这可能与套接字上排队的总数据量不同。*" - Remy LebeauFIONREAD
调用无法执行任何不可逆的操作,而 recv
可能会返回一个较小的数字。微软对非本机套接字操作的支持在许多方面都存在问题,这就是其中之一。这就是为什么强烈建议旨在在 Windows 上运行的软件使用 Windows API,而不是旨在使用 POSIXy 的那些 API。 - David Schwartzrecv()
调用MSG_PEEK
,但这不常见,而且我希望它能够通过过滤器读取数据并将其存储在缓冲区中,然后只需窥视缓冲区并将数据留在那里以供稍后的非窥视读取来删除它。 - Remy LebeauThe short answer is : this cannot be done with MS-Windows WinSock2,
as I can discovered over the last week of trying.
Glad to have finally found this post, which sheds some light on the issues I've been having, using latest Windows 10 Pro, version 20H2 Build 19042.867 (x86/x86_64) :
On a bound, disconnected UDP socket 'sk' (in Listening / Server mode):
1. Any attempt to use either ioctlsocket(sk, FIONREAD, &n_bytes)
OR WsaIoctl with a shifted FIONREAD argument, though they succeed,
and retern 0, after a call to select() returns > with that
'sk' FD bit set in the read FD set,
and the ioctl call returns 0 (success), and n_bytes is > 0,
causes the socket sk to be in a state where any
subsequent call to recv(), recvfrom(), or ReadFile() returns
SOCKET_ERROR with a WSAGetLastError() of :
10045, Operation Not Supported, or ReadFile
error 87, 'Invalid Parameter'.
Moreover, even worse:
2. Any attempt to use recv or recvfrom with the 'MSG_PEEK' msg_flags
parameter returns -1 and WSAGetLastError returns :
10040 : 'A message sent on a datagram socket was larger than
the internal message buffer or some other network limit,
or the buffer used to receive a datagram into was smaller
than the datagram itself.
' .
Yet for that socket I DID successfully call:
setsockopt(s, SOL_SOCKET, SO_RCVBUF, bufsz = 4096 , sizeof(bufsz) )
and the UDP packet being received was of only 120 bytes in size.
In short, with modern windows winsock2 ( winsock2.h / Ws2_32.dll) ,
there appears to be absolutely no way to use any documented API
to determine the number of bytes received on a bound UDP socket
before calling recv() / recvfrom() in MSG_WAITALL blocking mode to
actually receive the whole packet.
If you do not call ioctlsocket() or WsaIoctl or
recv{,from}(...,MSG_PEEK,...)
before entering recv{,from}(...,MSG_WAITALL,...) ,
then the recv{,from} succeeds.
I am considering advising clients that they must install and run
a Linux instance with MS Services for Linux under their windows
installation , and developing some
API to communicate with it from Windows, so that reliable
asynchronous UDP communication can be achieved - or does anyone
know of a good open source replacement for WinSock2 ?
I need access to a "C" library TCP+UDP/IP implementation for
modern Windows 10 that conforms to its own documentation,
unlike WinSock2 - does anyone know of one ?