多线程的TcpClient长开连接后发送超时 [英] Multi-threaded TcpClient send timeout after long open connection

查看:330
本文介绍了多线程的TcpClient长开连接后发送超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



我猜这件事情做的并发访问,但我想不出什么导致它,为什么我最初从UI发送数据没有问题而只有在空闲几个小时后超时。



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



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

 私人TcpClient的_connection; 
私人的StreamWriter _output;
私人螺纹_parsingThread;
私有对象_outputLock =新的对象();


公共无效连接(字符串的地址,端口INT)
{
地址=地址;
端口=端口;

_parsingThread =新主题(新的ThreadStart(运行));
_parsingThread.IsBackground = TRUE;
_parsingThread.Start();
}

私人无效的run()
{

{
使用(_connection =新的TcpClient())
{
_connection.Connect(地址,端口);
_connection.ReceiveTimeout = 180000;
_connection.SendTimeout = 60000;

StreamReader的输入=新的StreamReader(_connection.GetStream());
_output =新的StreamWriter(_connection.GetStream());

串线;

{
线= input.ReadLine();

如果(!string.IsNullOrEmpty(线))
{
ParseMessage(线);
}
}
,而(行!= NULL);
}
}
赶上(异常前)
{
//没有真正捕获的异常,只是压缩例如
}
终于
{
//东西
}
}

保护无效的WriteLine(串线)
{
锁(_outputLock)
{
_output.WriteLine(线);
_output.Flush();
}
}


解决方案

的的的读和) EN-US /库/ system.net.sockets.networkstream.aspx相对=nofollow> 的NetworkStream 类没有被设计从多个同时使用线程。从MSDN:




使用的对于简单的单个线程的同步阻塞I / O的方法。如果你想使用单独的线程来处理你的I / O,可以考虑使用 BeginWrite EndWrite 方法,或的BeginRead EndRead 的通信方法。




我的假设是,当你调用的WriteLine (和在内部, NetworkStream.Write )的你的UI线程,它会阻止,直到并发的ReadLine (其中, NetworkStream.Read )的操作在后台线程完成。如果后者没有在的SendTimeout ,那么会超时。

$ B内这样做
$ b

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

 的NetworkStream流= _connection.GetStream(); 
StreamReader的输入=新的StreamReader(流);
_output =新的StreamWriter(流);

串线;

{
//轮询数据可用性。
,而(stream.DataAvailable!)
的Thread.Sleep(300);

线= input.ReadLine();

如果(!string.IsNullOrEmpty(线))
{
ParseMessage(线);
}
}
,而(行!= NULL);

线= input.ReadLine();


I'm having a problem with a TcpClient closing with a send timeout in a multi-threaded application after the connection has been open for a long period of time (several hours or overnight). The NetworkStream is being used by two threads, a UI thread and a background thread. There is a StreamReader used for reading incoming data, and a StreamWriter used for outgoing data. The StreamReader is only ever accessed one thread (the background one), but the StreamWriter is accessed by both the UI thread and the background thread.

What happens is that if I open a connection and connect to a remote server, I can immediately send and receive data without any problems. I do not get any send timeouts and data is correctly sent and received. However, if I then walk away and do not send any data for several hours and then return and start sending data (this is a chat application if that helps make it make sense), the socket will timeout on the send. During the time that I walk away there is no problem at all receiving data. Additionally, the remote server polls for an active connection and my client must respond to that, and since the connection is open for several hours it must be correctly sending a response. This polling response is only sent on the background thread, though. Data I enter is sent from the UI thread, and that's where the timeout occurs.

I'm guessing it's something to do with concurrent access, but I can't figure out what's causing it and why I can initially send data from the UI without a problem and only have it timeout after being idle for several hours.

Below is the relevant code. The variables at the top are declared in the class. Address and Port are properties in the class. WriteLine is the only method anywhere in the application that sends data with the StreamWriter. I put a lock around the call to StreamWriter.WriteLine hoping that would correct any synchronization issues. WriteLine is called from the background thread inside ParseMessage, and elsewhere from the UI.

If I increase TcpClient.SendTimeout to something larger, that doesn't fix anything. It just takes longer for the socket to timeout. I can't have the background thread both read and write because the background thread is blocking on ReadLine, so nothing would ever get written.

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

解决方案

The blocking methods (Read and Write) of the NetworkStream class are not designed to be used concurrently from multiple threads. From MSDN:

Use the Write and Read methods for simple single thread synchronous blocking I/O. If you want to process your I/O using separate threads, consider using the BeginWrite and EndWrite methods, or the BeginRead and EndRead methods for communication.

My assumption is that, when you call WriteLine (and, internally, NetworkStream.Write) from your UI thread, it would block until the concurrent ReadLine (internally, NetworkStream.Read) operation completes in the background thread. If the latter does not do so within the SendTimeout, then the Write would time out.

To work around your issue, you should convert your implementation to use non-blocking methods. However, as a quick hack to first test whether this is really the issue, try introducing a DataAvailable poll before your 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();

这篇关于多线程的TcpClient长开连接后发送超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆