想象一下,服务器应用程序通过TCP连接到某个客户端设备。服务器的用户决定使用NetworkStream.BeginWrite向客户端设备发送一些消息,但由于连接速度慢或其他一些未知因素,服务器调用BeginWrite,但BeginWrite尚未完成其写入并执行其回调。同时,用户决定断开主线程中的客户端设备,导致由于底层连接不再可用而立即在回调中抛出ObjectDisposedException。
这是我的意思的一个伪示例:
/// bad pseudo of possible code happening in main server thread
while(!quit){
if( command has been inputted) {
switch (command) {
case send:
string m = getAStringFromGUI();
Send(m);
break;
case disconnect:
ClientDevice.Close();
break;
}
}
}
private void Send(string msg){
NetworkStream stream;
byte[ ] packetBuffer = Encoding.ASCII.GetBytes(msg);
stream = clientDevice.GetStream();
stream.BeginWrite(packetBuffer, 0, packetBuffer.Length, new AsyncCallback(StreamWriteCompleteCallback), stream);
}
private void StreamWriteCompleteCallback(IAsyncResult ar) {
try {
NetworkStream stream = (NetworkStream)ar.AsyncState;
stream.EndWrite(ar);
}
catch (ObjectDisposedException) {
// client device was disconnected by the server before write completed
}
}
如果在send命令之后输入了disconnect命令,则会抛出异常。显然,在这个简单的示例中,您可以阻塞直到写入完成,但是如果您希望将写入异步发生但仍然阻止断开命令执行,直到任何写入命令完成(或超时),该怎么办?我相信你可以跟踪所有使用等待句柄的BeginWrite调用,并利用它们来确保在断开连接之前完成写入,但这看起来很多工作。有没有办法知道是否有任何线程试图写入给定的网络流?
答案 0 :(得分:0)
您需要手动跟踪请求,但这比您想象的更容易,一个简单的并发字典就足够了:
//This class will store ongoing requests and also will be used as the async parameter
public class ExecutingRequest
{
public Guid Id { get; set; }
public NetworkStream Stream { get; set; }
}
//Somewhere in your server class
ConcurrentDictionary<Guid, ExecutingRequest> pendingRequests = new ConcurrentDictionary<Guid, ExecutingRequest>();
private void Send(string msg)
{
NetworkStream stream;
byte[ ] packetBuffer = Encoding.ASCII.GetBytes(msg);
stream = clientDevice.GetStream();
var request = new ExecutingRequest{ Stream = stream, Id = Guid.NewGuid() };
pendingRequests.AddOrUpdate(request.Id, request, (a,b) => request);
stream.BeginWrite(packetBuffer, 0, packetBuffer.Length, new AsyncCallback(StreamWriteCompleteCallback), request);
}
private void StreamWriteCompleteCallback(IAsyncResult ar)
{
try {
ExecutingRequest req = (ExecutingRequest)ar.AsyncState;
pendingRequests.TryRemove(req.Id, out ExecutingRequest dummy);
req.stream.EndWrite(ar);
}
catch (ObjectDisposedException)
{
// client device was disconnected by the server before write completed
}
}
然后,您可以通过检查字典的长度来检查是否有任何请求正在运行。