我的程序为远程主机创建了许多传出连接(使用TcpClient
)。它为每个连接创建3个线程:
第一个线程然后触发2个子线程:一个读取,另一个写入。写入完成后,写入线程通知第一个线程并返回,然后第一个线程关闭TcpClient
实例(通过using()
语句)。我看不出如何解锁读取线程。
我希望在已关闭的Read()
上调用NetworkStream
将返回EOS / EOF(-1
),表示已收到的所有数据已结束,因此检查是否足够。
这是我的代码的简化版本:
void DoTask() { // called from UI thread
Thread taskThread = new Thread( DoTaskInternal );
taskThread.Start();
}
EventWaitHandle _taskDone = new EventWaitHandle( false );
void DoTaskInternal() {
using(TcpClient client = ConnectToRemote()) {
Thread readThread = new Thread( ReadLoop );
Thread writeThread = new Thread( WriteLoop );
readThread.Start( client.GetStream() );
writeThread.Start( client.GetStream() );
_taskDone.WaitOne();
// the writeThread will have returned, but wait for the readThread to finish:
readThread.Join();
}
}
void ReadLoop(NetworkStream s) {
StreamReader rdr = new StreamReader( s );
Int32 nc; Char c;
while( (nc = rdr.Read()) != -1 ) {
Char c = (Char)nc;
// do stuff with each char received
}
}
void WriteLoop(NetworkStream s) {
// (write to the stream here)
// tell the other thread we're done:
_taskDone.Set();
}
但是,根据我的经验,对rdr.Read()
的调用会导致3种不同的异常中的一种被抛出,具体取决于确切的时间。
System.IO.IOException("无法从传输连接读取数据:阻止操作因调用WSACancelBlockingCall而中断。")
大约90%的时间都会发生此异常。
System.InvalidOperationException("流不支持阅读。")
这个只发生过一次。它具有与IOException
完全相同的堆栈跟踪。当读取线程在调用NetworkStream.Read
之前在streamSocket.Receive
方法内部时,必须发生这种情况。
System.ObjectDisposedException("无法访问已处置的对象。)
这种情况发生在大约9%的时间。这是NetworkStream.Read
内的第一次检查。
但是,确实有一个线程从网络流读取而另一个线程关闭连接必须是一个常见的场景,因此捕获(和吞咽)这些异常似乎并不理想。
有没有更好的方法来处理这个问题,这使我能够从网络流中逐个字符地读取,但在连接关闭时不必处理异常?