public void Unmarshal(TransportPacket input, ITransportDeliveryCharacteristics tdc, EventHandler<MessageEventArgs> messageAvailable) { // <item>Message fits within the transport packet length, so sent unmodified // <pre>[byte:message-type] [byte:channelId] [uint32:packet-size] // [bytes:content]</pre> // </item> // <item> if the message is the first fragment, then the high-bit is // set on the message-type; the number of fragments is encoded using // the adaptive <see cref="ByteUtils.EncodeLength(int)"/> format. // <pre>[byte:message-type'] [byte:channelId] [uint32:packet-size] // [byte:seqno] [bytes:encoded-#-fragments] [bytes:frag]</pre> // </item> // <item> for all subsequent fragments; seqno' = seqno | 128; // the number of fragments is encoded using the adaptive // <see cref="ByteUtils.EncodeLength(int)"/> format. // <pre>[byte:message-type'] [byte:channelId] [uint32:packet-size] // [byte:seqno'] [bytes:encoded-fragment-#] [bytes:frag]</pre> // Fastpath: if the message-type doesn't have the high-bit set, // then this is a non-fragmented message if ((input.ByteAt(0) & 128) == 0) { // If the submarshaller uses LWMCFv1.1 then we would have just // sent the packet as-is; if not, then we'll have prefixed a // LWMCFv1.1 header which must be first removed if (!subMarshallerIsLwmcf11) { input.RemoveBytes(0, (int)LWMCFv11.HeaderSize); } subMarshaller.Unmarshal(input, tdc, messageAvailable); return; } MessageType type; byte channelId; uint contentLength; Stream s = input.AsReadStream(); LWMCFv11.DecodeHeader(out type, out channelId, out contentLength, s); byte seqNo = (byte)s.ReadByte(); TransportPacket subPacket; if ((seqNo & 128) == 0) { // This starts a new message uint numFrags = (uint)ByteUtils.DecodeLength(s); subPacket = UnmarshalFirstFragment(seqNo, numFrags, input.SplitOut((int)(s.Length - s.Position)), tdc); } else { // This is a message in progress seqNo = (byte)(seqNo & ~128); uint fragNo = (uint)ByteUtils.DecodeLength(s); subPacket = UnmarshalFragInProgress(seqNo, fragNo, input.SplitOut((int)(s.Length - s.Position)), tdc); } if (subPacket != null) { subMarshaller.Unmarshal(subPacket, tdc, messageAvailable); subPacket.Dispose(); } }
public void TestRemoveBytes() { byte[] bytes = new byte[256]; for (int i = 0; i < bytes.Length; i++) { bytes[i] = (byte)(i % 256); } byte[] reversed = new byte[bytes.Length]; Buffer.BlockCopy(bytes, 0, reversed, 0, bytes.Length); Array.Reverse(reversed); TransportPacket tp = new TransportPacket(); Stream stream = tp.AsWriteStream(); // we'll make 256 copies of [0,1,...,254,255,255,254,...,1,0] for (int i = 0; i < 256; i++) { stream.Write(bytes, 0, bytes.Length); stream.Write(reversed, 0, reversed.Length); } stream.Flush(); Assert.AreEqual((bytes.Length + reversed.Length) * 256, tp.Length); byte[] copy = tp.ToArray(); Assert.AreEqual((bytes.Length + reversed.Length) * 256, tp.Length); // Now remove successively larger chunks from between the bytes/reversed // boundary points int nextIndex = 0; // the start into the next bytes/reverse pair for (int i = 0; i < bytes.Length / 2; i++) { Assert.AreEqual(0, tp.ByteAt(nextIndex)); Assert.AreEqual(1, tp.ByteAt(nextIndex + 1)); Assert.AreEqual(bytes.Length - 2, tp.ByteAt(nextIndex + bytes.Length - 2)); Assert.AreEqual(bytes.Length - 1, tp.ByteAt(nextIndex + bytes.Length - 1)); Assert.AreEqual(bytes.Length - 1, tp.ByteAt(nextIndex + bytes.Length)); Assert.AreEqual(bytes.Length - 2, tp.ByteAt(nextIndex + bytes.Length +1)); Assert.AreEqual(1, tp.ByteAt(nextIndex + bytes.Length + reversed.Length - 2)); Assert.AreEqual(0, tp.ByteAt(nextIndex + bytes.Length + reversed.Length - 1)); // remove 2i bytes from the end of the bytes copy extending tp.RemoveBytes(nextIndex + bytes.Length - i, 2 * i); Assert.AreEqual((bytes.Length + reversed.Length) * 256 - 2 * i * (i+1) / 2, tp.Length); Assert.AreEqual(0, tp.ByteAt(nextIndex)); Assert.AreEqual(bytes.Length - i - 1, tp.ByteAt(nextIndex + bytes.Length - i - 1)); Assert.AreEqual(bytes.Length - i - 1, tp.ByteAt(nextIndex + bytes.Length - i)); Assert.AreEqual(0, tp.ByteAt(nextIndex + bytes.Length + reversed.Length - 2*i - 1)); nextIndex += bytes.Length + reversed.Length - 2 * i; } CheckDisposed(tp); CheckForUndisposedSegments(); }
protected override void NotifyPacketReceived(TransportPacket packet) { if (packet.Length < PacketHeaderSize) { throw new TransportError(this, "should not receive datagrams whose size is less than PacketHeaderSize bytes", packet); } uint packetSeqNo = 0; packet.BytesAt(0, 4, (b,offset) => packetSeqNo = DataConverter.Converter.ToUInt32(b, offset)); packet.RemoveBytes(0, 4); // We handle wrap around by checking if the difference between the // packet-seqno and the expected next packet-seqno > uint.MaxValue / 2 // After all, it's unlikely that 2 billion packets will mysteriously disappear! if (packetSeqNo < nextIncomingPacketSeqNo && nextIncomingPacketSeqNo - packetSeqNo < uint.MaxValue / 2) { return; } nextIncomingPacketSeqNo = packetSeqNo + 1; // pass it on base.NotifyPacketReceived(packet); }
public void TestDisposal() { TransportPacket packet = new TransportPacket(10); packet.Dispose(); try { packet.Grow(20); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.RemoveBytes(0,2); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.ByteAt(0); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.Consolidate(); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.Prepend(new byte[0]); Assert.Fail("should have thrown ObjectDisposedException"); } catch (ObjectDisposedException) { /* expected */ } try { packet.Append(new byte[0]); Assert.Fail("should have thrown ObjectDisposedException"); } catch (ObjectDisposedException) { /* expected */ } try { Console.WriteLine(packet.Length); Assert.Fail("should have thrown ObjectDisposedException"); } catch (ObjectDisposedException) { /* expected */ } }
/// <summary> /// Handle incoming packets from previously-unknown remote endpoints. /// We check if the packet indicates a handshake and, if so, negotiate. /// Otherwise we send a GT error message and disregard the packet. /// </summary> /// <param name="ep">the remote endpoint</param> /// <param name="packet">the first packet</param> protected void PreviouslyUnseenUdpEndpoint(EndPoint ep, TransportPacket packet) { TransportPacket response; Stream ms; // This is the GT (UDP) protocol 1.0: // bytes 0 - 3: the protocol version (the result from ProtocolDescriptor) // bytes 4 - n: the number of bytes in the capability dictionary (see ByteUtils.EncodeLength) // bytes n+1 - end: the capability dictionary // The # bytes in the dictionary isn't actually necessary in UDP, but oh well foreach(TransportFactory<UdpHandle> factory in factories) { if(packet.Length >= factory.ProtocolDescriptor.Length && ByteUtils.Compare(packet.ToArray(0, factory.ProtocolDescriptor.Length), factory.ProtocolDescriptor)) { packet.RemoveBytes(0, factory.ProtocolDescriptor.Length); ms = packet.AsReadStream(); Dictionary<string, string> dict = null; try { uint count = ByteUtils.DecodeLength(ms); // we don't use it dict = ByteUtils.DecodeDictionary(ms); if(ms.Position != ms.Length) { byte[] rest = packet.ToArray(); log.Info(String.Format( "{0} bytes still left at end of UDP handshake packet: {1} ({2})", rest.Length, ByteUtils.DumpBytes(rest, 0, rest.Length), ByteUtils.AsPrintable(rest, 0, rest.Length))); } ITransport result = factory.CreateTransport(UdpHandle.Bind(udpMultiplexer, ep)); if (ShouldAcceptTransport(result, dict)) { // Send confirmation // NB: following uses the format specified by LWMCF v1.1 response = new TransportPacket(LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.Acknowledged, (uint)factory.ProtocolDescriptor.Length)); response.Append(factory.ProtocolDescriptor); udpMultiplexer.Send(response, ep); NotifyNewTransport(result, dict); } else { // NB: following follows the format specified by LWMCF v1.1 response = new TransportPacket(LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.IncompatibleVersion, 0)); udpMultiplexer.Send(response, ep); result.Dispose(); } return; } catch(Exception e) { log.Warn(String.Format("Error decoding handshake from remote {0}", ep), e); } } } // If we can figure out some way to say: the packet is an invalid form: // response = new TransportPacket(); // ms = response.AsWriteStream(); // log.Info("Undecipherable packet (ignored)"); // // NB: following follows the format specified by LWMCF v1.1 // LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.UnknownConnexion, // (uint)ProtocolDescriptor.Length, ms); // ms.Write(ProtocolDescriptor, 0, ProtocolDescriptor.Length); // ms.Flush(); // udpMultiplexer.Send(response, ep); response = new TransportPacket(); ms = response.AsWriteStream(); log.Info("Unknown protocol version: " + ByteUtils.DumpBytes(packet.ToArray(), 0, 4) + " [" + ByteUtils.AsPrintable(packet.ToArray(), 0, 4) + "]"); // NB: following follows the format specified by LWMCF v1.1 LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.IncompatibleVersion, 0, ms); ms.Flush(); udpMultiplexer.Send(response, ep); }