using System; using System.Collections.Generic; using System.Linq; using System.Net; namespace UdPunching { public class ExchangeMessage { // [0.....] Id // [1.....] Section count // [2.....] Section Type // [3....N] Section Content // [N....M] Next Section Type public ExchangeMessageId Id { get; set; } public DateTime? TimeStamp { get; set; } public IPEndPoint PeerEndPoint { get; set; } public Guid? PeerId { get; set; } public byte[] PayloadBytes { get; set; } //TODO: more properties public ExchangeMessage() { } public ExchangeMessage(ExchangeMessageId id) { Id = id; TimeStamp = DateTime.Now; } public ExchangeMessage(byte[] data) { var ptr = -1; Id = (ExchangeMessageId)data[++ptr]; var count = data[++ptr]; for (var i = 0; i < count; i++) { var section = (ExchangeMessageSection)data[++ptr]; switch (section) { case ExchangeMessageSection.TimeStamp: ptr += data.ReadUnixTimeStamp(++ptr, out var timeStamp); TimeStamp = timeStamp; break; case ExchangeMessageSection.PeerEndPoint: ptr += data.ReadIpEndPoint(++ptr, out var ipEndPoint); PeerEndPoint = ipEndPoint; break; case ExchangeMessageSection.PeerId: PeerId = new Guid(data.Skip(++ptr).Take(16).ToArray()); ptr += 16; break; case ExchangeMessageSection.PayloadData: ptr += data.ReadLeInt16(out var len, ++ptr); var payload = new byte[len]; Buffer.BlockCopy(data, ++ptr, payload, 0, len); ptr += len; PayloadBytes = payload; break; default: //TODO: handle more sections throw new ArgumentOutOfRangeException(); } } } private Dictionary CreateSectionDictionary() { var dic = new Dictionary(); if (TimeStamp.HasValue) { dic[ExchangeMessageSection.TimeStamp] = TimeStamp.Value.ToUnixTimeStamp().ToLeInt32Bytes(); } if (null != PeerEndPoint) { var buf = new byte[17]; PeerEndPoint.Address.ToPaddingString().WriteAsciiBytesTo(buf); PeerEndPoint.Port.WriteLeInt16To(buf, 15); dic[ExchangeMessageSection.PeerEndPoint] = buf; } if (PeerId.HasValue) { dic[ExchangeMessageSection.PeerId] = PeerId.Value.ToByteArray(); } if (null != PayloadBytes) { var buf = new byte[PayloadBytes.Length + 2]; PayloadBytes.Length.WriteLeInt16To(buf); PayloadBytes.CopyTo(buf, 2); dic[ExchangeMessageSection.PayloadData] = buf; } //TODO: add more sections from properties return dic; } public byte[] ToBytes() { var dic = CreateSectionDictionary(); var lst = new List(1 + 1 + dic.Count + dic.Sum(p => p.Value.Length)) { (byte)Id, (byte)dic.Count }; foreach (var kvp in dic) { lst.Add((byte)kvp.Key); lst.AddRange(kvp.Value); } return lst.ToArray(); } public void WriteToBuffer(byte[] buffer) { var dic = CreateSectionDictionary(); var ptr = -1; buffer[++ptr] = (byte)Id; buffer[++ptr] = (byte)dic.Count; foreach (var item in dic) { buffer[++ptr] = (byte)item.Key; item.Value.CopyTo(buffer, ++ptr); } } } }