web-dev-qa-db-ja.com

c#はtcp切断を検出します

2つの簡単なアプリケーションがあります。

  • クライアントが接続するのを特定のtcpポートで待機するサーバーアプリケーション。次に、彼の言うことを聞き、フィードバックを送り返し、そのクライアントを切断します。

  • サーバーアプリケーションに接続して何かを言い、フィードバックを待ってサーバーから切断し、フォームにフィードバックを表示するフォームアプリケーション。

サーバーアプリケーションは正しく動作しているように見えますが(Telnetでテストしたところ、フィードバックが表示され、フィードバックの直後に切断が発生していることがわかります)、フォームアプリケーションはからの切断に気付かないようです)サーバー。 (TcpClient.Connectedは、サーバーが切断された後もtrueのままであるようです)

私の質問は、TcpClient.Connectedがtrueのままである理由と、サーバーが切断されたかどうか/いつ切断されたかを知る方法です。

これが私の完全なコードです:

フォーム申請:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Sender
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void sendButton_Click(object sender, EventArgs e)
        {
            TcpClient tcpClient = new TcpClient();
            tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 81);
            responseLabel.Text = "waiting for response...";
            responseLabel.Invalidate();

            // write request
            NetworkStream networkStream = tcpClient.GetStream();
            byte[] buffer = (new ASCIIEncoding()).GetBytes("Hello World! ");
            networkStream.Write(buffer, 0, buffer.Length);
            networkStream.Flush();

            // read response
            Thread readThread = new Thread(new ParameterizedThreadStart(ReadResponse));
            readThread.Start(tcpClient);
        }

        void ReadResponse(object arg)
        {
            TcpClient tcpClient = (TcpClient)arg;
            StringBuilder stringBuilder = new StringBuilder();
            NetworkStream networkStream = tcpClient.GetStream();
            bool timeout = false;
            DateTime lastActivity = DateTime.Now;
            while (tcpClient.Connected && !timeout)
            {
                if (networkStream.DataAvailable)
                {
                    lastActivity = DateTime.Now;
                    while (networkStream.DataAvailable)
                    {
                        byte[] incomingBuffer = new byte[1024];
                        networkStream.Read(incomingBuffer, 0, 1024);
                        char[] receivedChars = new char[1024];
                        (new ASCIIEncoding()).GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0);
                        stringBuilder.Append(receivedChars);
                    }
                }
                else
                {
                    if (DateTime.Now > lastActivity.AddSeconds(60))
                        timeout = true;
                }
                System.Threading.Thread.Sleep(50);
            }
            Invoke((MethodInvoker)delegate
            {
                responseLabel.Text = "Response from Listener:\n" + stringBuilder.ToString();
                responseLabel.Invalidate();
            });

            if (timeout)
            {
                Console.Write("A timeout occured\n");
                networkStream.Close();
                tcpClient.Close();
            }
        }

    }
}

サーバーアプリケーション:

using System.Net;
using System.Net.Sockets;
using System.Text;
using System;
using System.Threading;

namespace Listener
{
    class Program
    {
        static void Main(string[] args)
        {
            var tcpListener = new TcpListener(IPAddress.Any, 81);
            tcpListener.Start();
            Thread clientThread = new Thread(new ParameterizedThreadStart(Listen));
            clientThread.Start(tcpListener);
        }

        static void Listen(object arg)
        {
            TcpListener tcpListener = (TcpListener)arg;
            while (true)
            {
                TcpClient tcpClient = tcpListener.AcceptTcpClient();
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(tcpClient);
            }
        }

        static void HandleClient(object arg)
        {
            TcpClient tcpClient = (TcpClient)arg;
            StringBuilder stringBuilder = new StringBuilder();
            ASCIIEncoding encoder = new ASCIIEncoding();
            DateTime lastActivity = DateTime.Now;

            // read request
            NetworkStream networkStream = tcpClient.GetStream();
            int timeout = 5; // gives client some time to send data after connecting
            while (DateTime.Now < lastActivity.AddSeconds(timeout) && stringBuilder.Length==0)
            {
                if (!networkStream.DataAvailable)
                {
                    System.Threading.Thread.Sleep(50);
                }
                else
                {
                    while (networkStream.DataAvailable)
                    {
                        lastActivity = DateTime.Now;
                        byte[] incomingBuffer = new byte[1024];
                        networkStream.Read(incomingBuffer, 0, 1024);
                        char[] receivedChars = new char[1024];
                        encoder.GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0);
                        stringBuilder.Append(receivedChars);
                    }
                }
            }
            string request = stringBuilder.ToString();

            // write response
            string response = "The listener just received: " + request;
            byte[] outgoingBuffer = encoder.GetBytes(response);
            networkStream.Write(outgoingBuffer, 0, outgoingBuffer.Length);
            networkStream.Flush();

            networkStream.Close();
            tcpClient.Close();
        }
    }

}
11
nl-x

TcpClient/NetworkStreamは、接続が閉じられても通知を受け取りません。使用できる唯一のオプションは、ストリームへの書き込み時に例外をキャッチすることです。

数年前、tcpクライアントの代わりにソケットを使用するようになりました。ソケットは、tcpclientと比較してより使いやすくなっています。

使用できる方法がいくつかあります

世論調査はそれらの1つです

http://msdn.Microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx

Write自体の結果を確認することもできます。実際に書き込まれたバイト数がわかります。

Connectedプロパティ自体は、最後の操作での状態のみを反映します。そのドキュメントには、「Connectedプロパティの値は、最新の操作時の接続の状態を反映しています。接続の現在の状態を確認する必要がある場合は、ノンブロッキングのゼロバイト送信呼び出しを行います。呼び出しの場合正常に戻るか、WAEWOULDBLOCKエラーコード(10035)をスローした場合、ソケットは接続されたままです。それ以外の場合、ソケットは接続されていません。」

http://msdn.Microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx

14
Hermit Dave
networkStream.DataAvailable

このプロパティは、サーバー側でソケットが閉じているかどうかを通知しません。

あなたの状況では、t分/秒ごとに接続をポーリングする「キープアライブ」機能を実装するか、ソケットから読み取りまたは書き込みを試みるたびに多くの試行/キャッチを追加することができます。 TCPを使用するときは、すべてのcatchステートメントに含まれるReconnect()メソッドを含めるだけです。

0
chrisevett