public UdpUnicaster(Identity identity, UdpClient udpClient, AcknowledgementCoordinator acknowledgementCoordinator, IObjectPool <byte[]> sendReceiveBufferPool, UdpClientRemoteInfo remoteInfo, IAuditCounter resendsCounter, IAuditAggregator <int> resendsAggregator, IAuditAggregator <double> outboundMessageRateLimitAggregator, IAuditAggregator <double> sendQueueDepthAggregator) { this.outboundPacketMemoryStreamPool = new ByteArrayPoolBackedMemoryStreamPool(sendReceiveBufferPool); this.identity = identity; this.udpClient = udpClient; this.acknowledgementCoordinator = acknowledgementCoordinator; this.remoteInfo = remoteInfo; this.resendsCounter = resendsCounter; this.resendsAggregator = resendsAggregator; this.outboundMessageRateLimitAggregator = outboundMessageRateLimitAggregator; this.sendQueueDepthAggregator = sendQueueDepthAggregator; }
private void HandleAnnouncement(UdpClientRemoteInfo remoteInfo, AnnouncementDto x) { announcementsReceivedCounter.Increment(); var peerIdentity = x.Identity; var peerId = peerIdentity.Id; bool isNewlyDiscoveredRoute = false; RoutingContext addedRoutingContext = null; UdpUnicaster addedUnicaster = null; var routingContext = routingContextsByPeerId.GetOrAdd( peerId, add => { isNewlyDiscoveredRoute = true; var unicastReceivePort = int.Parse((string)x.Identity.Properties[UdpConstants.kUnicastPortIdentityPropertyKey]); var unicastIpAddress = remoteInfo.IPEndpoint.Address; var unicastEndpoint = new IPEndPoint(unicastIpAddress, unicastReceivePort); var unicastRemoteInfo = new UdpClientRemoteInfo { Socket = remoteInfo.Socket, IPEndpoint = unicastEndpoint }; addedUnicaster = udpUnicasterFactory.Create(unicastRemoteInfo); return(addedRoutingContext = new RoutingContext(addedUnicaster)); }); if (addedRoutingContext == routingContext) { addedUnicaster.Initialize(); } if (isNewlyDiscoveredRoute) { routingTable.Register(peerId, routingContext); } peerTable.GetOrAdd(peerId).HandleInboundPeerIdentityUpdate(peerIdentity); }
public void Unicast(UdpClientRemoteInfo remoteInfo, MemoryStream[] frames, Action action) { // Frames larger than half the maximum packet size certainly cannot be packed together. var smallFrames = new List <MemoryStream>(frames.Length); var largeFrames = new List <MemoryStream>(frames.Length); const int kHalfMaximumTransportSize = UdpConstants.kMaximumTransportSize / 2; for (var i = 0; i < frames.Length; i++) { var frame = frames[i]; if (frame.Length <= kHalfMaximumTransportSize) { smallFrames.Add(frame); } else { largeFrames.Add(frame); } } // Order small frames ascending by size, large frames descending by size. smallFrames.Sort(new MemoryStreamByPositionComparer()); largeFrames.Sort(new ReverseComparer <MemoryStream>(new MemoryStreamByPositionComparer())); // Place large frames into outbound buffers. var outboundBuffers = new List <MemoryStream>(frames.Length); foreach (var largeFrame in largeFrames) { var outboundBuffer = outboundMemoryStreamPool.TakeObject(); outboundBuffer.Write(largeFrame.GetBuffer(), 0, (int)largeFrame.Position); outboundBuffers.Add(outboundBuffer); } // Place small frames into outbound buffers. Note that as the // small frames are ascending in size and the buffers are descending // in size, while we iterate if a small frame cannot fit into the // next outbound buffer then none of the following small frames can either. int activeOutboundBufferIndex = 0; foreach (var smallFrame in smallFrames) { // precompute greatest outbound buffer permission for which // we will still be able to fit into the buffer. int frameSize = (int)smallFrame.Position; int greatestFittableBufferPosition = UdpConstants.kMaximumTransportSize - frameSize; // Attempt to place the small frame into existing outbound buffers bool placed = false; while (!placed && activeOutboundBufferIndex != outboundBuffers.Count) { var outboundBuffer = outboundBuffers[activeOutboundBufferIndex]; if (outboundBuffer.Position > greatestFittableBufferPosition) { activeOutboundBufferIndex++; } else { outboundBuffer.Write(smallFrame.GetBuffer(), 0, (int)smallFrame.Position); placed = true; } } // If no existing outbound buffer had space, allocate a new one if (!placed) { AssertEquals(outboundBuffers.Count, activeOutboundBufferIndex); var outboundBuffer = outboundMemoryStreamPool.TakeObject(); outboundBuffer.Write(smallFrame.GetBuffer(), 0, (int)smallFrame.Position); outboundBuffers.Add(outboundBuffer); } } // Console.WriteLine($"Batched {frames.Length} to {outboundBuffers.Count} buffers."); int sendsRemaining = outboundBuffers.Count; foreach (var outboundBuffer in outboundBuffers) { var job = new UdpUnicastJob { OutboundBuffer = outboundBuffer, RemoteInfo = remoteInfo, SendCompletionHandler = () => { outboundBuffer.SetLength(0); outboundMemoryStreamPool.ReturnObject(outboundBuffer); if (Interlocked.Decrement(ref sendsRemaining) == 0) { action(); } } }; unicastJobQueue.Enqueue(job); } // int sendsRemaining = outboundBuffers.Count; // Parallel.ForEach( // outboundBuffers, // outboundBuffer => { // outboundBytesAggregator.Put(outboundBuffer.Length); // // var e = new SocketAsyncEventArgs(); // e.RemoteEndPoint = remoteInfo.IPEndpoint; // e.SetBuffer(outboundBuffer.GetBuffer(), 0, (int)outboundBuffer.Position); // e.Completed += (sender, args) => { // // Duplicate code with below. // args.SetBuffer(null, 0, 0); // args.Dispose(); // // outboundBuffer.SetLength(0); // outboundMemoryStreamPool.ReturnObject(outboundBuffer); // // if (Interlocked.Decrement(ref sendsRemaining) == 0) { // action(); // } // }; // // const int kSendStateAsync = 1; // const int kSendStateDone = 2; // const int kSendStateError = 3; // int sendState; // try { // bool completingAsynchronously = remoteInfo.Socket.SendToAsync(e); // sendState = completingAsynchronously ? kSendStateAsync : kSendStateDone; // } catch (ObjectDisposedException) when (isShutdown) { // sendState = kSendStateError; // } // // if (sendState == kSendStateDone || sendState == kSendStateError) { // // Completed synchronously so e.Completed won't be called. // e.SetBuffer(null, 0, 0); // e.Dispose(); // // outboundBuffer.SetLength(0); // outboundMemoryStreamPool.ReturnObject(outboundBuffer); // // if (Interlocked.Decrement(ref sendsRemaining) == 0) { // action(); // } // } // }); // int sendsRemaining = outboundBuffers.Count; // foreach (var outboundBuffer in outboundBuffers) { // outboundBytesAggregator.Put(outboundBuffer.Length); // // var e = new SocketAsyncEventArgs(); // e.RemoteEndPoint = remoteInfo.IPEndpoint; // e.SetBuffer(outboundBuffer.GetBuffer(), 0, (int)outboundBuffer.Position); // e.Completed += (sender, args) => { // // Duplicate code with below. // args.SetBuffer(null, 0, 0); // args.Dispose(); // // outboundBuffer.SetLength(0); // outboundMemoryStreamPool.ReturnObject(outboundBuffer); // // if (Interlocked.Decrement(ref sendsRemaining) == 0) { // action(); // } // }; // // const int kSendStateAsync = 1; // const int kSendStateDone = 2; // const int kSendStateError = 3; // int sendState; // try { // bool completingAsynchronously = remoteInfo.Socket.SendToAsync(e); // sendState = completingAsynchronously ? kSendStateAsync : kSendStateDone; // } catch (ObjectDisposedException) when (isShutdown) { // sendState = kSendStateError; // } // // if (sendState == kSendStateDone || sendState == kSendStateError) { // // Completed synchronously so e.Completed won't be called. // e.SetBuffer(null, 0, 0); // e.Dispose(); // // outboundBuffer.SetLength(0); // outboundMemoryStreamPool.ReturnObject(outboundBuffer); // // if (sendState == kSendStateError) { // // Don't send remaining messages. // // To the application, this appears like packet loss. // action(); // return; // } else if (Interlocked.Decrement(ref sendsRemaining) == 0) { // action(); // } // } // } }
public UdpUnicaster Create(UdpClientRemoteInfo remoteInfo) { return(new UdpUnicaster(identity, udpClient, acknowledgementCoordinator, sendReceiveBufferPool, remoteInfo, resendsCounter, resendsAggregator, outboundMessageRateLimitAggregator, sendQueueDepthAggregator)); }