Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
949 views
in Technique[技术] by (71.8m points)

timeout - c# detecting tcp disconnect

I have two simple applications:

  • A Server application that waits on a specific tcp port for a client to connect. Then listens to what he says, send back some feedback and DISCONNECT that client.

  • A Form application that connects to the server application, then says something, then wait for the feedback and disconnect from the server, then show the feedback in the form.

Though the server application seems to behave correctly (I have tested it with Telnet and I see the feedback and I see the disconnect occurring directly after the feedback), the form application however doesn't seem to notice the disconnect from the server. ( TcpClient.Connected seems to stay true even after the server has disconnected )

My question is: why is TcpClient.Connected staying true and how can I know if/when the server has disconnected?

Here is my full code:

Form application:

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:
" + stringBuilder.ToString();
                responseLabel.Invalidate();
            });

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

    }
}

Server application:

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();
        }
    }

}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TcpClient / NetworkStream does not get notified when the connection is closed. The only option available to you is to catch exceptions when writing to the stream.

A few years back we moved to using sockets instead of tcp client. socket is more usable as compared to tcpclient.

there are a couple of methods that you can use

Poll is one of them

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

You can also do a check on outcome of Write itself. it gives you the number of bytes actually written.

The Connected property itself only reflects the state at the last operation. Its documentation states "The value of the Connected property reflects the state of the connection as of the most recent operation. If you need to determine the current state of the connection, make a non-blocking, zero-byte Send call. If the call returns successfully or throws a WAEWOULDBLOCK error code (10035), then the socket is still connected; otherwise, the socket is no longer connected."

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...