SslStream.WriteAsync “The BeginWrite method cannot be called when another write operation is pending”

喜欢而已 提交于 2019-12-12 09:43:33

问题


How to prevent this issue when writing data to the client Asynchronously

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

MYCODE

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

回答1:


It is important to understand exactly what the await keyword does:

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)).

When you call Send with some non null buffer you will get to

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

Using await you block the execution only in the Send method, but the code in the caller continues to execute even if the WriteAsync has not completed yet. Now if the Send method is called again before the WriteAsync has completed you will get the exception that you have posted since SslStream does not allow multiple write operations and the code that you have posted does not prevent this from happening.

If you want to make sure that the previous BeginWrite has completed you have to change the Send method to return a Task

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

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

and wait for it's completion by calling it using await like:

    await Send(sslStream, message);

This should work if you do not attempt to write data from multiple threads.

Also here is some code that prevents write operations from multiple threads to overlap (if properly integrated with your code). It uses an intermediary queue and the Asynchronous Programming Model(APM) and works quite fast. You need to call EnqueueDataForWrite to send the data.

    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);
    }



回答2:


If the operation was started with BeginWrite, call SslStream.EndWrite to end the old write operation before you start the next. If the operation was started using WriteAsync, ensure the Task has completed first, such as using the await keyword or Wait().



来源:https://stackoverflow.com/questions/12611861/sslstream-writeasync-the-beginwrite-method-cannot-be-called-when-another-write

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!