12

そのため、ブロッキング Read() は、送信されたすべてのデータの受信が完了する前に戻る可能性があるように見えます。次に、問題のストリームからの DataAvailable 値によって制御されるループで Read() をラップします。問題は、この while ループ内でより多くのデータを受信できることですが、システムにこれを知らせるための舞台裏の処理が行われていないことです。私がネット上で見つけた解決策のほとんどは、何らかの形で私には適用できませんでした.

私がやったことは、ループの最後のステップとして、ストリームから各ブロックを読み取った後に単純な Thread.Sleep(1) を実行することです。これはシステムを更新する時間を与えているようで、正確な結果が得られていませんが、これは少しハックで、解決策としてはかなり「状況に応じた」ものに思えます。

私が対処している状況のリストを次に示します。 IIS アプリケーションとスタンドアロン アプリケーションの間の単一の TCP 接続。どちらも送受信通信用に C# で記述されています。リクエストを送信し、レスポンスを待ちます。このリクエストは HTTP リクエストによって開始されますが、HTTP リクエストからデータを読み取るこの問題は発生していません。事後です。

着信接続を処理するための基本的なコードは次のとおりです

protected void OnClientCommunication(TcpClient oClient)
{
    NetworkStream stream = oClient.GetStream();
    MemoryStream msIn = new MemoryStream();

    byte[] aMessage = new byte[4096];
    int iBytesRead = 0;

    while ( stream.DataAvailable )
    {
        int iRead = stream.Read(aMessage, 0, aMessage.Length);
        iBytesRead += iRead;
        msIn.Write(aMessage, 0, iRead);
        Thread.Sleep(1);
    }
    MemoryStream msOut = new MemoryStream();

    // .. Do some processing adding data to the msOut stream

    msOut.WriteTo(stream);
    stream.Flush();

    oClient.Close();
}

より良い解決策のためのすべてのフィードバックを歓迎します。または、DataAvailable の値を確認する前に、適切に更新できるようにするために Sleep(1) を実行する必要があることを歓迎します。

2年後、この質問に対する答えが今の状況ではないことを願っています:)

4

8 に答える 8

12

読み取る必要があるデータの量を知る必要があります。データがなくなるまで単純にデータの読み取りをループすることはできません。

これが、HTTP GET の結果の HTTP ヘッダーにバイト カウントがある理由です。クライアント側は、すべてのデータをいつ受信したかを知ることができます。

相手側の送信内容を制御できるかどうかに応じて、次の 2 つの解決策があります。

  1. 「フレーミング」文字: (SB)data(EB) を使用します。ここで、SB と EB は開始ブロック文字と終了ブロック文字 (選択したもの) ですが、データ内では使用できません。EB を「見る」と、完了したことがわかります。

  2. 各メッセージの前に長さフィールドを実装して、続くデータの量を示します: (len)data。(len) バイトを読み取り、次に (len) バイトを読み取ります。必要に応じて繰り返します。

これは、長さゼロの読み取りがデータの終わりを意味するファイルからの読み取りとは異なります (反対側が切断されたことを意味しますが、それは別の話です)。

3 つ目の (推奨されません) 解決策は、タイマーを実装することです。 データの取得を開始したら、タイマーを設定します。受信ループが一定時間 (たとえば、データが頻繁に来ない場合は数秒間) アイドル状態になっている場合は、おそらくそれ以上データが来ていないと想定できます。この最後の方法は最後の手段です...信頼性が低く、調整が難しく、脆弱です。

于 2012-01-31T23:19:04.730 に答える
10

これには問題があります。
通信がループよりも高速になることを期待していますがwhile()、これはほとんどありません。
ループはwhile()データがなくなるとすぐに終了しますが、終了直後の数ミリ秒ではそうではない場合があります。

一定量のバイトを期待していますか? 解雇
される頻度は?OnClientCommunication()誰がそれを引き起こしますか?

while()ループ後のデータはどうしますか? 以前のデータに追加し続けますか?

DataAvailable WILLは false を返します。これは、通信よりも高速に読み取りを行っているためです。このコード ブロックに戻って、さらに多くのデータを処理する場合にのみ問題ありません。

于 2010-11-23T22:11:53.057 に答える
2

ネットワーク ストリームからデータを読み取る前に DataAvailable をチェックしようとしたところ、false が返されましたが、1 バイトを読み取った後は true が返されました。だから私はMSDNのドキュメントをチェックし、彼らもチェックする前に読んだ. このパターンに従うために、while ループを do while ループに再配置します。

http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.dataavailable.aspx

        // Check to see if this NetworkStream is readable. 
        if(myNetworkStream.CanRead){
            byte[] myReadBuffer = new byte[1024];
            StringBuilder myCompleteMessage = new StringBuilder();
            int numberOfBytesRead = 0;

            // Incoming message may be larger than the buffer size. 
            do{
                 numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);

                 myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));

            }
            while(myNetworkStream.DataAvailable);

            // Print out the received message to the console.
            Console.WriteLine("You received the following message : " +
                                         myCompleteMessage);
        }
        else{
             Console.WriteLine("Sorry.  You cannot read from this NetworkStream.");
        }
于 2012-08-19T10:02:16.137 に答える
0

この例では、データ、つまりテキスト メッセージを送受信するためのアルゴリズムを示します。ファイルを送信することもできます。

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;

namespace Network
{
    /// <summary>
    /// Represents a network stream for transferring data.
    /// </summary>
    public class NetworkStream
    {
        #region Fields
        private static readonly byte[] EmptyArray = Array.Empty<byte>();
        private readonly Socket m_Socket;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the class <seealso cref="NetworkStream"/>.
        /// </summary>
        /// <param name="socket">
        /// Berkeley socket interface.
        /// </param>
        public NetworkStream(Socket socket)
        {
            m_Socket = socket ?? throw new ArgumentNullException(nameof(socket));
        }
        #endregion

        #region Properties

        #endregion

        #region Methods
        /// <summary>
        /// Sends a message.
        /// </summary>
        /// <param name="message">
        /// Message text.
        /// </param>
        /// <exception cref="ArgumentNullException"/>
        public void Send(string message)
        {
            if (message is null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            byte[] data = Encoding.UTF8.GetBytes(message);
            Write(data);
        }

        /// <summary>
        /// Receives the sent message.
        /// </summary>
        /// <returns>
        /// Sent message.
        /// </returns>
        public string Receive()
        {
            byte[] data = Read();
            return Encoding.UTF8.GetString(data);
        }

        /// <summary>
        /// Receives the specified number of bytes from a bound <seealso cref="Socket"/>.
        /// </summary>
        /// <param name="socket">
        /// <seealso cref="Socket"/> for receiving data.
        /// </param>
        /// <param name="size">
        /// The size of the received data.
        /// </param>
        /// <returns>
        /// Returns an array of received data.
        /// </returns>
        private byte[] Read(int size)
        {
            if (size < 0)
            {
                // You can throw an exception.
                return null;
            }

            if (size == 0)
            {
                // Don't throw an exception here, just return an empty data array.
                return EmptyArray;
            }

            // There are many examples on the Internet where the
            // Socket.Available property is used, this is WRONG!

            // Important! The Socket.Available property is not working as expected.
            // Data packages may be in transit, but the Socket.Available property may indicate otherwise.
            // Therefore, we use a counter that will allow us to receive all data packets, no more and no less.
            // The cycle will continue until we receive all the data packets or the timeout is triggered.

            // Note. This algorithm is not designed to work with big data.

            SimpleCounter counter = new(size, m_Socket.ReceiveBufferSize);
            byte[] buffer = new byte[counter.BufferSize];
            int received;

            using MemoryStream storage = new();

            // The cycle will run until we get all the data.
            while (counter.IsExpected)
            {
                received = m_Socket.Receive(buffer, 0, counter.Available, SocketFlags.None);
                // Pass the size of the received data to the counter.
                counter.Count(received);
                // Write data to memory.
                storage.Write(buffer, 0, received);
            }

            return storage.ToArray();
        }

        /// <summary>
        /// Receives the specified number of bytes from a bound <seealso cref="Socket"/>.
        /// </summary>
        /// <returns>
        /// Returns an array of received data.
        /// </returns>
        private byte[] Read()
        {
            byte[] sizeData;
            // First, we get the size of the master data.
            sizeData = Read(sizeof(int));
            // We convert the received data into a number.
            int size = BitConverter.ToInt32(sizeData);

            // If the data size is less than 0 then throws an exception.
            // We inform the recipient that an error occurred while reading the data.

            if (size < 0)
            {
                // Or return the value null.
                throw new SocketException();
            }

            // If the data size is 0, then we will return an empty array.
            // Do not allow an exception here.

            if (size == 0)
            {
                return EmptyArray;
            }

            // Here we read the master data.
            byte[] data = Read(size);
            return data;
        }

        /// <summary>
        /// Writes data to the stream.
        /// </summary>
        /// <param name="data"></param>
        private void Write(byte[] data)
        {
            if (data is null)
            {
                // Throw an exception.
                // Or send a negative number that will represent the value null.
                throw new ArgumentNullException(nameof(data));
            }

            byte[] sizeData = BitConverter.GetBytes(data.Length);

            // In any case, we inform the recipient about the size of the data.
            m_Socket.Send(sizeData, 0, sizeof(int), SocketFlags.None);

            if (data.Length != 0)
            {
                // We send data whose size is greater than zero.
                m_Socket.Send(data, 0, data.Length, SocketFlags.None);
            }
        }
        #endregion

        #region Classes
        /// <summary>
        /// Represents a simple counter of received data over the network.
        /// </summary>
        private class SimpleCounter
        {
            #region Fields
            private int m_Received;
            private int m_Available;
            private bool m_IsExpected;
            #endregion

            #region Constructors
            /// <summary>
            /// Initializes a new instance of the class <seealso cref="SimpleCounter"/>.
            /// </summary>
            /// <param name="dataSize">
            /// Data size.
            /// </param>
            /// <param name="bufferSize">
            /// Buffer size.
            /// </param>
            /// <exception cref="ArgumentOutOfRangeException"/>
            public SimpleCounter(int dataSize, int bufferSize)
            {
                if (dataSize < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(dataSize), dataSize, "Data size cannot be less than 0");
                }

                if (bufferSize < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(dataSize), bufferSize, "Buffer size cannot be less than 0");
                }

                DataSize = dataSize;
                BufferSize = bufferSize;

                // Update the counter data.
                UpdateCounter();
            }
            #endregion

            #region Properties
            /// <summary>
            /// Returns the size of the expected data.
            /// </summary>
            /// <value>
            /// Size of expected data.
            /// </value>
            public int DataSize { get; }

            /// <summary>
            /// Returns the size of the buffer.
            /// </summary>
            /// <value>
            /// Buffer size.
            /// </value>
            public int BufferSize { get; }

            /// <summary>
            /// Returns the available buffer size for receiving data.
            /// </summary>
            /// <value>
            /// Available buffer size.
            /// </value>
            public int Available
            {
                get
                {
                    return m_Available;
                }
            }

            /// <summary>
            /// Returns a value indicating whether the thread should wait for data.
            /// </summary>
            /// <value>
            /// <see langword="true"/> if the stream is waiting for data; otherwise, <see langword="false"/>.
            /// </value>
            public bool IsExpected
            {
                get
                {
                    return m_IsExpected;
                }
            }
            #endregion

            #region Methods
            // Updates the counter.
            private void UpdateCounter()
            {
                int unreadDataSize = DataSize - m_Received;
                m_Available = unreadDataSize < BufferSize ? unreadDataSize : BufferSize;
                m_IsExpected = m_Available > 0;
            }

            /// <summary>
            /// Specifies the size of the received data.
            /// </summary>
            /// <param name="bytes">
            /// The size of the received data.
            /// </param>
            public void Count(int bytes)
            {
                // NOTE: Counter cannot decrease.

                if (bytes > 0)
                {
                    int received = m_Received += bytes;
                    // NOTE: The value of the received data cannot exceed the size of the expected data.
                    m_Received = (received < DataSize) ? received : DataSize;

                    // Update the counter data.
                    UpdateCounter();
                }
            }

            /// <summary>
            /// Resets counter data.
            /// </summary>
            public void Reset()
            {
                m_Received = 0;
                UpdateCounter();
            }
            #endregion
        }
        #endregion
    }
}
于 2021-09-06T08:07:08.067 に答える