Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 检测网络流上正在进行的写入操作_C#_Multithreading_Networking_Tcp_Server - Fatal编程技术网

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

C# 检测网络流上正在进行的写入操作,c#,multithreading,networking,tcp,server,C#,Multithreading,Networking,Tcp,Server,假设服务器应用程序通过TCP连接到某个客户端设备。服务器用户决定使用NetworkStream.BeginWrite向客户端设备发送一些消息,但由于连接速度慢或其他未知因素,服务器调用BeginWrite,但BeginWrite尚未完成写入并执行回调。同时,用户决定断开主线程中的客户机设备,导致ObjectDisposedException立即在回调中抛出,因为基础连接不再可用 这里有一个伪例子来说明我的意思: /// bad pseudo of possible code happening

假设服务器应用程序通过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命令,将引发异常。显然,在这个简单的示例中,您可以阻塞直到写入完成,但是如果您希望异步进行写入,但在任何写入命令完成(或超时)之前仍然阻止执行断开连接命令,该怎么办?我相信您可以使用waithandles跟踪所有BeginWrite调用,并利用这些来确保在断开连接之前完成写操作,但这似乎需要做很多工作。有没有办法知道是否有线程试图写入给定的网络流

您需要手动跟踪请求,但这比您想象的要简单,一个简单的并发字典就足够了:

//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
    }
}
//此类将存储正在进行的请求,还将用作异步参数
公共类执行请求
{
公共Guid Id{get;set;}
公共网络流{get;set;}
}
//服务器类中的某个地方
ConcurrentDictionary pendingRequests=新建ConcurrentDictionary();
私有void发送(字符串消息)
{
网络流;
byte[]packetBuffer=Encoding.ASCII.GetBytes(msg);
stream=clientDevice.GetStream();
var request=newexecutingrequest{Stream=Stream,Id=Guid.NewGuid()};
pendingRequests.AddOrUpdate(request.Id,request,(a,b)=>request);
stream.BeginWrite(packetBuffer,0,packetBuffer.Length,新异步回调(StreamWriteCompleteCallback),request);
}
私有void StreamWriteCompleteCallback(IAsyncResult ar)
{
试一试{
ExecutingRequest req=(ExecutingRequest)ar.AsyncState;
pendingRequests.TryRemove(req.Id,out-ExecutingRequest-dummy);
请求流结束写入(ar);
}
捕获(ObjectDisposedException)
{
//写入完成之前,服务器已断开客户端设备的连接
}
}

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

您需要手动跟踪请求,但这比您想象的要简单,一个简单的并发字典就足够了:

//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
    }
}
//此类将存储正在进行的请求,还将用作异步参数
公共类执行请求
{
公共Guid Id{get;set;}
公共网络流{get;set;}
}
//服务器类中的某个地方
ConcurrentDictionary pendingRequests=新建ConcurrentDictionary();
私有void发送(字符串消息)
{
网络流;
byte[]packetBuffer=Encoding.ASCII.GetBytes(msg);
stream=clientDevice.GetStream();
var request=newexecutingrequest{Stream=Stream,Id=Guid.NewGuid()};
pendingRequests.AddOrUpdate(request.Id,request,(a,b)=>request);
stream.BeginWrite(packetBuffer,0,packetBuffer.Length,新异步回调(StreamWriteCompleteCallback),request);
}
私有void StreamWriteCompleteCallback(IAsyncResult ar)
{
试一试{
ExecutingRequest req=(ExecutingRequest)ar.AsyncState;
pendingRequests.TryRemove(req.Id,out-ExecutingRequest-dummy);
请求流结束写入(ar);
}
捕获(ObjectDisposedException)
{
//写入完成之前,服务器已断开客户端设备的连接
}
}

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

这是不够的,因为它不能解决一个线程的争用条件,一个线程检查了字典,即将调用
.Dispose()
,而另一个线程将调用
.BeginWrite()
。如果要排除任何
.BeginWrite()
.Dispose()
中断的可能性,则必须存在互斥。另外,从技术上讲,不需要跟踪单个请求,您只需跟踪未完成请求的数量即可。@Jeroenmoster只是想澄清一下-如果我想关闭特定连接,但首先等待其请求完成,我是否需要跟踪所有单个请求?否则,您怎么知道当前未完成的请求与您试图连接的连接有关close@Cobalt:我的意思是“每个流的未完成请求数”。我个人会将其抽象为一个类,该类封装了客户端和在该连接上发出的任何请求,但即使您在这里使用了一个中央集合(我不会这样做,因为您正在设置一个锁定热点),它也足以增加/减少每个流的挂起计数。跟踪整个请求可能对诊断有一定价值。@Jeroenmoster啊,我明白你的意思了。至于争用条件,是否可以简单地用互斥体包装每个连接,并用类似于
mutex.WaitOne()
mutex.ReleaseMutex()的东西将每次尝试写入或关闭连接的行为封装起来?还是我在想这个wrong@Cobalt:一个
监视器
比一个完整的互斥锁更简单。关闭:
lock(client){while(pendingRequestCount!=0)Monitor.Wait(client);client.Close();}
Send:
lock(client){++pendingRequestCount;};发送(…)。完成回调:
lock(client){--pendingRequestCount;if(pendingRequestCount==0)Monitor.pulsell(client);}
。但是,要小心被中止的请求,计数必须正确,否则您将失败