public void SendPost(IPEndPoint destination, Uri uri, bool reliable, string content) { CoapMessageType type = CoapMessageType.NonConfirmable; if (reliable) { type = CoapMessageType.Confirmable; } CoapMessage message = new CoapMessage(type, CoapMessageCode.POST); message.Destination = destination; message.Uri = uri; message.Payload = content == null ? null : System.Text.Encoding.UTF8.GetBytes(content); SendRequest(message); }
public async Task HandleMessage(CoapMessage message) { await Task.Run(() => {}); Console.WriteLine("I'll do something at some point"); }
public Task HandleRequest(CoapMessage message) { throw new System.NotImplementedException(); }
public Task HandleResponse(CoapMessage response) { throw new System.NotImplementedException(); }
// TODO: build this kind of mechanism into handling a queue of tasks: // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/start-multiple-async-tasks-and-process-them-as-they-complete // https://devblogs.microsoft.com/pfxteam/processing-tasks-as-they-complete/ public SendMessageStatus SendMessage(CoapMessage message) { message.DecomposeUri(message.Uri); message.MessageID = (ushort)(idhash[0] << 8); message.MessageID |= (ushort)idhash[1]; // prepare hasher for next message ID idhash = hasher.ComputeHash(idhash); SendMessageStatus status = SendMessageStatus.BusyQueued; // TODO: move this to something that will check in the background - not as part of send! - check for any messages that are complete List <Task> toRemove = new List <Task>(); foreach (Task t in messagesInProgress) { if (t.IsCompleted || t.IsCanceled || t.IsFaulted) { // TODO: do a bit more than this, handle errors toRemove.Add(t); } } foreach (Task t in toRemove) { messagesInProgress.Remove(t); } // TODO: if busy sending enqueue the message, make max configurable if (messagesInProgress.Count > 100) { // too busy, queue the message and move on outgoingQueue.Enqueue(message); status = SendMessageStatus.BusyQueued; } else { // TODO: generate tokens and whatever needed to fulfill the protocol // encode the message into a byte array byte[] datagram = message.ToByteArray(); // attempt to send the message Task <int> t = null; t = endpoint.SendAsync(datagram, datagram.Length, message.Destination); if (t.IsCompleted) { status = SendMessageStatus.SentImmediately; } else { Console.WriteLine($"Didn't send immediately so putting on progress list to check"); status = SendMessageStatus.Sending; messagesInProgress.Add(t); } } // TODO: kick queue return(status); }
public async Task HandleEmptyMessage(CoapMessage Request) { await Task.Run(() => {}); Console.WriteLine("I received a response to a message"); }
public static CoapMessage FromByteArray(byte[] bytes, IPEndPoint remoteEndpoint) { int pos = 0; if (bytes.Length < 4) { throw new CoapMessageFormatException("Not enough data to form a valid header"); } // header processing CoapMessage message = new CoapMessage(); // need to do this now because we might need the endpoint and port for the url message.Source = remoteEndpoint; byte b = bytes[pos++]; message.Version = (byte)((b & 0xc0) >> 6); message.Type = (CoapMessageType)((b & 0x30000000) >> 4); message.TokenLength = (byte)((b & 0x0f000000)); if (message.TokenLength > 8) { throw new CoapMessageFormatException("Token length must be 0-8 bytes in length."); } b = bytes[pos++]; message.Code = (CoapMessageCode)b; b = bytes[pos++]; message.MessageID = (UInt16)(b << 8); b = bytes[pos++]; message.MessageID |= b; if (message.TokenLength > 0) { message.TokenValue = new byte[message.TokenLength]; Array.Copy(bytes, pos, message.TokenValue, 0, message.TokenLength); pos += message.TokenLength; } bool processingOptions = true; int optionNumber = 0; int optionLength = 0; while (pos < bytes.Length && processingOptions) { if (bytes[pos] == 0xff) { pos++; processingOptions = false; // process payload int payloadLength = bytes.Length - pos; if (payloadLength == 0) { throw new CoapMessageFormatException("Payload marker cannot be followed by no payload"); } if (payloadLength > 0) { message.Payload = new byte[payloadLength]; Array.Copy(bytes, pos, message.Payload, 0, payloadLength); } } if (processingOptions) { int delta = (char)((bytes[pos] & 0xf0) >> 4); optionLength = (char)(bytes[pos] & 0x0f); pos++; switch (delta) { case 13: { byte extendedDelta = bytes[pos++]; optionNumber += (extendedDelta + 13); } break; case 14: { UInt16 extendedDelta = (UInt16)bytes[pos++]; extendedDelta <<= 8; extendedDelta |= bytes[pos++]; optionNumber += (extendedDelta + 269); } break; case 15: if (bytes[pos] == 0xff) { processingOptions = false; } else { throw new CoapMessageFormatException("Malformed payload marker or invalid option"); } break; default: optionNumber += delta; break; } switch (optionLength) { case 13: { byte extendedLength = bytes[pos++]; optionLength = (extendedLength + 13); } break; case 14: { UInt16 extendedLength = (UInt16)bytes[pos++]; extendedLength <<= 8; extendedLength |= bytes[pos++]; optionLength = (extendedLength + 269); } break; case 15: { } break; } // TODO: based on table in 5.10 process options according to format and store in subclasses according to type // switch (optionNumber) // { // case 1: // { // If-Match - opaque - 0-8 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 3: // { // Uri-Host - string - 1-255 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 4: // { // ETag - opaque - 1-8 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 5: // { // If-None-Match - empty - 0 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 7: // { // Uri-Port - uint - 0-2 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 8: // { // Location-Path - string - 0-255 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 11: // { // Uri-Path - string - 0-255 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 12: // { // Content-Format - uint - 0-2 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 14: // { // Max-Age - uint - 0-4 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 15: // { // Uri-Query - string - 0-255 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 17: // { // Accept - uint - 0-2 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 20: // { // Location-Query - string - 0-255 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 35: // { // Proxy-Uri - string - 1-1034 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 39: // { // Proxy-Scheme - string - 1-255 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // case 60: // { // Size1 - uint - 0-4 // byte[] value = new byte[optionLength]; // Array.Copy(bytes, pos, value, 0, optionLength); // } // break; // } byte[] value = new byte[optionLength]; Array.Copy(bytes, pos, value, 0, optionLength); pos += optionLength; // add value as option if (message.Options == null) { message.Options = new SortedDictionary <CoapOptionNumber, List <CoapOption> >(); } if (!message.Options.ContainsKey((CoapOptionNumber)optionNumber)) { message.Options[(CoapOptionNumber)optionNumber] = new List <CoapOption>(); } CoapOption option = new CoapOption(); option.Number = optionNumber; option.Length = optionLength; option.Value = value; message.Options[(CoapOptionNumber)optionNumber].Add(option); } } // construct URL for easier processing by consumers message.Uri = message.ComposeUri(); return(message); }