1

サーバーから送信されたバイトデータを読み取ろうとするクラスを作成しました。アプリが終了したら、ループも終了したいのですが、NetworkStream.Read()データが利用できない場合は待機しているように見えます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Threading.Tasks;
namespace Stream
{
    class Program
    {
        private static TcpClient client = new TcpClient("127.0.0.1", 80);
        private static NetworkStream stream = client.GetStream();

        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        void Run()
        {
            ThreadPool.QueueUserWorkItem(x =>
            {
                while (true)
                {
                    stream.Read(new byte[64], 0, 64);
                }
            });

            client.Close();
            // insert Console.ReadLine(); if you want to see an exception occur.
        }
    }
}

このコードを使用すると、

  1. System.IO.IOException「トランスポート接続からデータを読み取れません:既存の接続がリモートホストによって強制的に閉じられました」と表示されます。
  2. ObjectDisposedException「廃棄されたオブジェクトにアクセスできません」と言ったり、
  3. System.IO.IOException「WSACancelBlockingCallの呼び出しによってブロッキング操作が中断されました」と表示されます。

では、どうすればこのメソッドを安全に終了できますか?

4

3 に答える 3

5

プログラムを実行しているときにわかることから、最初の例外「トランスポート接続からデータを読み取れません...」は、Stream.ReadではなくTcpClientのコンストラクターによってスローされ、おそらく存在するという事実が原因です127.0.0.1:80 で接続を受け入れるサーバーがありません。

Stream.Read でうまく終了したい場合は、非同期で行います。このようにして、読み取り中にスローされた例外をキャッチし、それに応じてプログラムをクリーンアップできます。プログラムの修正版は次のとおりです。

using System;
using System.Net.Sockets;

namespace Stream
{
    class Program
    {
        private static TcpClient client;
        private static NetworkStream stream;

        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        void Run()
        {
            try
            {
                client = new TcpClient("127.0.0.1", 80);
                stream = client.GetStream();
                byte[] buffer = new byte[64];

                stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnRead), buffer);

                client.Close();
                Console.ReadKey();
            }
            catch (Exception)
            {
                //...
            }
        }

        void OnRead(IAsyncResult result)
        {
            try
            {
                stream.EndRead(result);
                byte[] buffer = result.AsyncState as byte[];

                if (buffer != null)
                {
                    //...
                }

                // continue to read the next 64 bytes
                buffer = new byte[64];
                stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnRead), buffer);
            }
            catch (Exception)
            {
                // From here you can get exceptions thrown during the asynchronous read
            }
        }
    }
}
于 2013-02-25T20:29:29.240 に答える
5
  1. ブロックを回避するには、DataAvailable が true の場合にのみ Read() を呼び出します。
  2. ThreadPool は長時間実行されるタスクには適していないため、while ループをリファクタリングします
  3. 接続から読み取る前に、接続を閉じます (作業項目が非同期で実行されるため)。

これは役立つかもしれません

     void Run() {
        ThreadPool.QueueUserWorkItem(ReadLoop);
     }

     void ReadLoop(object x) {
         if (stream.DataAvailable) 
           stream.Read(new byte[64], 0, 64);
         else 
             Thread.Sleep(TimeSpan.FromMilliseconds(200));

         if (Finished)
               client.Close();
         else if (!Disposed && !Finished) 
               ThreadPool.QueueUserWorkItem(ReadLoop);
     }

Finished と Disposed は実際のクラスで自分で管理する必要があります。

于 2013-02-23T08:48:16.867 に答える
3

単純なコンソール クライアント アプリケーションが実装されている場合、別のスレッドを使用して接続し、サーバーからデータを受信する必要はありません。

static void Main(string[] args)
{
    var client = new TcpClient("...", ...);
    var buffer = new byte[1024];
    using (var networkStream = client.GetStream())
    {
        int bytesRead;
        while ((bytesRead = networkStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            var hexString = BitConverter.ToString(buffer, 0, bytesRead);
            Console.WriteLine("Bytes received: {0}", hexString);
        }
    }
}
于 2013-02-23T11:18:46.853 に答える