C#从SslStream连续读取长连接,可持续长达几天,并且高效地进行无死循环

问题描述

我对C#完全陌生,需要对客户端和服务器之间发送和接收的数据进行加密,在Google搜索了两天后,了解到最好的方法是使用SslStream,我发现了一些答案,这些都是很好的例子,但它们都以某种方式假设我们只需要阅读一条消息然后关闭连接,这完全不是我的情况,每当用户触发其设备通过持久连接发送消息时,我都必须阅读。 Microsoft文档中的一个示例:

static string ReadMessage(SslStream sslStream)
    {
        // Read the  message sent by the client.
        // The client signals the end of the message using the
        // "<EOF>" marker.
        byte [] buffer = new byte[2048];
        StringBuilder messageData = new StringBuilder();
        int bytes = -1;
        do
        {
            // Read the client's test message.
            bytes = sslStream.Read(buffer,buffer.Length);

            // Use Decoder class to convert from bytes to UTF8
            // in case a character spans two buffers.
            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer,bytes)];
            decoder.GetChars(buffer,bytes,chars,0);
            messageData.Append (chars);
            // Check for EOF or an empty message. <------   In my case,I don't have EOF
            if (messageData.ToString().IndexOf("<EOF>") != -1)
            {
                break;
            }
        } while (bytes !=0);

        return messageData.ToString();
    }

和其他答案实际上告诉我如何从SslStream连续读取,但是它们使用无限循环来做到这一点,在服务器端,可能有成千上万的客户端连接到它,所以可能的性能不佳使我担心,例如这个 : Read SslStream continuously in C# Web MVC 5 project

所以我想知道是否存在更好的方法来持续从SslStream持久连接中进行读取。

我知道使用裸套接字,我可以使用 socketasynceventargs 来知道何时有新数据准备就绪,希望我可以使用SslStream做到这一点,可能我误会了一些东西,任何想法都会受到赞赏,谢谢前进。

解决方法

这是我的主意。我选择了递归,而不是永远循环。此方法将立即返回 ,但在命中EOF时将触发事件并继续读取:

public static void ReadFromSSLStreamAsync(
    SslStream sslStream,Action<string> result,Action<Exception> error,StringBuilder stringBuilder = null)
{
    const string EOFToken = "<EOF>";

    stringBuilder = stringBuilder ?? new StringBuilder();
    var buffer = new byte[4096];

    try
    {
        sslStream.BeginRead(buffer,buffer.Length,asyncResult =>
        {
            // Read all bytes avaliable from stream and then
            // add them to string builder
            {
                int bytesRead;
                try
                {
                    bytesRead = sslStream.EndRead(asyncResult);
                }
                catch (Exception ex)
                {
                    error?.Invoke(ex);
                    return;
                }

                // Use Decoder class to convert from bytes to
                // UTF8 in case a character spans two buffers.
                var decoder = Encoding.UTF8.GetDecoder();
                var buf = new char[decoder.GetCharCount(buffer,bytesRead)];
                decoder.GetChars(buffer,bytesRead,buf,0);
                stringBuilder.Append(buf);
            }

            // Find the EOFToken,if found copy all data before the token
            // and send it to event,then remove it from string builder
            {
                int tokenIndex;
                while((tokenIndex = stringBuilder.ToString().IndexOf(EOFToken)) != -1)
                {
                    var buf = new char[tokenIndex];
                    stringBuilder.CopyTo(0,tokenIndex);
                    result?.Invoke(new string(buf));
                    stringBuilder.Remove(0,tokenIndex + EOFToken.Length);
                }
            }

            // Continue reading...
            ReadFromSSLStreamAsync(sslStream,result,error,stringBuilder);
        },null);
    }
    catch(Exception ex)
    {
        error?.Invoke(ex);
    }
}

您可以这样称呼它:

ReadFromSSLStreamAsync(sslStream,sslData =>
{
    Console.WriteLine($"Finished: {sslData}");
},error =>
{
    Console.WriteLine($"Errored: {error}");
});

它不是TaskAsync,因此您不必await。但这是异步的,因此您的线程可以继续执行其他操作。