1

送信と受信をそれぞれ処理する tcp 接続と 2 つのスレッドがあります。プログラムが開始されてから数分後に受信スレッドがデータを受信できず、Receive() メソッドからタイムアウト エラーが発生することがあります。しかし、Wireshark は、ワイヤ上にデータがあり、プログラムが受信を停止したため、受信ウィンドウのサイズがゼロに減少したことを示しています。

これは、Mac OS X 上の Parallels で実行されている Windows 7 である私のラップトップでより頻繁に発生するようです。

class Test
{
    public static string Host { get; set; }
    public static int Port { get; set; }
    public static Action<Exception> ErrorHandler { get; set; }

    private Socket socket;
    private object sendSync;

    private Thread recvThread;
    private Thread sendThread;
    private Thread connectThread;

    private readonly int RECEIVETIMOUT = 20 * 1000;
    private readonly int MAXCONNECTATTEMPTINTERVAL = 27;

    private int connectingAttempt = 0;

    private ManualResetEvent sendReadyEvent;
    private ManualResetEvent receiveReadyEvent;
    private ManualResetEvent sendStoppedEvent;
    private ManualResetEvent receiveStoppedEvent;


    private Queue<byte[]> waitingRequests;

    private Test()
    {
        sendSync = new object();
        waitingRequests = new Queue<byte[]>();

        sendReadyEvent = new ManualResetEvent(false);
        receiveReadyEvent = new ManualResetEvent(false);

        sendStoppedEvent = new ManualResetEvent(true);
        receiveStoppedEvent = new ManualResetEvent(true);

        sendThread = new Thread(this.Send);
        sendThread.IsBackground = true;
        sendThread.Start();

        recvThread = new Thread(this.Receive);
        recvThread.IsBackground = true;
        recvThread.Start();

        connectThread = new Thread(this.Connect);
        connectThread.IsBackground = true;
        connectThread.Start();

        callbackThread = new Thread(this.callback);
        callbackThread.IsBackground = true;
        callbackThread.Start();
    }

    private static Test instance = null;
    public static Test GetInstance()
    {
        if(instance == null)
            instance = new Test();
        return instance;
    }

    private void Connect()
    {
        while (true)
        {
            try
            {
                sendStoppedEvent.WaitOne();
                receiveStoppedEvent.WaitOne();

                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.ReceiveTimeout = RECEIVETIMOUT;

                var ip = Dns.GetHostAddresses(Host)[0];
                var endPoint = new IPEndPoint(ip, Port);
                socket.Connect(endPoint);

                sendStoppedEvent.Reset();
                receiveStoppedEvent.Reset();

                startSend();
                startReceive();
            }
            catch (SocketException e)
            {
                var waitSecond = (int)Math.Pow(3, connectingAttempt);
                if (waitSecond >= MAXCONNECTATTEMPTINTERVAL)
                {
                    waitSecond = MAXCONNECTATTEMPTINTERVAL;
                    if(ErrorHandler !=null)
                        ErrorHandler(e);
                }

                Thread.Sleep(waitSecond * 1000);
                connectingAttempt++;
            }
        }
    }

    public void Send(byte[] request)
    {
        lock (sendSync)
        {
            waitingRequests.Enqueue(request);
            Monitor.Pulse(sendSync);
        }
    }

    private void Send()
    {
        while (true)
        {
            sendReadyEvent.WaitOne();
            byte[] bytes;
            lock (sendSync)
            {
                if (waitingRequests.Count == 0)
                    Monitor.Wait(sendSync)
                bytes = waitingRequests.Dequeue();
            }

            int i = 0;
            while (i < bytes.Length)
            {
                try
                {
                    i += socket.Send(bytes, i, bytes.Length - i, SocketFlags.None);
                }
                catch (SocketException e)
                {
                    stopSend();
                    break;
                }
                catch (ObjectDisposedException e)
                {
                    stopSend();
                    break;
                }
            }
        }
    }

    private void stopSend()
    {
        socket.Close();
        sendReadyEvent.Reset();
        sendStoppedEvent.Set();
    }

    private void startSend()
    {
        sendReadyEvent.Set();
    }

    private int totalReceived = 0;
    private int  recevied = 0;

    private void Receive()
    {
        int RECVBUFFERSIZE = 1024 * 1024 * 2;
        byte[] recvBuffer = new byte[RECVBUFFERSIZE];
        while (true)
        {
            receiveReadyEvent.WaitOne();
            try
            {
                recevied = socket.Receive(recvBuffer, totalReceived, 1024, SocketFlags.None);
                if (recevied == 0)
                    stopReceive();
                totalReceived += recevied;
                //parse data
                ......
            }
            catch (SocketException e)
            {
                stopReceive();
            }
            catch (ObjectDisposedException e)
            {
                stopReceive();
            }
        }
    }

    private void stopReceive()
    {
        totalReceived = 0;
        recevied = 0;

        socket.Close();
        receiveReadyEvent.Reset();
        receiveStoppedEvent.Set();
    }

    private void startReceive()
    {
        receiveReadyEvent.Set();
    }
}

ここに Wireshark のダンプがあります

No. 時刻 ソース 宛先 プロトコル 長さ 情報

4850 1971.053146 クライアント サーバー TCP 90 netview-aix-3 > autodesk-nlm [PSH、ACK] Seq=7969 Ack=188674 Win=232 Len=36

No. 時刻 ソース 宛先 プロトコル 長さ 情報

4851 1971.077669 サーバー クライアント TCP 286 [TCP ウィンドウ フル] autodesk-nlm > netview-aix-3 [PSH、ACK] Seq=188674 Ack=7969 Win=28544 Len=232

No. 時刻 ソース 宛先 プロトコル 長さ 情報

4852 1971.077672 サーバー クライアント TCP 60 autodesk-nlm > netview-aix-3 [ACK] Seq=188906 Ack=8005 Win=28544 Len=0

No. 時刻 ソース 宛先 プロトコル 長さ 情報

4853 1971.077766 クライアント サーバー TCP 189 [TCP ZeroWindow] netview-aix-3 > autodesk-nlm [PSH、ACK] Seq=8005 Ack=188906 Win=0 Len=135

4

0 に答える 0