Quantcast
Channel: CoAPSharp - CoAP for .NET » Documentation
Viewing all articles
Browse latest Browse all 10

CoAP Client for Block Transfer Using PUT

$
0
0

How to Write a Client that uses Block Transfer to Send Data to Server using PUT Option

Before you try out this sample, we suggest that you go through the CoAP block transfer draft or at-least read the primer of Block Transfer

NOTE: This sample is same as the sample for V 0.1.0 and there are no changes.

Pre-Requisites:

  1. You need Microsoft .NET Micro Framework 4.2
  2. You need Visual Studio 2010 or higher (even an express edition will do)
  3. This tutorial will use the emulator, but if you wish, you can download this to a Netduino 2 Plus board. For working with Netduino, please visit their website

Example Scenario: 

Let’s take an example based on which this CoAP client will be built.  A client has a binary data to send to the server. Given that we want to keep the size of the message small, we would like to break the entire binary data into smaller chunks and send the chunks in multiple calls to the server. In this example, we take a binary array of length 35 bytes. We also use a size exponent of zero. Therefore, per message we will send only 16 bytes (Size= 2 ^ (SizeExp + 4))

Now, if we are sending 16 bytes per server call, then we need 3 calls, 16 bytes in first call, 16 bytes in second call and 3 bytes in the last call.

Key Concepts Covered:

  1. How to use the block option
  2. How to make multiple calls for a large data transfer
  3. How to send size information to server
  4. How to inform server there is more data to send or this is the last block

Steps: 

  1. Create a console project in Microsoft .NET Micro framework (or a Netduino 2 Plus Application for Netduino boards)
  2. Now, pickup the CoAPSharp binary and add that as a reference.
  3. Now let’s first define a few variables that we need for housekeeping. We need the following variables:
    - A constant to keep the maximum block size
    - A byte array that holds the data to transfer
    - A block sequence number tracker (This number must fit in 24 bits, so a 16-bit number should suffice)
    - An instance of the client channel
    - A variable to keep track of how many bytes were transferred.
    public class Program
    {
        /// <summary>
        /// What is our block size in terms of bytes
        /// </summary>
        private const int BLOCK_SIZE_BYTES = 16;
        /// <summary>
        /// We want to transfer 35 bytes, in 16 byte blocks..3 transfers
        /// </summary>
        private byte[] _dataToTransfer = new byte[35];
        /// <summary>
        /// Holds the sequence number of the block
        /// </summary>
        private UInt32 _blockSeqNo = 0;
        /// <summary>
        /// Holds the client channel instance
        /// </summary>
        private CoAPClientChannel _client = null;
        /// <summary>
        /// Holds how many bytes got transferred to server
        /// </summary>
        private int _totalBytesTransferred = 0;
        /// <summary>
        /// Entry point
        /// </summary>
        public static void Main()
        {
  4. Let’s first setup the client and wire up the events
    /// <summary>
    /// Setup the client
    /// </summary>
    public void SetupClient()
    {
        this._client = new CoAPClientChannel();
        this._client.Initialize("localhost", 5683);
        this._client.CoAPError +=new CoAPErrorHandler(OnCoAPError);
        this._client.CoAPRequestReceived += new CoAPRequestReceivedHandler(OnCoAPRequestReceived);
        this._client.CoAPResponseReceived += new CoAPResponseReceivedHandler(OnCoAPResponseReceived);
    }
  5. Let’s define a method to check if we have more data to send
    /// <summary>
    /// Check if we have data to send
    /// </summary>
    /// <returns>bool</returns>
    public bool HasDataToSend()
    {
        //In this example, we are only transferring on large data set...
        return (this._totalBytesTransferred < this._dataToTransfer.Length);
    }
  6. Now, we write the method that sends a single block. In this example, we are sending a maximum of 16 bytes per call. This is a PUT method, and as per the specifications, you will use the Block1 option for such a transfer. We first create a CON request of type PUT. We calculate the start index and length of the source byte array and put the selected bytes as the payload. The only major change in this example is that we add the Block1 option. During each transfer, we send the new sequence number (it’s just a one-up number). Also, we keep sending “HasMoreBlocks” as true, until we reach the last block, when this attribute is made true.
    /// <summary>
    /// Transfer data to server in blocks
    /// </summary>
    /// <returns>true if there are more blocks to be transferred</returns>
    public void TransferToServerInBlocks()
    {
        CoAPRequest blockPUTReq = new CoAPRequest(CoAPMessageType.CON, CoAPMessageCode.PUT, this._client.GetNextMessageID());
        blockPUTReq.SetURL("coap://localhost:5683/largedata/blockput");
        blockPUTReq.AddTokenValue(DateTime.Now.ToString("HHmm"));
        //Get needed bytes from source            
        int copyBeginIdx = (int)(this._blockSeqNo * BLOCK_SIZE_BYTES);
        int bytesToCopy = ((copyBeginIdx + BLOCK_SIZE_BYTES) < this._dataToTransfer.Length) ? BLOCK_SIZE_BYTES : (this._dataToTransfer.Length - copyBeginIdx);
        byte[] blockToSend = new byte[bytesToCopy];
        Array.Copy(this._dataToTransfer, copyBeginIdx, blockToSend, 0, bytesToCopy);
        //Calculate how many more bytes left to transfer
        bool hasMore = (this._totalBytesTransferred + bytesToCopy < this._dataToTransfer.Length);
        //Add the bytes to the payload
        blockPUTReq.Payload = new CoAPPayload(blockToSend);
        //Now add block option to the request
        blockPUTReq.SetBlockOption(CoAPHeaderOption.BLOCK1,new CoAPBlockOption(this._blockSeqNo, hasMore, CoAPBlockOption.BLOCK_SIZE_16));
        //send
        this._client.Send(blockPUTReq);
        //Updated bytes transferred
        this._totalBytesTransferred += bytesToCopy;
    }
  7. Now, once the data is sent, the server is expected to respond back with an ACK. The server also adds the Block1 option in the response. The block option contains data to indicate what block was accepted by the server. Sometimes, the server may take time to extract all data from it’s endpoint. In that case, it may send the “HasMoreBlocks” as true in the response to indicate that the client should wait for another ACK with “HasMoreBlocks” as false. We then increment the block number. After that, we check if there are additional data to send and then send the next block.
    /// <summary>
    /// Once a response is received, check if that has block option set.
    /// If yes, server has responded back. Ensure you check the more flag.
    /// If that falg was set in the response, that means, server is still
    /// getting the data that you sent in the previous PUT request. So wait
    /// for a final ACK
    /// </summary>
    /// <param name="coapResp">CoAPResponse</param>
    void OnCoAPResponseReceived(CoAPResponse coapResp)
    {
        if (coapResp.MessageType.Value == CoAPMessageType.ACK)
        {
            CoAPBlockOption returnedBlockOption = coapResp.GetBlockOption(CoAPHeaderOption.BLOCK1);
            if (returnedBlockOption != null && !returnedBlockOption.HasMoreBlocks)
            {
                //send the next block
                this._blockSeqNo++;
                if(this.HasDataToSend()) this.TransferToServerInBlocks();
            }
        }
    }

Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images