是否有可能检测客户端是否已关闭Stream?

情况简介:

我有一个服务,通过套接字发送回复信息。 连接不安全。 我想要设置另一项可以为这些连接提供TLS的服务 - 这项新服务将提供一个端口并根据提供的客户端证书分配连接。 我不想使用stunnel出于几个原因,其中之一就是每个接收端口需要一个转发端口。

我目前试图实现的解决方案:

本质上,我试图将一个SslStream(传入)与一个NetworkStream耦合(传出 - 可以是一个Socket,但是我将它放入一个NetworkStream中以匹配传入),并且具有读/写操作。 此链接将提供客户端(通过SSL / TLS)和服务(通过不安全的连接)之间的流程。

以下是我想出的链接这些流的类:

public class StreamConnector
{
    public StreamConnector(Stream s1, Stream s2)
    {
        StreamConnectorState state1 = new StreamConnectorState(s1, s2);
        StreamConnectorState state2 = new StreamConnectorState(s2, s1);
        s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1);
        s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2);
    }

    private void ReadCallback(IAsyncResult result)
    {
        // Get state object.
        StreamConnectorState state = (StreamConnectorState)result.AsyncState;

        // Finish reading data.
        int length = state.InStream.EndRead(result);

        // Write data.
        state.OutStream.Write(state.Buffer, 0, length);

        // Wait for new data.
        state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state);
    }
}

public class StreamConnectorState
{
    private const int BYTE_ARRAY_SIZE = 4096;

    public byte[] Buffer { get; set; }
    public Stream InStream { get; set; }
    public Stream OutStream { get; set; }

    public StreamConnectorState(Stream inStream, Stream outStream)
    {
        Buffer = new byte[BYTE_ARRAY_SIZE];
        InStream = inStream;
        OutStream = outStream;
    }
}

问题:

当客户端完成发送信息并处理SslStream时,服务器没有任何指示是否发生了这种情况。 这StreamConnector类愉快地继续运行到永恒,没有抛出任何错误,我找不到任何指标,它应该停止。 (当然,事实上,我每次在ReadCallback中得到0的长度,但是我需要能够提供长时间运行的连接,所以这不是一个很好的判断方法。)

另一个潜在的问题是即使没有数据可用,ReadCallback也会被调用。 如果我直接使用Socket而不是流,那么不确定这是否会有所不同,但保持一遍又一遍地运行该代码似乎效率低下。

我的问题:

1)有没有方法可以判断Stream是否从客户端被关闭?

2)有没有更好的方法去做我想做的事情?

2a)是否有更有效的方式来运行异步读/写循环?

编辑:谢谢,罗伯特。 原因是我没有关闭Streams(因为不知道如何判断Streams何时需要关闭),因此循环不断被调用。 我将包含完整的代码解决方案以防其他人遇到此问题:

/// <summary>
/// Connects the read/write operations of two provided streams
/// so long as both of the streams remain open.
/// Disposes of both streams when either of them disconnect.
/// </summary>
public class StreamConnector
{
    public StreamConnector(Stream s1, Stream s2)
    {
        StreamConnectorState state1 = new StreamConnectorState(s1, s2);
        StreamConnectorState state2 = new StreamConnectorState(s2, s1);
        s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1);
        s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2);
    }

    private void ReadCallback(IAsyncResult result)
    {
        // Get state object.
        StreamConnectorState state = (StreamConnectorState)result.AsyncState;

        // Check to make sure Streams are still connected before processing.
        if (state.InStream.IsConnected() && state.OutStream.IsConnected())
        {
            // Finish reading data.
            int length = state.InStream.EndRead(result);

            // Write data.
            state.OutStream.Write(state.Buffer, 0, length);

            // Wait for new data.
            state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state);
        }
        else
        {
            // Dispose of both streams if either of them is no longer connected.
            state.InStream.Dispose();
            state.OutStream.Dispose();
        }
    }
}

public class StreamConnectorState
{
    private const int BYTE_ARRAY_SIZE = 4096;

    public byte[] Buffer { get; set; }
    public Stream InStream { get; set; }
    public Stream OutStream { get; set; }

    public StreamConnectorState(Stream inStream, Stream outStream)
    {
        Buffer = new byte[BYTE_ARRAY_SIZE];
        InStream = inStream;
        OutStream = outStream;
    }
}

public static class StreamExtensions
{
    private static readonly byte[] POLLING_BYTE_ARRAY = new byte[0];

    public static bool IsConnected(this Stream stream)
    {
        try
        {
            // Twice because the first time will return without issue but
            // cause the Stream to become closed (if the Stream is actually
            // closed.)
            stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length);
            stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length);
            return true;
        }
        catch (ObjectDisposedException)
        {
            // Since we're disposing of both Streams at the same time, one
            // of the streams will be checked after it is disposed.
            return false;
        }
        catch (IOException)
        {
            // This will be thrown on the second stream.Write when the Stream
            // is closed on the client side.
            return false;
        }
    }
}

您必须尝试读取或写入套接字 - 或基于它的任何东西 - 来检测断开连接。

试图写入会抛出一个异常/返回一个错误(取决于你的语言的范例),或者可能只写0字节。 试图读取将会抛出一个异常/返回一个错误(再次取决于你的语言的范例)或返回null

值得注意的是,如果您使用的是基于select的服务器模型,那么断开连接的套接字会在断开连接时显示 - 即返回select - 可读,然后尝试从中读取并获取错误或null


我忍不住想你的客户应该告诉服务器何时完成某种消息。 最好准备一个电线被切断或电源故障或拔出一个插头,但通常你想用某种类型的信息结束标记来终止连接。 保存真正问题的例外,而不是正常的会话。

链接地址: http://www.djcxy.com/p/57929.html

上一篇: Is it possible to detect if a Stream has been closed by the client?

下一篇: problem with lock in thread &managment lock