/// <summary> /// Notify listeners of the new temperature /// </summary> /// <param name="temp">The temperature</param> static private void NotifyListeners(int temp) { ArrayList resObservers = coapServer.ObserversList.GetResourceObservers(OBSERVED_RESOURCE_URI); if (resObservers == null || resObservers.Count == 0) { return; } //The next observe sequence number UInt16 obsSeq = (UInt16)Convert.ToInt16(DateTime.Today.ToString("mmss"));//Will get accomodated in 24-bits limit and will give good sequence foreach (CoAPRequest obsReq in resObservers) { UInt16 mId = coapServer.GetNextMessageID(); CoAPRequest notifyReq = new CoAPRequest(CoAPMessageType.CON, CoAPMessageCode.PUT, mId); notifyReq.RemoteSender = obsReq.RemoteSender; notifyReq.Token = obsReq.Token; //Add observe option with sequence number notifyReq.AddOption(CoAPHeaderOption.OBSERVE, AbstractByteUtils.GetBytes(obsSeq)); //The payload will be JSON Hashtable ht = new Hashtable(); ht.Add("temp", temp); string jsonStr = JSONResult.ToJSON(ht); notifyReq.AddPayload(jsonStr); //Tell recipient about the content-type of the response notifyReq.AddOption(CoAPHeaderOption.CONTENT_FORMAT, AbstractByteUtils.GetBytes(CoAPContentFormatOption.APPLICATION_JSON)); //send it coapServer.Send(notifyReq); } }
/// <summary> /// As bytes...endian ness is taken care of in this method /// </summary> /// <param name="reserved">Not used now</param> /// <returns>byte[]</returns> public byte[] ToStream(UInt16 reserved) { if (this.SequenceNumber <= 0xF) { //single byte byte[] retVal = new byte[1]; retVal[0] = (byte)this.SequenceNumber; retVal[0] = (byte)(retVal[0] << 4); //seq number in top 4 bits retVal[0] = (this.HasMoreBlocks) ? (byte)(retVal[0] | 0x08) : retVal[0]; //more flag in 5th bit retVal[0] = (byte)(retVal[0] | this.SizeExponent); //size exponent, last 3 bits return(retVal); } else if (this.SequenceNumber > 0xF && this.SequenceNumber <= 0xFFF) { //double byte byte[] temp = new byte[2]; if (AbstractByteUtils.IsTargetLittleEndian()) { temp = AbstractByteUtils.GetBytes((UInt16)(this._seqNumber << 4)); temp[0] = (this.HasMoreBlocks) ? (byte)(temp[0] | 0x08) : (byte)(temp[0] & 0xF7); temp[0] = (byte)(temp[0] | this.SizeExponent); } else { temp = AbstractByteUtils.GetBytes((UInt16)(this._seqNumber >> 4)); temp[1] = (this.HasMoreBlocks) ? (byte)(temp[1] | 0x08) : (byte)(temp[1] & 0xF7); temp[1] = (byte)(temp[1] | this.SizeExponent); } return(temp); } else { //3 bytes...we start with 4(long) and then return last 3 only byte[] temp = new byte[4]; byte[] retVal = new byte[3]; if (AbstractByteUtils.IsTargetLittleEndian()) { temp = AbstractByteUtils.GetBytes((UInt64)(this._seqNumber << 4)); temp[0] = (this.HasMoreBlocks) ? (byte)(temp[0] | 0x08) : (byte)(temp[0] & 0xF7); temp[0] = (byte)(temp[0] | this.SizeExponent); Array.Copy(temp, 0, retVal, 0, 3); } else { //use the index =2, index 3 will be discarded temp = AbstractByteUtils.GetBytes((UInt64)(this._seqNumber >> 4)); temp[2] = (this.HasMoreBlocks) ? (byte)(temp[1] | 0x08) : (byte)(temp[1] & 0xF7); temp[2] = (byte)(temp[1] | this.SizeExponent); Array.Copy(temp, 1, retVal, 0, 3); } return(retVal); } }
/// <summary> /// Called when a CoAP request is received...we will only support CON requests /// of type GET... the path is sensors/temp /// </summary> /// <param name="coapReq">CoAPRequest object</param> private static void OnCoAPRequestReceived(CoAPRequest coapReq) { string reqPath = (coapReq.GetPath() != null) ? coapReq.GetPath().ToLower() : ""; if (coapReq.MessageType.Value == CoAPMessageType.CON) { if (coapReq.Code.Value != CoAPMessageCode.GET) { CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.METHOD_NOT_ALLOWED, coapReq /*Copy all necessary values from request in the response*/); //When you use the constructor that accepts a request, then automatically //the message id , token and remote sender values are copied over to the response coapServer.Send(resp); } else if (reqPath != "sensors/temp") { //We do not understand this.. CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.NOT_FOUND, coapReq /*Copy all necessary values from request in the response*/); coapServer.Send(resp); } else { Debug.WriteLine(coapReq.ToString()); CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.CONTENT, coapReq /*Copy all necessary values from request in the response*/); //The payload will be JSON Hashtable ht = new Hashtable(); ht.Add("temp", GetRoomTemperature()); string jsonStr = JSONResult.ToJSON(ht); resp.AddPayload(jsonStr); //Tell recipient about the content-type of the response resp.AddOption(CoAPHeaderOption.CONTENT_FORMAT, AbstractByteUtils.GetBytes(CoAPContentFormatOption.APPLICATION_JSON)); //send it coapServer.Send(resp); } } }
/// <summary> /// Handle request received from remote clients /// </summary> /// <param name="msgStream">The byte stream that represents a request message</param> /// <param name="sender">The remote sender endpoint</param> protected virtual void ProcessRequestMessageReceived(byte[] msgStream, ref EndPoint sender) { CoAPRequest coapReq = new CoAPRequest(); IPEndPoint remoteSender = (IPEndPoint)sender; try { coapReq.FromByteStream(msgStream); coapReq.RemoteSender = new IPEndPoint(remoteSender.Address, remoteSender.Port); //Setup who sent this message //setup the default values of host and port if (!coapReq.Options.HasOption(CoAPHeaderOption.URI_HOST)) { coapReq.Options.AddOption(CoAPHeaderOption.URI_HOST, AbstractByteUtils.StringToByteUTF8(this._host)); } if (!coapReq.Options.HasOption(CoAPHeaderOption.URI_PORT)) { coapReq.Options.AddOption(CoAPHeaderOption.URI_PORT, AbstractByteUtils.GetBytes((UInt16)this._port)); } if (coapReq.MessageType.Value == CoAPMessageType.CON && coapReq.Code.Value == CoAPMessageCode.EMPTY) { //This is a PING..send a RST this.RespondToPing(coapReq); } else { this.HandleRequestReceived(coapReq);//Other messages, let program handle it } } catch { ;//TOCHECK::Do nothing, we do not want to crash the server just because we //could not process one received message...will check later how to improve this } }
/// <summary> /// This thread continuously looks for messages on the socket /// Once available, it will post them for handling downstream /// </summary> protected void ProcessReceivedMessages() { byte[] buffer = null; int maxSize = AbstractNetworkUtils.GetMaxMessageSize(); while (!this._isDone) { Thread.Sleep(1000); try { if (this._clientSocket.Available >= 4 /*Min size of CoAP block*/) { buffer = new byte[maxSize * 2]; int bytesRead = this._clientSocket.Receive(buffer); byte[] udpMsg = new byte[bytesRead]; Array.Copy(buffer, udpMsg, bytesRead); byte mType = AbstractCoAPMessage.PeekMessageType(udpMsg); if ((mType == CoAPMessageType.CON || mType == CoAPMessageType.NON) && AbstractCoAPMessage.PeekIfMessageCodeIsRequestCode(udpMsg)) { //This is a request CoAPRequest coapReq = new CoAPRequest(); coapReq.FromByteStream(udpMsg); coapReq.RemoteSender = this._remoteEP;//Setup who sent this message string uriHost = ((IPEndPoint)this._remoteEP).Address.ToString(); UInt16 uriPort = (UInt16)((IPEndPoint)this._remoteEP).Port; //setup the default values of host and port //setup the default values of host and port if (!coapReq.Options.HasOption(CoAPHeaderOption.URI_HOST)) { coapReq.Options.AddOption(CoAPHeaderOption.URI_HOST, AbstractByteUtils.StringToByteUTF8(uriHost)); } if (!coapReq.Options.HasOption(CoAPHeaderOption.URI_PORT)) { coapReq.Options.AddOption(CoAPHeaderOption.URI_PORT, AbstractByteUtils.GetBytes(uriPort)); } this.HandleRequestReceived(coapReq); } else { //This is a response CoAPResponse coapResp = new CoAPResponse(); coapResp.FromByteStream(udpMsg); coapResp.RemoteSender = this._remoteEP;//Setup who sent this message //Remove the waiting confirmable message from the timeout queue if (coapResp.MessageType.Value == CoAPMessageType.ACK || coapResp.MessageType.Value == CoAPMessageType.RST) { this._msgPendingAckQ.RemoveFromWaitQ(coapResp.ID.Value); } this.HandleResponseReceived(coapResp); } } } catch (SocketException se) { //Close this client connection this._isDone = true; this.HandleError(se, null); } catch (ArgumentNullException argEx) { this.HandleError(argEx, null); } catch (ArgumentException argEx) { this.HandleError(argEx, null); } catch (CoAPFormatException fEx) { //Invalid message.. this.HandleError(fEx, null); } } }
/// <summary> /// Convert this object into a byte stream in network byte order /// </summary> /// <param name="reserved">Not used now</param> /// <returns>byte array</returns> public byte[] ToStream(UInt16 reserved) { byte[] mID = AbstractByteUtils.GetBytes(this.Value); mID = AbstractNetworkUtils.ToNetworkByteOrder(mID); return(mID); }
/// <summary> /// Set the request URL /// </summary> /// <param name="coapURL">The coap URL associated with the request</param> public void SetURL(string coapURL) { if (coapURL == null || coapURL.Trim().Length == 0) { throw new ArgumentNullException("Invalid CoAP URL"); } coapURL = coapURL.Trim(); /* * The URI object provided by NETMF does not work if the scheme is not http/https * Therefore, after checking for the scheme, we replace it with http * and then use the Uri class for other items */ string scheme = AbstractURIUtils.GetUriScheme(coapURL); if (scheme == null || (scheme.Trim().ToLower() != "coap" && scheme.Trim().ToLower() != "coaps")) { throw new ArgumentException("Invalid CoAP URL"); } if (scheme.Trim().ToLower() == "coaps") { this.IsSecure = true; } if (coapURL.IndexOf("#") >= 0) { throw new ArgumentException("Fragments not allowed in CoAP URL"); } //Add these items as option //The host this.Options.AddOption(CoAPHeaderOption.URI_HOST, AbstractByteUtils.StringToByteUTF8(AbstractURIUtils.GetUriHost(coapURL))); //The port this.Options.AddOption(CoAPHeaderOption.URI_PORT, AbstractByteUtils.GetBytes((UInt16)AbstractURIUtils.GetUriPort(coapURL))); //Path components string[] segments = AbstractURIUtils.GetUriSegments(coapURL); if (segments != null && segments.Length > 0) { foreach (string segment in segments) { if (segment.Trim().Length == 0) { continue; } this.Options.AddOption(CoAPHeaderOption.URI_PATH, AbstractByteUtils.StringToByteUTF8(AbstractURIUtils.UrlDecode(segment))); } } //Query string[] qParams = AbstractURIUtils.GetQueryParameters(coapURL); if (qParams != null && qParams.Length > 0) { foreach (string queryComponent in qParams) { if (queryComponent.Trim().Length == 0) { continue; } this.Options.AddOption(CoAPHeaderOption.URI_QUERY, AbstractByteUtils.StringToByteUTF8(AbstractURIUtils.UrlDecode(queryComponent))); } } }
/// <summary> /// Called when a CoAP request is received (NON, CON) /// </summary> /// <param name="coapReq">CoAPRequest object</param> private static void OnCoAPRequestReceived(CoAPRequest coapReq) { string reqPath = (coapReq.GetPath() != null) ? coapReq.GetPath().ToLower() : ""; /* * For well-know path, we should support both CON and NON. * For NON request, we send back the details in another NON message * For CON request, we send back the details in an ACK */ /*Well known should be a GET*/ if (coapReq.Code.Value != CoAPMessageCode.GET) { if (coapReq.MessageType.Value == CoAPMessageType.CON) { CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.METHOD_NOT_ALLOWED, coapReq /*Copy all necessary values from request in the response*/); //When you use the constructor that accepts a request, then automatically //the message id , token and remote sender values are copied over to the response coapServer.Send(resp); } else { //For NON, we can only send back a RST CoAPResponse resp = new CoAPResponse(CoAPMessageType.RST, CoAPMessageCode.METHOD_NOT_ALLOWED, coapReq /*Copy all necessary values from request in the response*/); //When you use the constructor that accepts a request, then automatically //the message id , token and remote sender values are copied over to the response coapServer.Send(resp); } } else { //Message type is GET...check the path..this server only supports well-known path if (reqPath != ".well-known/core") { if (coapReq.MessageType.Value == CoAPMessageType.CON) { CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.NOT_FOUND, coapReq /*Copy all necessary values from request in the response*/); coapServer.Send(resp); } else { //For NON, we can only send back a RST CoAPResponse resp = new CoAPResponse(CoAPMessageType.RST, CoAPMessageCode.NOT_FOUND, coapReq /*Copy all necessary values from request in the response*/); coapServer.Send(resp); } } else { //Request is GET and path is right if (coapReq.MessageType.Value == CoAPMessageType.CON) { CoAPResponse resp = new CoAPResponse(CoAPMessageType.ACK, CoAPMessageCode.CONTENT, coapReq /*Copy all necessary values from request in the response*/); //Add response payload resp.AddPayload(GetSupportedResourceDescriptions()); //Tell recipient about the content-type of the response resp.AddOption(CoAPHeaderOption.CONTENT_FORMAT, AbstractByteUtils.GetBytes(CoAPContentFormatOption.APPLICATION_LINK_FORMAT)); coapServer.Send(resp); } else { //Its a NON, send a NON back...in CoAPSharp, NON is always considered as request CoAPResponse resp = new CoAPResponse(CoAPMessageType.NON, CoAPMessageCode.CONTENT, coapReq.ID.Value); //Copy over other needed values from the reqeust resp.Token = coapReq.Token; resp.RemoteSender = coapReq.RemoteSender; resp.AddPayload(GetSupportedResourceDescriptions()); //Tell recipient about the content-type of the response resp.AddOption(CoAPHeaderOption.CONTENT_FORMAT, AbstractByteUtils.GetBytes(CoAPContentFormatOption.APPLICATION_LINK_FORMAT)); //send it coapServer.Send(resp); } } } }
/// <summary> /// Get the value as a byte array /// </summary> /// <returns>byte array</returns> public byte[] GetValueAsBytes() { return(AbstractByteUtils.GetBytes(Value)); }
/// <summary> /// Convert this option into a byte stream int network byte order /// </summary> /// <param name="previousOptionNumber">The previous option number in the sequence of options</param> /// <returns>byte array</returns> public byte[] ToStream(UInt16 previousOptionNumber) { //The option format is given below /********************************************* | 4 bits option delta | 4 bits length | | ---------------------------------------------- | 0-2 bytes option delta extended | | ---------------------------------------------- | 0-2 bytes option length extended | | ---------------------------------------------- | 0-* bytes option value | *********************************************/ int streamLength = 1; //Get Option delta byte optionDelta = 0; UInt16 derivedOptionNumber = (UInt16)(this.Number - previousOptionNumber); byte[] optionDeltaEx = null; if (derivedOptionNumber <= 12) { optionDelta = (byte)derivedOptionNumber; } else if (derivedOptionNumber > 12 && derivedOptionNumber <= 255 /*Option number is single byte*/) { optionDeltaEx = new byte[1] { (byte)(derivedOptionNumber - 13) }; optionDelta = 13; streamLength++;//1 additional byte } else /*Option number is double byte*/ { optionDelta = 14; optionDeltaEx = AbstractByteUtils.GetBytes((UInt16)(derivedOptionNumber - 269)); optionDeltaEx = AbstractNetworkUtils.ToNetworkByteOrder(optionDeltaEx); streamLength += 2; //two additional bytes } //Get option length byte optionLength = 0; byte[] optionLengthEx = null; if (this.Value != null) { if (this.Value.Length > 0 && this.Value.Length < 13) { optionLength = (byte)this.Value.Length; } else if (this.Value.Length > 12 && this.Value.Length <= 255) { optionLength = 13; optionLengthEx = new byte[1] { (byte)(this.Value.Length - 13) }; streamLength++;//1 additional byte } else if (this.Value.Length > 255) { optionLength = 14; optionLengthEx = AbstractByteUtils.GetBytes((UInt16)(this.Value.Length - 269)); optionLengthEx = AbstractNetworkUtils.ToNetworkByteOrder(optionLengthEx); streamLength += 2; //two additional bytes } } streamLength += ((this.Value != null && !this.IsEmpty()) ? this.Value.Length : 0); byte[] optionStream = new byte[streamLength]; int count = 0; optionStream[count++] = (byte)((optionDelta << 4) | (optionLength)); //header + length if (optionDelta == 13) //delta extended { optionStream[count++] = optionDeltaEx[0]; } else if (optionDelta == 14) { optionStream[count++] = optionDeltaEx[0]; optionStream[count++] = optionDeltaEx[1]; } if (optionLength == 13) { optionStream[count++] = optionLengthEx[0]; } else if (optionLength == 14) { optionStream[count++] = optionLengthEx[0]; optionStream[count++] = optionLengthEx[1]; } if (this.Value != null && this.Value.Length > 0 && !this.IsEmpty()) { byte[] optionValue = new byte[this.Value.Length]; Array.Copy(this.Value, optionValue, this.Value.Length); if (this.NeedsByteOrdering(this.Number)) { optionValue = AbstractNetworkUtils.ToNetworkByteOrder(optionValue); } Array.Copy(optionValue, 0, optionStream, count, optionValue.Length); } return(optionStream); }
/// <summary> /// Receive a message from the server. This will block if there /// are no messages. Please note, you must handle all errors (except timeout) /// and no error is raised. /// </summary> /// <param name="rxTimeoutMillis"> /// The timeout value in milliseconds.The default value is 0, which indicates an infinite time-out period. /// Specifying -1 also indicates an infinite time-out period /// </param> /// <param name="timedOut">Is set to true on timeout</param> /// <returns>An instance of AbstractCoAPMessage on success, else null on error/timeout</returns> public AbstractCoAPMessage ReceiveMessage(int rxTimeoutMillis, ref bool timedOut) { byte[] buffer = null; int maxSize = AbstractNetworkUtils.GetMaxMessageSize(); CoAPRequest coapReq = null; CoAPResponse coapResp = null; try { this._clientSocket.ReceiveTimeout = rxTimeoutMillis; buffer = new byte[maxSize * 2]; int bytesRead = this._clientSocket.Receive(buffer); byte[] udpMsg = new byte[bytesRead]; Array.Copy(buffer, udpMsg, bytesRead); byte mType = AbstractCoAPMessage.PeekMessageType(udpMsg); if ((mType == CoAPMessageType.CON || mType == CoAPMessageType.NON) && AbstractCoAPMessage.PeekIfMessageCodeIsRequestCode(udpMsg)) { //This is a request coapReq = new CoAPRequest(); coapReq.FromByteStream(udpMsg); coapReq.RemoteSender = this._remoteEP;//Setup who sent this message string uriHost = ((IPEndPoint)this._remoteEP).Address.ToString(); UInt16 uriPort = (UInt16)((IPEndPoint)this._remoteEP).Port; //setup the default values of host and port //setup the default values of host and port if (!coapReq.Options.HasOption(CoAPHeaderOption.URI_HOST)) { coapReq.Options.AddOption(CoAPHeaderOption.URI_HOST, AbstractByteUtils.StringToByteUTF8(uriHost)); } if (!coapReq.Options.HasOption(CoAPHeaderOption.URI_PORT)) { coapReq.Options.AddOption(CoAPHeaderOption.URI_PORT, AbstractByteUtils.GetBytes(uriPort)); } return(coapReq); } else { //This is a response coapResp = new CoAPResponse(); coapResp.FromByteStream(udpMsg); coapResp.RemoteSender = this._remoteEP;//Setup who sent this message return(coapResp); } } catch (SocketException se) { if (se.ErrorCode == (int)SocketError.TimedOut) { timedOut = true; } else { throw se; } } return(null); }