public async Task SmallBufferSend() { BlockingCollection <byte[]> inStm = new BlockingCollection <byte[]>(); BlockingCollection <byte[]> outStm = new BlockingCollection <byte[]>(); var mockTransport = new MockB2BSctpTransport(outStm, inStm); SctpAssociation assoc = new SctpAssociation(mockTransport, null, 5000, 5000, 1400, 0); SctpDataSender sender = new SctpDataSender("dummy", assoc.SendChunk, 1400, 0, 1000); sender.StartSending(); sender.SendData(0, 0, new byte[] { 0x00, 0x01, 0x02 }); await Task.Delay(100); Assert.Single(outStm); byte[] sendBuffer = outStm.Single(); SctpPacket pkt = SctpPacket.Parse(sendBuffer, 0, sendBuffer.Length); Assert.NotNull(pkt); Assert.NotNull(pkt.Chunks.Single() as SctpDataChunk); var datachunk = pkt.Chunks.Single() as SctpDataChunk; Assert.Equal("000102", datachunk.UserData.HexStr()); }
public async Task MaxBufferSendWithRandomDrops() { SctpDataReceiver receiver = new SctpDataReceiver(1000, 1400, 0); SctpDataSender sender = new SctpDataSender("dummy", null, 1400, 0, 1000); sender._burstPeriodMilliseconds = 1; sender._rtoInitialMilliseconds = 1; sender._rtoMinimumMilliseconds = 1; sender.StartSending(); SctpDataFrame frame = SctpDataFrame.Empty; ManualResetEventSlim frameReady = new ManualResetEventSlim(false); // This local function replicates a data chunk being sent from a data // sender to the receiver of a remote peer and the return of the SACK. Action <SctpDataChunk> doSend = async(chunk) => { if (chunk.SendCount == 1 && Crypto.GetRandomInt(0, 99) % 5 == 0) { logger.LogDebug($"Data chunk {chunk.TSN} dropped."); } else { logger.LogDebug($"Data chunk {chunk.TSN} provided to receiver."); var frames = receiver.OnDataChunk(chunk); sender.GotSack(receiver.GetSackChunk()); if (frames.Count > 0) { logger.LogDebug($"Receiver got frame of length {frames.First().UserData?.Length}."); frame = frames.First(); frameReady.Set(); } } await Task.Delay(1); }; sender._sendDataChunk = doSend; byte[] buffer = new byte[RTCSctpTransport.SCTP_DEFAULT_MAX_MESSAGE_SIZE]; //byte[] buffer = new byte[2000]; Crypto.GetRandomBytes(buffer); string hash = Crypto.GetSHA256Hash(buffer); logger.LogDebug($"Max buffer hash {hash}."); await Task.Delay(50); sender.SendData(0, 0, buffer); frameReady.WaitHandle.WaitOne(10000, true); Assert.False(frame.IsEmpty()); Assert.Equal(buffer.Length, frame.UserData.Length); Assert.Equal(hash, Crypto.GetSHA256Hash(frame.UserData)); }
public async Task InitialDataChunkDropped() { uint arwnd = 131072; ushort mtu = 1400; uint initialTSN = Crypto.GetRandomUInt(true); SctpDataReceiver receiver = new SctpDataReceiver(arwnd, mtu, initialTSN); SctpDataSender sender = new SctpDataSender("dummy", null, mtu, initialTSN, arwnd); sender.StartSending(); // This local function replicates a data chunk being sent from a data // sender to the receiver of a remote peer and the return of the SACK. Action <SctpDataChunk> doSend = (chunk) => { if (chunk.TSN == initialTSN && chunk.SendCount == 1) { logger.LogDebug($"Data chunk {chunk.TSN} dropped."); } else { receiver.OnDataChunk(chunk); sender.GotSack(receiver.GetSackChunk()); } }; sender._sendDataChunk = doSend; sender.SendData(0, 0, new byte[] { 0x55 }); Assert.Equal(initialTSN + 1, sender.TSN); await Task.Delay(250); Assert.Null(receiver.CumulativeAckTSN); await Task.Delay(500); sender.SendData(0, 0, new byte[] { 0x55 }); Assert.Equal(initialTSN + 2, sender.TSN); await Task.Delay(1250); Assert.Equal(initialTSN + 1, receiver.CumulativeAckTSN); await Task.Delay(500); sender.SendData(0, 0, new byte[] { 0x55 }); Assert.Equal(initialTSN + 3, sender.TSN); await Task.Delay(250); Assert.Equal(initialTSN + 2, receiver.CumulativeAckTSN); }
public async Task SACKChunkRetransmit() { uint arwnd = 131072; ushort mtu = 1400; uint initialTSN = Crypto.GetRandomUInt(true); SctpDataReceiver receiver = new SctpDataReceiver(arwnd, mtu, initialTSN); SctpDataSender sender = new SctpDataSender("dummy", null, mtu, initialTSN, arwnd); sender.StartSending(); // This local function replicates a data chunk being sent from a data // sender to the receiver of a remote peer and the return of the SACK. Action <SctpDataChunk> doSend = (chunk) => { receiver.OnDataChunk(chunk); sender.GotSack(receiver.GetSackChunk()); }; Action <SctpDataChunk> dontSend = (chunk) => { }; sender._sendDataChunk = doSend; sender.SendData(0, 0, new byte[] { 0x00, 0x01, 0x02 }); Assert.Equal(initialTSN + 1, sender.TSN); await Task.Delay(250); Assert.Equal(initialTSN, receiver.CumulativeAckTSN); // This send to the receiver is blocked so the receivers ACK TSN should stay the same. sender._sendDataChunk = dontSend; sender.SendData(0, 0, new byte[] { 0x00, 0x01, 0x02 }); Assert.Equal(initialTSN + 2, sender.TSN); await Task.Delay(250); Assert.Equal(initialTSN, receiver.CumulativeAckTSN); // Unblock. Receiver's ACK TSN should not advance as it has a missing chunk. sender._sendDataChunk = doSend; sender.SendData(0, 0, new byte[] { 0x00, 0x01, 0x02 }); Assert.Equal(initialTSN + 3, sender.TSN); await Task.Delay(250); Assert.Equal(initialTSN + 2, receiver.CumulativeAckTSN); }
public void MaxBufferSend() { uint arwnd = SctpAssociation.DEFAULT_ADVERTISED_RECEIVE_WINDOW; SctpDataReceiver receiver = new SctpDataReceiver(arwnd, 1400, 0); SctpDataSender sender = new SctpDataSender("dummy", null, 1400, 0, arwnd); sender.StartSending(); SctpDataFrame frame = SctpDataFrame.Empty; ManualResetEventSlim frameReady = new ManualResetEventSlim(false); // This local function replicates a data chunk being sent from a data // sender to the receiver of a remote peer and the return of the SACK. Action <SctpDataChunk> doSend = (chunk) => { logger.LogDebug($"Data chunk {chunk.TSN} provided to receiver."); var frames = receiver.OnDataChunk(chunk); sender.GotSack(receiver.GetSackChunk()); if (frames.Count > 0) { logger.LogDebug($"Receiver got frame of length {frames.First().UserData?.Length}."); frame = frames.First(); frameReady.Set(); } }; sender._sendDataChunk = doSend; byte[] buffer = new byte[RTCSctpTransport.SCTP_DEFAULT_MAX_MESSAGE_SIZE]; Crypto.GetRandomBytes(buffer); string hash = Crypto.GetSHA256Hash(buffer); logger.LogDebug($"Max buffer hash {hash}."); sender.SendData(0, 0, buffer); frameReady.WaitHandle.WaitOne(10000, true); Assert.False(frame.IsEmpty()); Assert.Equal(RTCSctpTransport.SCTP_DEFAULT_MAX_MESSAGE_SIZE, (uint)frame.UserData.Length); Assert.Equal(hash, Crypto.GetSHA256Hash(frame.UserData)); }
public void MediumBufferSend() { ushort mtu = 1400; SctpDataReceiver receiver = new SctpDataReceiver(1000, mtu, 0); SctpDataSender sender = new SctpDataSender("dummy", null, mtu, 0, 1000); sender.StartSending(); SctpDataFrame frame = SctpDataFrame.Empty; ManualResetEventSlim frameReady = new ManualResetEventSlim(false); // This local function replicates a data chunk being sent from a data // sender to the receiver of a remote peer and the return of the SACK. Action <SctpDataChunk> doSend = (chunk) => { logger.LogDebug($"Data chunk {chunk.TSN} provided to receiver."); var frames = receiver.OnDataChunk(chunk); sender.GotSack(receiver.GetSackChunk()); if (frames.Count > 0) { logger.LogDebug($"Receiver got frame of length {frames.First().UserData?.Length}."); frame = frames.First(); frameReady.Set(); } }; sender._sendDataChunk = doSend; byte[] buffer = new byte[10 * mtu]; Crypto.GetRandomBytes(buffer); string hash = Crypto.GetSHA256Hash(buffer); logger.LogDebug($"Medium buffer hash {hash}."); sender.SendData(0, 0, buffer); frameReady.WaitHandle.WaitOne(5000, true); Assert.False(frame.IsEmpty()); Assert.Equal(buffer.Length, frame.UserData.Length); Assert.Equal(hash, Crypto.GetSHA256Hash(frame.UserData)); }
public async Task IncreaseCongestionWindowSlowStart() { uint arwnd = SctpAssociation.DEFAULT_ADVERTISED_RECEIVE_WINDOW; ushort mtu = 1400; uint initialTSN = 0; SctpDataReceiver receiver = new SctpDataReceiver(arwnd, mtu, initialTSN); SctpDataSender sender = new SctpDataSender("dummy", null, mtu, initialTSN, arwnd); sender._burstPeriodMilliseconds = 1; sender._rtoInitialMilliseconds = 1; sender._rtoMinimumMilliseconds = 1; Action <SctpDataChunk> reluctantSender = (chunk) => { if (chunk.TSN % 10 == 0) { receiver.OnDataChunk(chunk); sender.GotSack(receiver.GetSackChunk()); } }; sender._sendDataChunk = reluctantSender; sender.StartSending(); Assert.Equal(SctpDataSender.CONGESTION_WINDOW_FACTOR, sender._congestionWindow); var buffer = new byte[mtu]; for (int i = 0; i <= 10; i++) { sender.SendData(0, 0, buffer); } await Task.Delay(500); Assert.Equal(SctpDataSender.CONGESTION_WINDOW_FACTOR + mtu, sender._congestionWindow); }
public async Task FillCongestionWindow() { uint arwnd = SctpAssociation.DEFAULT_ADVERTISED_RECEIVE_WINDOW; ushort mtu = 1400; Action <SctpDataChunk> blackholeSender = (chunk) => { }; SctpDataSender sender = new SctpDataSender("dummy", blackholeSender, mtu, 0, arwnd); sender.StartSending(); Assert.Equal(SctpDataSender.CONGESTION_WINDOW_FACTOR, sender._congestionWindow); var buffer = new byte[mtu]; sender.SendData(0, 0, buffer); sender.SendData(0, 0, buffer); sender.SendData(0, 0, buffer); sender.SendData(0, 0, buffer); await Task.Delay(100); Assert.True(sender._congestionWindow < sender._outstandingBytes); }