ソケットの同期受信

そんなことも知らなかったのとか言わないでね。知ってたはずなんだけどつながってなかったというか、MSDNの書き方がわかりにくかったというか(ry

疑問の発端

C#でXMLSocketとかShoutcastストリームとか受信するプログラムを書いていました。で、TcpClientからNetworkStreamを取得して、さてどうやって受信しようかと思い、MSDNを見ました。

Read 操作は、size パラメータで指定されたバイト数に達するまで、使用できるデータをすべて読み取ります。

http://msdn.microsoft.com/ja-jp/library/system.net.sockets.networkstream.read(v=vs.80).aspx

「えっバッファが一杯になるまでブロックしたままなの!?」
仕方なくバッファサイズを1にして、というかReadByteで1バイトずつ受信・確認していました。

なにかがおかしい

でも周りじゃ1バイトずつ読み取るなんて話は聞いたことがないし、同じようにXMLSocket扱うプログラムでも普通に大きめのバッファを使っている。かといってそのプログラムで受信が遅れてるようすもない。あれー??

やってみた

    static void Main(string[] args)
    {
        TcpListener listener = new TcpListener(9999);
        listener.Start();
        var c = listener.AcceptTcpClient();
        var s = c.GetStream();
        var buf = new byte[10];
        while (true)
        {
            Console.WriteLine(s.Read(buf, 0, buf.Length)); //受信した長さを表示
            Console.ReadLine();
        }
    }

こんなプログラムを書いて、Telnetで接続していろいろやってみた。

Readのブロック中にtelnetでキーを何度か押す

そのたびに1と表示。バッファサイズ関係なくブロック解除されてる。

Readのブロック中に長文(>10byte)を貼り付け

10と表示。Readを再度呼び出すと残りが受信された。

Readしてない時にキーを何度か(<=10byte)押す

Readしたときにまとめて受信された。

Readしてない時にキーを何度か(>10byte)押す

Readしたときにまとめて10バイト、次のReadで残り。

結果

バッファが一杯にならなくてもデータが途切れたらブロック解除される。
ということはReadByteなんてやらなくても一気に受信すればよかったのね。なるほど。

余談

.NET Framework 4のドキュメントでは微妙に表記が変わってました。

Read 操作では、size パラメーターで指定したバイト数までの、使用可能なデータをすべて読み取ります。

http://msdn.microsoft.com/ja-jp/library/system.net.sockets.networkstream.read(v=VS.100).aspx

「達するまで」よりは「までの」のほうが理解できた。前者は最大値というよりは「達しなければならない」というニュアンスを感じたのよ、と言い訳。
英語版では表記に違いはなかったからそっちを読んだほうが早かったかも・・・