问题描述
我对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
。但这是异步的,因此您的线程可以继续执行其他操作。