14

MSG_PEEKC ++には、ソケットからバイトを受信する次の関数があり、フラグで使用可能なバイト数をチェックできます。のMSG_PEEK場合、「recv」の戻り値は、ソケットで使用可能なバイト数です。

#include <sys/socket.h>
ssize_t recv(int socket, void *buffer, size_t length, int flags); 

buffer作成せずに(メモリを割り当てずに)、ソケットで使用可能なバイト数を取得する必要がありますbuffer。それは可能ですか、そしてどのように?

4

4 に答える 4

37

あなたが探しているのはioctl(fd,FIONREAD,&bytes_available)、そして窓の下ioctlsocket(socket,FIONREAD,&bytes_available)です。

ただし、OSは必ずしもバッファリングするデータの量を保証するわけではないため、大量のデータを待っている場合は、データが入ってくるときにデータを読み込んで独自のバッファに保存する方がよいでしょう。何かを処理するために必要なものがすべて揃うまで。

これを行うには、通常、次のように一度にチャンクを読み取るだけです。

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たり、判断したりする必要があります。また、recvでブロックしないようにする場合は、ソケットのフラグが非常に便利です。 。epollO_NONBLOCK

于 2012-10-20T03:00:30.937 に答える
3

Windowsでは、フラグioctlsocket()付きの関数を使用しFIONREADて、実際のバイト自体を読み取ったり覗いたりすることなく、使用可能なバイト数をソケットに問い合わせることができます。recv()返される値は、ブロックせずに返すことができる最小バイト数です。実際に呼び出すrecv()までに、さらに多くのバイトが到着している可能性があります。

于 2012-10-20T03:39:39.827 に答える
1
The 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 ?
于 2021-03-15T15:27:58.627 に答える
0

使用するときは注意してくださいFIONREAD!を使用する際の問題ioctl(fd, FIONREAD, &available)は、一部のシステムでは、ソケットバッファで読み取ることができる合計バイト数が常に返されることです。

これは、STREAMソケット(TCP)の場合は問題ありませんが、DATAGRAMソケット(UDP)の場合は誤解を招く恐れがあります。データグラムソケットの場合、読み取り要求はバッファ内の最初のデータグラムのサイズに制限され、最初のデータグラムのサイズよりも小さい値を読み取る場合でも、そのデータグラムの未読バイトはすべて破棄されます。したがって、理想的には、バッファ内の次のデータグラムのサイズのみを知りたいと考えています。

たとえば、macOS / iOSでは、常に合計金額を返すことが文書化FIONREADされています(に関するコメントを参照SO_NREAD)。次のデータグラムのサイズ(およびストリームソケットの合計サイズ)のみを取得するには、次のコードを使用できます。

int available;
socklen_t optlen = sizeof(readable);
int err = getsockopt(soc, SOL_SOCKET, SO_NREAD, &available, &optlen);

Linuxでは、UDPソケットの次のデータグラムのサイズのみFIONREADを返すように文書化されています。

Windowsではioctlsocket(socket, FIONREAD, &available)、常に合計サイズを指定するように文書化されています。

sパラメーターで渡されたソケットがメッセージ指向の場合(たとえば、SOCK_DGRAMと入力)、FIONREADは、ソケットでキューに入れられた最初のデータグラム(メッセージ)のサイズではなく、読み取ることができる合計バイト数をレポートに返します。

ソース:https ://docs.microsoft.com/en-us/windows/win32/api/ws2spi/nc-ws2spi-lpwspioctl

Windowsでのみ最初のデータグラムのサイズを取得する方法を知りません。

于 2021-12-09T11:32:33.523 に答える