c# - SslStream.WriteAsync "The BeginWrite method cannot be called when another write operation is pending"

标签 c# sslstream

异步向客户端写入数据时如何防止这个问题

The BeginWrite method cannot be called when another write operation is pending

我的代码

public async void Send(byte[] buffer)
{
    if (buffer == null)
        return;
    await SslStream.WriteAsync(buffer, 0, buffer.Length);
}

最佳答案

准确理解 await 关键字的作用很重要:

An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off (MSDN - await (C# Reference)).

当您使用一些非空缓冲区调用 Send 时,您将到达

    await SslStream.WriteAsync(buffer, 0, buffer.Length);

使用 await 仅在 Send 方法中阻止执行,但即使 WriteAsync 尚未完成,调用方中的代码仍会继续执行。现在,如果在 WriteAsync 完成之前再次调用 Send 方法,您将得到您发布的异常,因为 SslStream 不允许多个写入操作,并且您发布的代码不会阻止这种情况的发生。

如果你想确保之前的 BeginWrite 已经完成,你必须更改 Send 方法以返回一个 Task

    async Task Send(SslStream sslStream, byte[] buffer)
    {
        if (buffer == null)
            return;

        await sslStream.WriteAsync(buffer, 0, buffer.Length);
    }

并通过使用 await 来调用它来等待它完成:

    await Send(sslStream, message);

如果您不尝试从多个线程写入数据,这应该可以工作。

还有一些代码可以防止来自多个线程的写操作重叠(如果与您的代码正确集成)。它使用中间队列和异步编程模型 (APM),并且运行速度非常快。 您需要调用 EnqueueDataForWrite 来发送数据。

    ConcurrentQueue<byte[]> writePendingData = new ConcurrentQueue<byte[]>();
    bool sendingData = false;

    void EnqueueDataForWrite(SslStream sslStream, byte[] buffer)
    {
        if (buffer == null)
            return;

        writePendingData.Enqueue(buffer);

        lock (writePendingData)
        {
            if (sendingData)
            {
                return;
            }
            else
            {
                sendingData = true;
            }
        }

        Write(sslStream);            
    }

    void Write(SslStream sslStream)
    {
        byte[] buffer = null;
        try
        {
            if (writePendingData.Count > 0 && writePendingData.TryDequeue(out buffer))
            {
                sslStream.BeginWrite(buffer, 0, buffer.Length, WriteCallback, sslStream);
            }
            else
            {
                lock (writePendingData)
                {
                    sendingData = false;
                }
            }
        }
        catch (Exception ex)
        {
            // handle exception then
            lock (writePendingData)
            {
                sendingData = false;
            }
        }            
    }

    void WriteCallback(IAsyncResult ar)
    {
        SslStream sslStream = (SslStream)ar.AsyncState;
        try
        {
            sslStream.EndWrite(ar);
        }
        catch (Exception ex)
        {
            // handle exception                
        }

        Write(sslStream);
    }

关于c# - SslStream.WriteAsync "The BeginWrite method cannot be called when another write operation is pending",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12611861/

相关文章:

新机器的证书问题-无法识别提供给软件包的凭据

c# - Linq/Entity框架中如何获取待创建的ID

c# - 在 try catch block 中对 IDisposable 使用 block 是否有任何问题?

c# - Asp.net:如何修复向多个收件人发送邮件时引发的异常?erroline:在邮件 header 中发现无效字符: ','

.net - 身份验证失败,因为远程方已关闭传输流

ssl - 关于 STARTTLS SMTP 命令

c# - Visual Studio 2019 - msvsmon.exe 意外退出。 .net core 1.1 项目的调试将中止

c# - 获取 Type 中使用的程序集的路径

c# - 构造 SslStream 的 GET 请求

c# - Socket.ReceiveAsync 和 SslStream