c# - C# 从 SslStream 连续读取(长连接,持续长达数天)并且有效地没有无限循环
问题描述
我对 C# 完全陌生,需要加密客户端和服务器之间发送和接收的数据,在谷歌上搜索了两天后,学会了最好的方法是使用 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, 0, 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,0,bytes)];
decoder.GetChars(buffer, 0, 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 连续读取,但他们使用无限循环来做到这一点,在服务器端,可能有数千个客户端连接到它,所以可能的性能不佳让我担心,就像这个: 在 C# Web MVC 5 项目中连续读取 SslStream
所以我想知道是否有更好的方法可以从持久的 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, 0, 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, 0, bytesRead)];
decoder.GetChars(buffer, 0, 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, buf, 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
。但它是异步的,因此您的线程可以继续执行其他操作。
推荐阅读
- powershell - 批处理文件中的 Powershell:错误和命令未执行
- c++ - DeleteFile api 在进程终止之前不会删除文件
- android - 根据条件仅在recycleview中显示某些项目
- java - 带有无头 chrome 的 Selenium 不会等待 javascript
- authentication - Cognito 登录和跟踪引荐来源
- mongodb - 在 mongodb 中遇到条件问题
- arrays - JSX:.map 不是函数
- ruby-on-rails - 方法在 Rails 控制台中工作 - 在视图中引发错误
- reactjs - 在反应中导入类
- python - 使用 Python 进行本地开发的无服务器 Lambda 层