6

ソケットの読み取りで発生するようなまれなバグがあります。

データの読み取り中に、これよりも大きいデータ パッケージの 1 ~ 3 バイトしか取得できないことがあるようです。

パイププログラミングから学んだように、送信者が十分なデータを提供する限り、常に少なくとも 512 バイトを取得します。

また、私の送信者は、何かを送信するたびに少なくとも4バイト以上送信します-したがって、送信の最初(!!)に少なくとも4バイトが一度に受信されると考えていました。

すべてのケースの 99.9% で、私の仮定は成り立つように見えますが、4 バイト未満しか受信されないという非常にまれなケースがあります。ばかげているように思えますが、なぜネットワークシステムがこれを行う必要があるのでしょうか?

誰かもっと知っていますか?

私が使用する読み取りコードは次のとおりです。

mySock, addr = masterSock.accept()
mySock.settimeout(10.0)
result = mySock.recv(BUFSIZE)
# 4 bytes are needed here ...
...
# read remainder of datagram
...

送信者は、send の 1 回の呼び出しで完全なデータグラムを送信します。

編集: すべてが localhost で動作しているため、複雑なネットワーク アプリケーション (ルーターなど) は必要ありません。BUFSIZE は少なくとも 512 で、送信者は少なくとも 4 バイトを送信します。

4

8 に答える 8

15

TCPを使用していると思います。TCP は、パケットやメッセージの境界を考慮しないストリーム ベースのプロトコルです。

これは、読み取りを行うと、要求したよりもバイト数が少なくなる可能性があることを意味します。たとえば、データが 128k の場合、最初の読み取りで 24k しか取得できず、残りのデータを取得するには再度読み取る必要があります。

C での例:

int read_data(int sock, int size, unsigned char *buf) {
   int bytes_read = 0, len = 0;
   while (bytes_read < size && 
         ((len = recv(sock, buf + bytes_read,size-bytes_read, 0)) > 0)) {
       bytes_read += len;
   }
   if (len == 0 || len < 0) doerror();
   return bytes_read;
}
于 2009-08-09T14:43:25.513 に答える
9

私の知る限り、この動作は完全に合理的です。ソケットは、送信時にデータを断片化する可能性があり、おそらく断片化します。適切なバッファリング手法を適用して、このようなケースを処理する準備をする必要があります。

一方、localhost でデータを送信していて、実際に 4 バイトしか受信していない場合は、コードのどこかにバグがある可能性があります。

編集: アイデア - パケット スニファーを起動して、送信されたパケットがいっぱいになるかどうかを確認してみてください。これにより、バグがクライアントまたはサーバーにあるときはいつでも、洞察が得られる場合があります。

于 2009-08-09T13:45:38.213 に答える
5

「ソケットから読み取る: 少なくとも x バイトを取得することが保証されていますか?」という質問に対する単純な答えは、いいえです。これらのソケット メソッドのドキュメント文字列を見てください。

>>> import socket
>>> s = socket.socket()
>>> print s.recv.__doc__
recv(buffersize[, flags]) -> data

Receive up to buffersize bytes from the socket.  For the optional flags
argument, see the Unix manual.  When no data is available, block until
at least one byte is available or until the remote end is closed.  When
the remote end is closed and all data is read, return the empty string.
>>> 
>>> print s.settimeout.__doc__
settimeout(timeout)

Set a timeout on socket operations.  'timeout' can be a float,
giving in seconds, or None.  Setting a timeout of None disables
the timeout feature and is equivalent to setblocking(1).
Setting a timeout of zero is the same as setblocking(0).
>>> 
>>> print s.setblocking.__doc__
setblocking(flag)

Set the socket to blocking (flag is true) or non-blocking (false).
setblocking(True) is equivalent to settimeout(None);
setblocking(False) is equivalent to settimeout(0.0).

recv()このことから、要求したバイト数を返す必要がないことは明らかです。また、 を呼び出しているsettimeout(10.0)ため、 の有効期限が近づくと、すべてではなく一部のデータが受信される可能性がありrecv()ます。その場合、recv()読み取ったものを返します-これは、要求したよりも少なくなります(ただし、一貫して4バイト未満になる可能性は低いようです)。

datagramあなたは、(コネクションレス) UDP ソケット (TCP ではない) を使用していることを意味する質問に言及しています。区別については、こちらで説明しています。投稿されたコードはソケットの作成を示していないため、ここでは推測することしかできませんが、この詳細は重要な場合があります。コードのより完全なサンプルを投稿していただけると助かります。

問題が再現可能な場合は、タイムアウトを無効にして (偶然にも処理していないようです)、問題が解決するかどうかを確認できます。

于 2009-08-10T04:39:06.937 に答える
3

これは、TCPが機能する方法です。すべてのデータを一度に取得することはありません。送信者のオペレーティングシステム、NIC、ルーター、スイッチ、ワイヤ自体、受信者のNIC、OSなど、送信者と受信者の間のタイミングの問題が多すぎます。ハードウェアとOSにバッファがあります。

TCPネットワークがOSパイプと同じであると想定することはできません。パイプを使用すると、すべてソフトウェアであるため、ほとんどのメッセージに対してメッセージ全体を一度に配信するためのコストはかかりません。ネットワークでは、単純なネットワークであっても、タイミングの問題があると想定する必要があります。

そのため、recv()は一度にすべてのデータを提供することはできません。すべてが正常に機能している場合でも、データが利用できない可能性があります。通常、recv()を呼び出して、出力をキャッチします。これで、受信したバイト数がわかります。予想よりも少ない場合は、正しいバイト数が得られるまで、(提案されているように)recv()を呼び出し続ける必要があります。ほとんどの場合、recv()はエラー時に-1を返すことに注意してください。そのため、それを確認し、ドキュメントでERRNO値を確認してください。特にEAGAINは人々の問題を引き起こしているようです。詳細はインターネットで読むことができますが、思い出すと、現時点ではデータがないので、もう一度やり直してください。

また、あなたの投稿から、送信者が送信する必要のあるデータを送信していることを確認しているようですが、完全を期すために、これを確認してください:http: //beej.us/guide/bgnet/output/html/multipage/ Advanced.html#sendall

部分的な受信を処理するには、recv()側で同様のことを行う必要があります。パケットサイズが固定されている場合は、期待する量のデータが得られるまで読む必要があります。パケットサイズが可変の場合は、送信するデータの量を示すヘッダーができるまで読み取ってから()、さらに多くのデータを読み取る必要があります。

于 2009-08-09T14:55:53.100 に答える
1

それでも興味がある場合は、次のようなパターン:

# 4 bytes are needed here ......
# read remainder of datagram...

ばかげた窓のことを作成する可能性があります。

これをチェックしてください

于 2009-09-27T22:15:33.980 に答える
1

recv http://linux.about.com/library/cmd/blcmdl2_recv.htmの Linux man ページから:

受信呼び出しは通常、要求された全量の受信を待つのではなく、要求された量までの利用可能なデータを返します。

したがって、送信者がまだバイトを送信している場合、呼び出しはこれまでに送信されたものだけを返します。

于 2009-08-09T13:45:40.773 に答える
1

送信者が 515 バイトを送信し、BUFSIZE が 512 の場合、最初の recv は 512 バイトを返し、次は 3 バイトを返します...これは何が起こっているのでしょうか?

(これは、より大きな送信から 3 バイトの recv になる多くのケースの 1 つにすぎません...)

于 2009-08-09T13:47:29.477 に答える