public void TestWriteStreamLargeWrite() { byte[] bytes = new byte[TransportPacket.MaxSegmentSize * 3 + 13]; for (int i = 0; i < bytes.Length; i++) { bytes[i] = (byte) (i%256); } TransportPacket tp = new TransportPacket(); Stream stream = tp.AsWriteStream(); Assert.AreEqual(0, stream.Position); stream.Write(bytes, 0, bytes.Length); stream.Flush(); Assert.AreEqual(bytes.Length, tp.Length); Assert.AreEqual(bytes, tp.ToArray()); }
private void FragmentMessage(Message message, ITransportDeliveryCharacteristics tdc, TransportPacket packet, MarshalledResult mr) { // <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> // Although we use an adaptive scheme for encoding the number, // we assume a maximum of 4 bytes for encoding # frags // for a total of 4 extra bytes bytes; we determine the frag // size from the message size - MaxHeaderSize const uint maxFragHeaderSize = 1 /*seqno*/ + 4; const uint maxHeaderSize = LWMCFv11.HeaderSize + maxFragHeaderSize; uint maxPacketSize = Math.Max(maxHeaderSize, tdc.MaximumPacketSize - maxHeaderSize); // round up the number of possible fragments uint numFragments = (uint)(packet.Length + maxPacketSize - 1) / maxPacketSize; Debug.Assert(numFragments > 1); uint seqNo = AllocateOutgoingSeqNo(tdc); for (uint fragNo = 0; fragNo < numFragments; fragNo++) { TransportPacket newPacket = new TransportPacket(); Stream s = newPacket.AsWriteStream(); uint fragSize = (uint)Math.Min(maxPacketSize, packet.Length - (fragNo * maxPacketSize)); if (fragNo == 0) { s.WriteByte((byte)seqNo); ByteUtils.EncodeLength(numFragments, s); } else { s.WriteByte((byte)(seqNo | 128)); ByteUtils.EncodeLength(fragNo, s); } newPacket.Prepend(LWMCFv11.EncodeHeader((MessageType)((byte)message.MessageType | 128), message.ChannelId, (uint)(fragSize + s.Length))); newPacket.Append(packet, (int)(fragNo * maxPacketSize), (int)fragSize); mr.AddPacket(newPacket); } packet.Dispose(); }
public void TestWriteStream() { /// This tests ReplaceBytes too. byte[] bytes = new byte[271]; // so it doesn't align with a segment size for (int i = 0; i < bytes.Length; i++) { bytes[i] = (byte)(i % 256); } TransportPacket tp = new TransportPacket(); Stream stream = tp.AsWriteStream(); long initialPosition = stream.Position; Assert.AreEqual(0, initialPosition); for (int i = 0; i < 255; i++) { stream.Write(bytes, i, bytes.Length - i); stream.Write(bytes, 0, i); } stream.Flush(); Assert.AreEqual(bytes.Length * 255, tp.Length); byte[] copy = tp.ToArray(); Assert.AreEqual(bytes.Length * 255, copy.Length); stream.Position = initialPosition; for (int i = 0; i < 255; i++) { for (int j = 0; j < bytes.Length; j++) { Assert.AreEqual(bytes[(i + j) % bytes.Length], copy[i * bytes.Length + j]); Assert.AreEqual(bytes[(i + j) % bytes.Length], stream.ReadByte()); } } Assert.AreEqual(stream.Length, stream.Position); stream.Position = initialPosition; stream.Position = stream.Length; CheckDisposed(tp); CheckForUndisposedSegments(); }
public void TestStreams() { byte[] source = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; TransportPacket packet = new TransportPacket(source); Stream rs = packet.AsReadStream(); Assert.AreEqual(source[0], rs.ReadByte()); Assert.AreEqual(source[1], rs.ReadByte()); Assert.AreEqual(source.Length - 2, rs.Length - rs.Position); Assert.AreEqual(source.Length - 2, packet.Length); // causes stream changes to be committed Assert.AreEqual(0, rs.Position); Assert.AreEqual(packet.Length, rs.Length); Assert.AreEqual(source[2], rs.ReadByte()); Assert.AreEqual(source[3], rs.ReadByte()); Assert.IsTrue(rs == packet.AsReadStream()); Stream ws = packet.AsWriteStream(); try { rs.ReadByte(); Assert.Fail("read stream should have been automatically closed"); } catch (ObjectDisposedException) { /* expected */ } rs = packet.AsReadStream(); try { ws.ReadByte(); Assert.Fail("read stream should have been automatically closed"); } catch (ObjectDisposedException) { /* expected */ } CheckDisposed(packet); CheckForUndisposedSegments(); }
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(); }
/// <summary> /// Marshal the given message to the provided stream in a form suitable to /// be sent out on the provided transport. /// </summary> /// <param name="senderIdentity">the identity of the sender</param> /// <param name="msg">the message being sent, that is to be marshalled</param> /// <param name="tdc">the characteristics of the transport that will send the marshalled form</param> /// <returns>the marshalled representation</returns> public virtual IMarshalledResult Marshal(int senderIdentity, Message msg, ITransportDeliveryCharacteristics tdc) { // This marshaller doesn't use <see cref="senderIdentity"/>. MarshalledResult mr = new MarshalledResult(); TransportPacket tp = new TransportPacket(); // We use TransportPacket.Prepend to add the marshalling header in-place // after the marshalling. Stream output = tp.AsWriteStream(); Debug.Assert(output.CanSeek); MarshalContents(msg, output, tdc); output.Flush(); // System messages don't have a channelId -- we encode their system message // type as the channelId instead tp.Prepend(LWMCFv11.EncodeHeader(msg.MessageType, msg.MessageType == MessageType.System ? (byte)((SystemMessage)msg).Descriptor : msg.ChannelId, (uint)output.Position)); mr.AddPacket(tp); return mr; }
/// <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); }