1

リスニングソケットをシャットダウンするためのサポートを探しています。私が抱えている問題は、Shutdown()直前に呼び出したときにClose()、これら2つの呼び出しの1つが、最後に受信したデータのソケットで偽の読み取りをトリガーしているように見えることです。どうすれば止められますか?

私のアプリには、FTPのように2組のソケットがあります。1つはリモートサーバーに接続するクライアントであり、もう1つはリモートホストからの2番目の接続をリッスンして受け入れるサーバーです。このインバウンド接続は非同期に接続されOnReceived、リモートホストからダウンする一方的なデータを処理します。

これはすべて正常に機能し、両方のソケットを数日または数週間接続したままにすることができます。しかし、何か問題が発生した場合は、すべてをシャットダウンしてやり直すことで対応します。inboundSocket.Shutdown()またはinbounSocket.Close()(どちらが2番目のスレッドでデバッグするのが難しいかわからない)の呼び出し中に、インバウンドソケットの最後のインバウンドパケットを再読み取りしているように見えます。これにより、さらに多くの問題が発生します。

どうすればシャットダウンできますか。この再読み取りを強制せずに、閉じる、切断するなど?

以下のサンプルコードは、要点を示すために削除されています。

前もって感謝します。

ダニエル

public class TcpIpSenderReceiver
{ 
    /// <summary>
    /// Main thread, main entry point 
    /// </summary>
    public void ConnectAndLogonAndStartReceivingInboundMessages()
    {   
        CreateListenerAndStartListening();
        AcceptInboundSocketAndHookUpDataReceiptCallBack();          
    }

    /// <summary>
    /// Main thread
    /// </summary>
    int CreateListenerAndStartListening()
    { 
        tcpListener = new TcpListener(LocalBoundIpAddress, listeningPort);
        tcpListener.Start();        
    }


    /// <summary>
    /// SECOND thread
    /// </summary>
    void AcceptInboundSocketAndHookUpDataReceiptCallBack()
    {
        int i = 0;
        while (i < 100 && !tcpListener.Pending())
        {
            i++;
            Thread.Sleep(100);
        }
        inboundSocket = tcpListener.AcceptSocket();
        bufferForReceive = new byte[bufferSize];
        WireUpCallbackForAsynchReceive();
    }

    /// <summary>
    /// SECOND thread
    /// </summary>
    void WireUpCallbackForAsynchReceive()
    {
        if (asynchCallbackFunctionForReceive == null)
        {
            asynchCallbackFunctionForReceive = new AsyncCallback(OnDataReceived);
        }
        if (inboundSocket.Connected)
        {
            try
            {
                asyncResultForReceive = inboundSocket.BeginReceive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None, asynchCallbackFunctionForReceive, null);
            }
            catch (Exception)
            {
                //...
            }
        }
    }


    /// <summary>
    /// SECOND thread
    /// </summary>
    void OnDataReceived(IAsyncResult asyn)
    {
        // Read data call goes here.....

        if (asyncResultForReceive != null)
        {
            inboundSocket.EndReceive(asyncResultForReceive);  
        }
        WireUpCallbackForAsynchReceive();  // listen again for next inbound message
    } 


    void Shutdown()
    {
        shouldAbortThread = true;
        asyncResultForReceive = null;
        asynchCallbackFunctionForReceive = null;
        if (outboundStreamWriter != null)
        {
            try
            {
                outboundStreamWriter.Close();
                outboundStreamWriter.Dispose();
                outboundStreamWriter = null;
            }
            finally { }
        }
        if (outboundNetworkStream != null)
        {
            try
            {
                outboundNetworkStream.Close();
                outboundNetworkStream.Dispose();
                outboundNetworkStream = null;
            }
            finally { }
        }
        if (tcpClient != null)
        {
            try
            {
                tcpClient.Close();
                tcpClient = null;
            }
            catch (SocketException)
            {
                // ...
            }
        }
        if (inboundSocket != null)
        {
            try
            {
                // I think this is where it's puking
                inboundSocket.Shutdown(SocketShutdown.Both);
                inboundSocket.Close();
                inboundSocket = null;
            }
            catch (SocketException)
            {
                //...
            }
        }
        if (tcpListener != null)
        {
            try
            {
                tcpListener.Stop();
                tcpListener = null;
            }
            catch (SocketException)
            {
                //...
            }
        }
    }


    #region Local variables

    volatile bool shouldAbortThread;
    TcpListener tcpListener;
    TcpClient tcpClient;
    StreamWriter outboundStreamWriter;
    NetworkStream outboundNetworkStream;
    Socket inboundSocket = null;
    IAsyncResult asyncResultForReceive;
    public AsyncCallback asynchCallbackFunctionForReceive;
    byte[] bufferForReceive;
    static string HostnameShared;
    #endregion
}
4

2 に答える 2

1

(コメントと、の戻り値を無視しているという事実から)を呼び出す前に、バッファ内のデータを処理しているOnDataReceivedように見えます。この場合、ソケットから実際に読み取られたバイト数(ソケットがシャットダウンされている場合は0になる可能性があります)がわからないため、からのバッファーに残っているデータを処理しています。以前に読み取ったため、古いデータを再読み取りしたように見えます。EndReceiveEndReceiveinboundSocket

データを処理するEndReceive に電話をかける必要があります。何かのようなもの:

void OnDataReceived(IAsyncResult asyn)
{
    var bytesRead = inboundSocket.EndReceive(asyn);
    if (bytesRead == 0) return; // Socket is closed

    // Process the data here

    WireUpCallbackForAsynchReceive();  // listen again for next inbound message
} 

さらに、の戻り値をチェックしていなかったという事実は、EndReceive各受信がバッファ全体に相当するデータ、またはサーバーがSend/への呼び出しで送信したのと同じバイト数を返すことを期待していることを示しています。 BeginSend、常にそうであるとは限りません。

于 2013-03-19T15:53:55.567 に答える
0

私はこれを次のように回避しました。コールバック関数を提供してフレームワークに2番目のスレッドを処理させる代わりに、明示的に独自のワーカースレッドを開始し、データが受信されるまでブロックさせました。ゼロバイトが受信された場合、もう一方の端がシャットダウンしていて、私の端をうまく終了できることがわかります。私が共有したかった唯一の落とし穴は、ブロッキングsocket.Receive()関数を試してキャッチし、SocketError.Interrupted例外を処理する必要があるということでした。これは、メインプログラムが終了したときに発生します。

コードは次のようになります。

        Thread dataReceivedWorkerThread;

    void AcceptInboundSocketAndStartWorkerThread()
    {            
        inboundSocket = tcpListener.AcceptSocket();
        dataReceivedWorkerThread = new Thread(new ThreadStart(ListenForAndWaitToReceiveInboundData));
        dataReceivedWorkerThread.Start();
    }

    void ListenForAndWaitToReceiveInboundData()
    {
            var bytesRead = 0;
            try
            {
                bytesRead = inboundSocket.Receive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None); // blocks here                   
            }
            catch (SocketException se)
            {
                if (se.SocketErrorCode == SocketError.Interrupted) 
                {
                    // Handle shutdown by (e.g.) parent thread....                      
                }
                else
                {
                    // handle other
                }
                return;
            }
            if (bytesRead == 0)
            {
                // handle shutdown from other end
                return;
            }

            // Do stuff with received data....
        }
    }
于 2014-01-03T10:14:14.740 に答える