/// Version 1.0.0 /// Last modified 23.5.2014 /// /// Copyright (C) 2014 Veli-Mikko Puupponen /// /// Halyri-system is a prototype emergency call system. Its purpose is to /// demonstrate the use of the advanced capabilities available in the current /// generation smartphones in facilitating the emergency service dispatcher's /// capability to determine the nature of the emergency and to dispatch help. /// /// For more information, see the README file of this package. /// /// The MIT License (MIT) /// /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to /// deal in the Software without restriction, including without limitation the /// rights to use, copy, modify, merge, publish, distribute, sublicense, /// and/or sell copies of the Software, and to permit persons to whom the /// Software is furnished to do so, subject to the following conditions: /// /// The above copyright notice and this permission notice shall be included in /// all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING /// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS /// IN THE SOFTWARE. /// using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SimpleUdpMediaClient.Packets { /// Veli-Mikko Puupponen /// /// The class represents a control packet for the protocol defined /// and implement by the UdpMediaClientSocket. The packet /// consist of a packet type identifier, a sender GUID, /// a Int64 sequence number, a target GUID and a command. /// /// Control packets are primarily user to request server packet /// routing from one client to another. /// /// This packet can have optional payload data. /// public class ControlPacket : INetworkPacket { /// /// The enumeration for command type. It is used to specify the command of the control packet. /// public enum CommandType : byte { RequestEnableRouting, RequestDisableRouting, Statistics } /// /// The specified id of the packet type. It is used to identify a packet. /// public const byte PacketId = 2; /// /// The specified length of the packet header in octets. /// public const int HeaderLengthInOctets = 42; private const int packetIdHeaderIndex = 0; private const int sourceGuidHeaderIndex = 1; private const int sequenceHeaderIndex = 17; private const int targetGuidHeaderIndex = 25; private const int commandHeaderIndex = 41; private byte[] sourceGuid; /// /// The function to get the Guid identifying the sender. /// public string SourceGuid { get { return (sourceGuid == null || sourceGuid.Length != 16) ? "" : new Guid(sourceGuid).ToString(); } } private byte[] targetGuid; /// /// The function to get the Guid identifying the target. /// public string TargetGuid { get { return (targetGuid == null || targetGuid.Length != 16) ? "" : new Guid(targetGuid).ToString(); } } private Int64 sequence; /// /// The function to set and get the packet sequence number. /// public Int64 Sequence { get { return sequence; } set { sequence = value; } } private byte command; /// /// The function to get the packet command. /// public CommandType Command { get { return (CommandType)command; } } private byte[] payloadData; /// /// The function to get the packet payload data. Can be null. /// public byte[] PayloadData { get { return payloadData; } } /// /// The function initializes a new ControlPacket instance with no parameters. /// public ControlPacket() { } /// /// The function initializes a new ControlPacket instance that has the /// specified source and target Guids, command, payload data /// and sequence number. /// /// It throws an ArgumentException if source or target Guid is malformed. /// /// The string representation of the sender's Guid. /// The string representation of the target's Guid. /// The packet command. /// The optional payload data. /// Packet sequence number. public ControlPacket(string sourceGuid, string targetGuid, CommandType command, byte[] payload, Int64 sequenceNumber) : this(sourceGuid, targetGuid, command, payload) { this.sequence = sequenceNumber; } /// /// The function initializes a new ControlPacket instance that has the /// specified source and target Guids, command and sequence number. /// /// It throws an ArgumentException if source or target Guid is malformed. /// /// The string representation of the sender's Guid. /// The string representation of the target's Guid. /// The packet command. /// Packet sequence number. public ControlPacket(string sourceGuid, string targetGuid, CommandType command, Int64 sequenceNumber) : this(sourceGuid, targetGuid, command) { this.sequence = sequenceNumber; } /// /// The function initializes a new ControlPacket instance that has the /// specified source and target Guids, command and payload data. /// /// It throws an ArgumentException if source or target Guid is malformed. /// /// The string representation of the sender's Guid. /// The string representation of the target's Guid. /// The packet command. /// The optional payload data. public ControlPacket(string sourceGuid, string targetGuid, CommandType command, byte[] payload) : this(sourceGuid, targetGuid, command) { payloadData = payload; } /// /// The function initializes a new ControlPacket instance that has the /// specified source and target Guids and command. /// /// It throws an ArgumentException if source or target Guid is malformed. /// /// The byte representation of the sender's Guid. /// The byte representation of the target's Guid. /// The packet command. public ControlPacket(string sourceGuid, string targetGuid, CommandType command) { Guid source; if (!Guid.TryParse(sourceGuid, out source)) throw new ArgumentException("Guid format error on source"); this.sourceGuid = source.ToByteArray(); Guid target; if (!Guid.TryParse(targetGuid, out target)) throw new ArgumentException("Guid format error on target"); this.targetGuid = target.ToByteArray(); this.command = (byte)command; } /// /// The function initializes a new ControlPacket instance that has the /// specified source and target Guids, command and payload data. /// /// It throws an ArgumentException if any of the arguments is null. /// /// The byte representation of the sender's Guid. /// The byte representation of the target's Guid. /// The packet command. /// The optional payload data. public ControlPacket(byte[] sourceGuid, byte[] targetGuid, CommandType command, byte[] payload) { if (sourceGuid == null || targetGuid == null || payload == null) throw new ArgumentException("Guid or payload cannot be null"); this.sourceGuid = sourceGuid; this.targetGuid = targetGuid; this.command = (byte)command; payloadData = payload; } /// /// The function gets the command, the target guid and the packet control data from the given data and saves them. /// /// The given data. public void FromBytes(byte[] packetBytes) { if (packetBytes.Length < HeaderLengthInOctets) throw new ArgumentException("Data length insufficient for headers."); if(packetBytes[packetIdHeaderIndex] != PacketId) throw new ArgumentException("Incompatible packet type."); byte[] sourceGuidBytes = new byte[sequenceHeaderIndex - sourceGuidHeaderIndex]; Array.Copy(packetBytes, sourceGuidHeaderIndex, sourceGuidBytes, 0, sourceGuidBytes.Length); sourceGuid = sourceGuidBytes; sequence = BitConverter.ToInt64(packetBytes, sequenceHeaderIndex); byte[] targetGuidBytes = new byte[commandHeaderIndex - targetGuidHeaderIndex]; Array.Copy(packetBytes, targetGuidHeaderIndex, targetGuidBytes, 0, targetGuidBytes.Length); targetGuid = targetGuidBytes; command = packetBytes[commandHeaderIndex]; if (packetBytes.Length > HeaderLengthInOctets) { payloadData = new byte[packetBytes.Length - HeaderLengthInOctets]; Array.Copy(packetBytes, HeaderLengthInOctets, payloadData, 0, payloadData.Length); } } /// /// The function saves the given sequence number and returns the data header of the currently saved sequence. /// /// The data header of current the sequence. public byte[] GetBytes(long sequenceNumber) { sequence = sequenceNumber; return GetBytes(); } /// /// The function returns the data header of the currently saved sequence. /// /// The data header of current the sequence. public byte[] GetBytes() { int payloadLength = (payloadData == null) ? 0 : payloadData.Length; byte[] networkPacketData = new byte[HeaderLengthInOctets + payloadLength]; networkPacketData[packetIdHeaderIndex] = PacketId; Array.Copy(sourceGuid, 0, networkPacketData, sourceGuidHeaderIndex, sourceGuid.Length); byte[] sequenceBytes = BitConverter.GetBytes(sequence); Array.Copy(sequenceBytes, 0, networkPacketData, sequenceHeaderIndex, sequenceBytes.Length); Array.Copy(targetGuid, 0, networkPacketData, targetGuidHeaderIndex, targetGuid.Length); networkPacketData[commandHeaderIndex] = command; if (payloadLength > 0) Array.Copy(payloadData, 0, networkPacketData, HeaderLengthInOctets, payloadData.Length); return networkPacketData; } } }