Reputation: 12206
In Unity with .Net I'm using websockets, so as a client:
using System.Threading;
using System.Net.WebSockets;
async void Connect()
{
cws = new ClientWebSocket();
try
{
await cws.ConnectAsync(u, CancellationToken.None);
if (cws.State == WebSocketState.Open) Debug.Log("connected");
... begin receive result loop, etc ...
}
catch (Exception e) { Debug.Log("woe " + e.Message); }
}
To send to the server, I use:
public async void SendString(string msg)
{
ArraySegment<byte> b = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg));
await cws.SendAsync(b, WebSocketMessageType.Text, true, CancellationToken.None);
}
Hence throughout the app,
ThatClass.SendString("some command");
and
ThatClass.SendString("some other command");
However at one point I do this,
ThatClass.SendString("a command");
ThatClass.SendString("another command");
One immediately after another.
Of course, it doesn't work. The error is
InvalidOperationException: There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time. System.Net.WebSockets.ManagedWebSocket.ThrowIfOperationInProgress (System.Threading.Tasks.Task operationTask, System.String methodName) (at :0) System.Net.WebSockets.ManagedWebSocket.SendAsync (System.ArraySegment`1[T] buffer, System.Net.WebSockets.WebSocketMessageType messageType, System.Boolean endOfMessage, System.Threading.CancellationToken cancellationToken) (at :0)
Is there a simple solution here? Or do you have to build a queue?
I thought perhaps Monitor would help, so
public async void SendString(string msg)
{
ArraySegment<byte> b = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg));
//await cws.SendAsync(b, WebSocketMessageType.Text, true, CancellationToken.None);
Monitor.Enter(cws);
try
{
cws.SendAsync(b, WebSocketMessageType.Text, true, CancellationToken.None);
}
finally
{
Monitor.Exit(cws);
}
}
However, in that case I get this error:
ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'. System.Net.Sockets.NetworkStream.EndRead (System.IAsyncResult asyncResult) (at :0) System.IO.Stream+<>c.b__43_1 (System.IO.Stream stream, System.IAsyncResult asyncResult) (at <980ae0ed635b4a3296831396fcc0a4a0>:0) System.Threading.Tasks.TaskFactory
1+FromAsyncTrimPromise
1[TResult,TInstance].Complete (TInstance thisRef, System.Func`3[T1,T2,TResult] endMethod, System.IAsyncResult asyncResult, System.Boolean requiresSynchronization) (at <980ae0ed635b4a3296831396fcc0a4a0>:0)
Is there a simple solution? (Or any solution!)
As a curiosity (this could be Unity related, IDK) if you add any statement between the two calls,
ThatClass.SendString("a command");
x = 1;
ThatClass.SendString("another command");
disturbingly, it then does work! :O (As often as I tested it.)
Upvotes: 0
Views: 2320
Reputation: 1245
You should be awating the SendAsync
call and return a Task
that represents this operation. If you do not await the call, the flow of the method continues before finishing the execution of SendAsync
. Try this:
using System.Threading.Tasks;
public async Task SendStringAsync(string msg)
{
ArraySegment<byte> b = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg));
Monitor.Enter(cws);
try
{
await cws.SendAsync(b, WebSocketMessageType.Text, true, CancellationToken.None);
}
finally
{
Monitor.Exit(cws);
}
}
And call it like this:
await mySockerWrapper.SendStringAsync("Hello World!");
Upvotes: 3