3

StreamReader と StreamWriter を使用して、TCPClient.NetworkStream 経由でデータを送受信したいと考えています。コードを以下に示します。
クライアント コード:

 using (TcpClient client = new TcpClient())
            {
                IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001);
                client.Connect(localEndPoint);
                client.NoDelay = true;

                using(NetworkStream stream = client.GetStream())
                {                    
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        writer.AutoFlush = true;
                        using (StreamReader reader = new StreamReader(stream))
                        {    

                            string message = "Client says: Hello";
                            writer.WriteLine(message);

                // If I comment the line below, the server receives the  first message
                // otherwise it keeps waiting for data
                           string response = reader.ReadToEnd();
                           Console.WriteLine(response);
                        }
                       ;
                    }
                }
            }


注:reader.ReadToEnd();にコメントすると それ以外の場合、サーバーはクライアントからのデータを待ち続けます。ReadToEnd が問題ですか?

サーバー コードは非同期で接続を受け入れますが、同期的にデータを処理します。

class Program
    {
        private static AsyncCallback tcpClientCallback;
        static void Main(string[] args){

            IPEndPoint localEndPoint =  new IPEndPoint(IPAddress.Parse("127.0.0.1"),9001);
            TcpListener listener = new TcpListener(localEndPoint);

            listener.Start();

            tcpClientCallback = new AsyncCallback(ConnectCallback);
            AcceptConnectionsAysnc(listener);

            Console.WriteLine("Started Aysnc TCP listener, press any key to exit (server is free to do other tasks)");
            Console.ReadLine();
        }

        private static void AcceptConnectionsAysnc(TcpListener listener)
        {
            listener.BeginAcceptTcpClient(tcpClientCallback, listener);
        }

        static void ConnectCallback(IAsyncResult result)
        {
            Console.WriteLine("Received a conenct request");
            TcpListener listener = (TcpListener)result.AsyncState;

            // We are starting a thread which waits to receive the data
            // This is not exactly scalable
            Task recieveTask = new Task(() => { HandleRecieve(result, listener); });
            recieveTask.Start();

            AcceptConnectionsAysnc(listener);
        }


        // This makes a blocking call - that means until the client is ready to
        // send some data the server waits
        private static void HandleRecieve(IAsyncResult result, TcpListener listener)
        {
            Console.WriteLine("Waiting for data");

            using (TcpClient client = listener.EndAcceptTcpClient(result))
            {
                client.NoDelay = true;

                using (NetworkStream stream = client.GetStream())
                {
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        using (StreamWriter writer = new StreamWriter(stream))
                        {
                            writer.AutoFlush = true;
                            Stopwatch watch = new Stopwatch();
                            watch.Start();
                            string data = reader.ReadToEnd();
                            watch.Stop();
                            Console.WriteLine("Time: " + watch.Elapsed.TotalSeconds);
                            Console.WriteLine(data);


// Send a response to client
                                writer.WriteLine("Response: " + data);
                            }

                        }
                    }            


                }

            }

        } 
4

1 に答える 1

7

ストリームはクローズされるまで終了しません。現在、クライアントとサーバーの両方が接続を開いたままにしており、両方が他方から最後まで読み取ろうとしています。これはデッドロックです。どちらも最後まで到達しないため、どちらも閉じることはありません (これにより、もう一方が最後まで到達する可能性があります)。

オプション:

  • raw ソケットを使用し、送信後にクライアントにアウトバウンドストリームを閉じさせます。

    socket.Shutdown(SocketShutdown.Send);
    

    これにより、サーバーは最後まで読み取れるようになり、サーバーは閉じられ、クライアントは最後まで読み取られるようになります。クライアントは、送信ソケットを閉じた後も受信ストリームから読み取ることができます。

  • とは異なる終了メタファを使用しReadToEndます。テキストベースのプロトコルの場合、行末が最も一般的です (つまり: ReadLine)。バイナリ プロトコルの場合、メッセージの長さプレフィックスが最も一般的です。これにより、ストリームの最後まで読み取ることなく、各エンドで次のメッセージを読み取ることができます。このシナリオでは、メッセージは 1 つだけです、戦略は機能します。

  • 上記が難しい場合は、別のプロトコルを使用してください。http は、単一の要求/応答操作に適しており、 を介してサーバーで処理できますHttpListener

さらに、ReadToEndサーバー (またはReadLine. サーバーが高スループットと多数の接続に対処する必要がある場合は、raw ソケットに基づく async-IO が優先されます。

于 2012-10-30T07:37:53.707 に答える