private Task <bool> SendMessage(Message message, bool isResponse, bool requestingResponse, int responseTimeout, out Task <Message> responseTask) { string callCategory = null; #if TRACE int c = GetNextCallId(); callCategory = String.Format("{1}:{2} {3}:Send({0})", message, this.typeName, this.connectionId, c); #endif Trace.WriteLineIf(NTrace.TraceVerbose, "Entering", callCategory); responseTask = null; TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool> (message); if (message == null) { throw new ArgumentNullException("message"); } if (!IsConnected) { Trace.WriteLineIf(NTrace.TraceVerbose, "Exiting (not connected)", callCategory); tcs.SetResult(false); if (requestingResponse) { var responseTcs = new TaskCompletionSource <Message>(); responseTcs.SetCanceled(); responseTask = responseTcs.Task; } return(tcs.Task); } SocketAsyncEventArgs eargs = null; #if NET_4 if (!writerAsyncArgs.TryPop(out eargs)) { while (eargs == null) { int count = bufferCount; if (count == sendBufferLimit) { Trace.WriteLineIf(NTrace.TraceVerbose, "Waiting for writer args", callCategory); SpinWait wait = new SpinWait(); while (!writerAsyncArgs.TryPop(out eargs)) { wait.SpinOnce(); } } else if (count == Interlocked.CompareExchange(ref bufferCount, count + 1, count)) { Trace.WriteLineIf(NTrace.TraceVerbose, "Creating new writer args", callCategory); eargs = new SocketAsyncEventArgs(); eargs.SetBuffer(new byte[1024], 0, 1024); eargs.Completed += ReliableSendCompleted; } } } #else while (eargs == null) { lock (writerAsyncArgs) { if (writerAsyncArgs.Count != 0) { eargs = writerAsyncArgs.Pop(); } else if (bufferCount != sendBufferLimit) { bufferCount++; eargs = new SocketAsyncEventArgs(); eargs.SetBuffer(new byte[1024], 0, 1024); eargs.Completed += ReliableSendCompleted; } } } #endif #if !SILVERLIGHT eargs.AcceptSocket = null; #endif Trace.WriteLineIf(NTrace.TraceVerbose, "Have writer args", callCategory); bool sent; lock (this.sendSync) { if (message.Header == null) { message.Header = new MessageHeader(); } message.Header.MessageId = MessageSerializer.GetNextMessageId(ref this.nextMessageId); if (requestingResponse) { responseTask = Responses.SendFor(message, tcs.Task, responseTimeout); } MessageSerializer slzr = this.serializer; if (slzr == null) { int sp = Interlocked.Decrement(ref this.pendingAsync); Trace.WriteLineIf(NTrace.TraceVerbose, String.Format("Decrement pending: {0}", sp), callCategory); #if !NET_4 lock (writerAsyncArgs) #endif writerAsyncArgs.Push(eargs); Trace.WriteLineIf(NTrace.TraceVerbose, "Exiting (serializer is null, probably disconnecting)", callCategory); tcs.SetResult(false); return(tcs.Task); } int length; byte[] buffer = slzr.GetBytes(message, out length, eargs.Buffer); eargs.SetBuffer(buffer, 0, length); eargs.UserToken = new KeyValuePair <NetworkConnection, TaskCompletionSource <bool> > (this, tcs); int p = Interlocked.Increment(ref this.pendingAsync); Trace.WriteLineIf(NTrace.TraceVerbose, String.Format("Increment pending: {0}", p), callCategory); if (!IsConnected) { Interlocked.Decrement(ref this.pendingAsync); Trace.WriteLineIf(NTrace.TraceVerbose, String.Format("Decrement pending: {0}", p), callCategory); #if !NET_4 lock (writerAsyncArgs) #endif writerAsyncArgs.Push(eargs); tcs.SetResult(false); return(tcs.Task); } Trace.WriteLineIf(NTrace.TraceVerbose, "Sending", callCategory); sent = !this.reliableSocket.SendAsync(eargs); } if (sent) { Trace.WriteLineIf(NTrace.TraceVerbose, "Send completed synchronously", callCategory); ReliableSendCompleted(this.reliableSocket, eargs); } Trace.WriteLineIf(NTrace.TraceVerbose, "Exiting", callCategory); return(tcs.Task); }
protected Task <bool> SendCore(Message message, bool dontSetId, bool responseRequested, int timeout, out Task <Message> responseTask) { if (message == null) { throw new ArgumentNullException("message"); } responseTask = null; Socket sock = this.socket; MessageSerializer mserialzier = this.serializer; TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool> (message); if (sock == null || mserialzier == null || (!IsConnected && !IsConnecting)) { tcs.TrySetResult(false); if (responseRequested) { var responseTcs = new TaskCompletionSource <Message>(); responseTcs.SetCanceled(); responseTask = responseTcs.Task; } return(tcs.Task); } if (message.Header == null) { message.Header = new MessageHeader(); } if (!dontSetId) { SetMessageId(message); if (responseRequested) { responseTask = Responses.SendFor(message, tcs.Task, timeout); } } IPEndPoint endPoint = IPEndPoint; if (endPoint == null) { tcs.SetResult(false); return(tcs.Task); } SocketAsyncEventArgs e; BufferPool.TryGetBuffer(out e); int length; byte[] buffer = mserialzier.GetBytes(message, out length, e.Buffer); if (!(message is PartialMessage) && length > 490) { byte count = (byte)Math.Ceiling((length / 490f)); int i = 0; int remaining = length; do { int payloadLen = Math.Min(490, remaining); var partial = new PartialMessage { OriginalMessageId = (ushort)message.Header.MessageId, Count = count, Header = new MessageHeader() }; partial.SetPayload(buffer, i, payloadLen); if (i == 0) // We have to fill the gap the original id uses for reliability { partial.Header.MessageId = message.Header.MessageId; } else { SetMessageId(partial); } lock (this.pendingAck) this.pendingAck.Add(partial.Header.MessageId, new Tuple <DateTime, Message> (DateTime.UtcNow, partial)); mserialzier.GetBytes(partial, out length, e.Buffer); e.SetBuffer(0, length); e.RemoteEndPoint = endPoint; remaining -= payloadLen; i += payloadLen; if (remaining == 0) { e.Completed += OnSendCompleted; e.UserToken = tcs; } else { e.Completed += OnPartialSendCompleted; } try { this.lastReliableSendActivity = Stopwatch.GetTimestamp(); if (!sock.SendToAsync(e)) { if (remaining == 0) { OnSendCompleted(this, e); } else { OnPartialSendCompleted(this, e); } } } catch (ObjectDisposedException) { BufferPool.PushBuffer(e); if (remaining == 0) { CleanupSend(e); } else { CleanupPartialSend(e); } tcs.TrySetResult(false); } if (remaining > 0) { BufferPool.TryGetBuffer(out e); } } while (remaining > 0); } else { e.SetBuffer(0, length); e.RemoteEndPoint = endPoint; e.Completed += OnSendCompleted; e.UserToken = tcs; if (message.PreferReliable || message.MustBeReliable) { this.lastReliableSendActivity = Stopwatch.GetTimestamp(); lock (this.pendingAck) this.pendingAck.Add(message.Header.MessageId, new Tuple <DateTime, Message> (DateTime.UtcNow, message)); } try { if (!sock.SendToAsync(e)) { OnSendCompleted(this, e); } } catch (ObjectDisposedException) { CleanupSend(e); tcs.TrySetResult(false); } } return(tcs.Task); }