接続が長時間 (数時間または一晩) 開かれた後、マルチスレッド アプリケーションで TcpClient が送信タイムアウトで終了するという問題が発生しています。NetworkStream は、UI スレッドとバックグラウンド スレッドの 2 つのスレッドによって使用されています。受信データの読み取りに使用される StreamReader と、送信データに使用される StreamWriter があります。StreamReader は 1 つのスレッド (バックグラウンド スレッド) にしかアクセスされませんが、StreamWriter は UI スレッドとバックグラウンド スレッドの両方からアクセスされます。
接続を開いてリモートサーバーに接続すると、問題なくすぐにデータを送受信できます. 送信タイムアウトは発生せず、データは正しく送受信されます。ただし、その後立ち去って数時間データを送信せずに戻ってデータの送信を開始すると (これが意味を成すのに役立つ場合、これはチャット アプリケーションです)、ソケットは送信時にタイムアウトします。私が立ち去る間、データの受信にはまったく問題はありません。さらに、リモート サーバーはアクティブな接続をポーリングし、クライアントはそれに応答する必要があります。接続は数時間開いているため、応答を正しく送信している必要があります。ただし、このポーリング応答はバックグラウンド スレッドでのみ送信されます。入力したデータは UI スレッドから送信され、そこでタイムアウトが発生します。
同時アクセスと関係があると思いますが、何が原因なのか、UIから問題なく最初にデータを送信でき、数時間アイドル状態になった後にタイムアウトするだけな理由がわかりません。
以下は関連するコードです。上部の変数はクラスで宣言されています。Address と Port はクラスのプロパティです。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();
}
}