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);
}
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);
}
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().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With