多线程的TcpClient长的开放连接后发送超时(Multi-threaded TcpClient

2019-07-29 17:07发布

我有一个TcpClient的一个发送超时关闭在多线程应用程序的连接已经开放的很长一段时间(几个小时或过夜)之后的一个问题。 所述的NetworkStream正在由两个线程,一个UI线程和后台线程。 有用于读取输入的数据一个StreamReader,和用于传出数据一个StreamWriter。 该StreamReader的是永远只能访问一个线程(背景之一),但StreamWriter的是由UI线程和后台线程都访问。

什么情况是,如果我打开一个连接,并连接到远程服务器,我可以立即发送,没有任何问题,接收数据。 我没有得到任何发送超时和数据被正确地发送和接收。 但是,如果我再走开,没有几个小时发送任何数据,然后返回并开始发送数据(这是一个聊天应用程序是否有帮助,使其意义),插座会超时的发送。 在此期间,我走路的时候是没有问题的,在所有接收数据的时间。 此外,对于活动连接远程服务器投票和我的客户必须到了回复,因为连接是打开了几个小时就必须正确地发送响应。 此轮询响应只发送在后台线程,虽然。 数据输入我从UI线程发送的,而这也正是发生超时。

我猜这是什么做的并发访问,但我想不出什么导致它,为什么我可以开始从UI发送的数据没有问题,只有在空闲几个小时后超时。

下面是相关的代码。 顶部的变量在类中声明。 地址和端口的类属性。 的WriteLine是任何地方,与发送的StreamWriter数据的应用程序的唯一方法。 我把锁随时待命,以StreamWriter.WriteLine希望将纠正任何同步问题。 的WriteLine从内部ParseMessage后台线程调用,并从其他地方的用户界面。

如果我增加TcpClient.SendTimeout更大的东西,这并不解决任何事情。 它只是需要更长的时间套接字超时。 我不能让后台线程读取和写入,因为后台线程阻塞的ReadLine,所以没有什么会永远被写入。

private TcpClient _connection;
private StreamWriter _output;
private Thread _parsingThread;
private object _outputLock = new object();


public void Connect(string address, int port)
{           
    Address = address;
    Port = port;

    _parsingThread = new Thread(new ThreadStart(Run));
    _parsingThread.IsBackground = true;
    _parsingThread.Start();
}

private void Run()
{
    try
    {
        using (_connection = new TcpClient())
        {
            _connection.Connect(Address, Port);
            _connection.ReceiveTimeout = 180000;
            _connection.SendTimeout = 60000;

            StreamReader input = new StreamReader(_connection.GetStream());
            _output = new StreamWriter(_connection.GetStream());

            string line;
            do
            {
                line = input.ReadLine();

                if (!string.IsNullOrEmpty(line))
                {
                    ParseMessage(line);
                }
            }
            while (line != null);
        }
    }
    catch (Exception ex)
    {
        //not actually catching exception, just compressing example
    }
    finally
    {
       //stuff
    }
}

protected void WriteLine(string line)
{
    lock (_outputLock)
    {
        _output.WriteLine(line);
        _output.Flush();
    }
}

Answer 1:

阻塞的方法( ReadWrite了的) NetworkStream类不被设计为从多个线程同时使用。 从MSDN:

使用WriteRead简单的单个线程同步阻塞I / O的方法。 如果你想处理您的I / O使用单独的线程,可以考虑使用BeginWriteEndWrite方法,或者BeginReadEndRead方法进行通信。

我的假设是,当你调用WriteLine (和,在内部, NetworkStream.Write从UI线程),它会阻止,直到并发ReadLine (内部, NetworkStream.Read )操作在后台线程完成。 如果后者不与内这样做SendTimeout ,然后Write会超时。

若要解决您的问题,您应该转换方案中使用非阻塞的方法。 然而,作为一个快速黑客先行先试,这是否是真正的问题,尝试引入DataAvailable你之前调查ReadLine

NetworkStream stream = _connection.GetStream();
StreamReader input = new StreamReader(stream);
_output = new StreamWriter(stream);

string line;
do
{
    // Poll for data availability.
    while (!stream.DataAvailable)
        Thread.Sleep(300);

    line = input.ReadLine();

    if (!string.IsNullOrEmpty(line))
    {
        ParseMessage(line);
    }
}
while (line != null);

line = input.ReadLine();


文章来源: Multi-threaded TcpClient send timeout after long open connection