检测网络流上正在进行的写操作

时间:2017-06-08 22:16:28

标签: c# multithreading networking tcp server

想象一下,服务器应用程序通过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调用,并利用它们来确保在断开连接之前完成写入,但这看起来很多工作。有没有办法知道是否有任何线程试图写入给定的网络流?

1 个答案:

答案 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
    }
}

然后,您可以通过检查字典的长度来检查是否有任何请求正在运行。