2012-06-02 58 views
1

我在連接打開很長一段時間(幾個小時或一夜之間)之後,在多線程應用程序中發送超時時發生TcpClient關閉時出現問題)。 NetworkStream被兩個線程(UI線程和後臺線程)使用。有一個StreamReader用於讀取傳入數據,以及一個StreamWriter用於傳出數據。 StreamReader只能訪問一個線程(後臺線程),但StreamWriter可以被UI線程和後臺線程訪問。多線程TcpClient在長時間打開連接後發送超時

會發生什麼情況是,如果我打開連接並連接到遠程服務器,我可以立即發送和接收數據,沒有任何問題。我沒有收到任何發送超時,並且數據正確發送和接收。但是,如果我隨後離開並且幾個小時不發送任何數據,然後返回並開始發送數據(如果這有助於使其有意義,則這是一個聊天應用程序),套接字將在發送時超時。在我離開的時候,所有接收數據都沒有問題。另外,遠程服務器輪詢正在進行的連接,並且我的客戶端必須對此作出響應,並且由於連接已打開幾個小時,它必須正確發送響應。不過,該輪詢響應僅在後臺線程上發送。我輸入的數據從UI線程發送,這就是超時發生的地方。

我猜這是與併發訪問有關,但我無法弄清楚是什麼導致它,以及爲什麼我最初可以從UI發送數據而沒有問題,並且只有在閒置幾個小時後纔會超時。

下面是相關的代碼。頂部的變量在類中聲明。地址和端口是類中的屬性。 WriteLine是應用程序中使用StreamWriter發送數據的唯一方法。我把鎖定在StreamWriter.WriteLine的調用上,希望能糾正任何同步問題。 WriteLine是從ParseMessage內部的後臺線程以及UI中的其他地方調用的。

如果我增加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(); 
    } 
} 

回答

1

NetworkStream的類的阻擋方法(ReadWrite)沒有被設計爲從多個線程同時使用。從MSDN:

使用簡單單線程同步阻塞I/O的WriteRead方法。如果要使用單獨的線程處理您的I/O,請考慮使用BeginWriteEndWrite方法或BeginReadEndRead方法進行通信。

我的假設是,當你從你的UI線程調用WriteLine(和,在內部,NetworkStream.Write),它會阻止,直到併發ReadLine(內部,NetworkStream.Read)操作在後臺線程完成。如果後者在SendTimeout內不這樣做,則Write將超時。

要解決您的問題,您應該將您的實現轉換爲使用非阻塞方法。然而,作爲一個快速黑客先行先試,這是否是真正的問題,試試你的ReadLine之前引入DataAvailable投票:

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