Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example for block transfer with UDP CoAP #593

Open
EfraimManurung opened this issue Dec 9, 2024 · 2 comments
Open

Example for block transfer with UDP CoAP #593

EfraimManurung opened this issue Dec 9, 2024 · 2 comments

Comments

@EfraimManurung
Copy link

Hi, 

Is there documentation or examples related to CoAP for block transfer for both client and server?

I searched throughout the examples and files and still did not find it. I would like to send a big chunk of payload around 1024 bytes from client to server. The simple example is the opposite, which is the client only GET from the server that serves some payload.

I also saw many issues asked related to block transfer examples. It would be helpful to tackle this problem together for the examples code.

@jkralik
Copy link
Member

jkralik commented Dec 9, 2024

Hi,

Blockwise transfers are enabled by default for both DTLS and UDP. To use blockwise, simply send a POST or PUT request. Here's an example:

Client

package main

import (
	"bytes"
	"context"
	"log"
	"os"
	"time"

	"github.com/plgd-dev/go-coap/v3/message"
	"github.com/plgd-dev/go-coap/v3/udp"
)

func main() {
	co, err := udp.Dial("localhost:5688")
	if err != nil {
		log.Fatalf("Error dialing: %v", err)
	}

	path := "/a"
	if len(os.Args) > 1 {
		path = os.Args[1]
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	resp, err := co.Post(ctx, path, message.TextPlain, bytes.NewReader(make([]byte, 1024*1024)))
	if err != nil {
		log.Fatalf("Error sending request: %v", err)
	}

	log.Printf("Response payload: %v", resp.String())
}

Server

package main

import (
	"bytes"
	"io"
	"log"

	coap "github.com/plgd-dev/go-coap/v3"
	"github.com/plgd-dev/go-coap/v3/message"
	"github.com/plgd-dev/go-coap/v3/message/codes"
	"github.com/plgd-dev/go-coap/v3/mux"
)

func loggingMiddleware(next mux.Handler) mux.Handler {
	return mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) {
		log.Printf("ClientAddress %v, %v\n", w.Conn().RemoteAddr(), r.String())
		next.ServeCOAP(w, r)
	})
}

func handleRequest(w mux.ResponseWriter, r *mux.Message) {
	customResp := w.Conn().AcquireMessage(r.Context())
	defer w.Conn().ReleaseMessage(customResp)

	if r.Body() != nil {
		data, err := io.ReadAll(r.Body())
		if err != nil {
			log.Printf("Cannot read body: %v", err)
			return
		}
		log.Printf("Received length: %v", len(data))
	}

	customResp.SetCode(codes.Changed)
	customResp.SetToken(r.Token())
	customResp.SetContentFormat(message.TextPlain)
	customResp.SetBody(bytes.NewReader([]byte("Response from server")))
	err := w.Conn().WriteMessage(customResp)
	if err != nil {
		log.Printf("Cannot set response: %v", err)
	}
}

func main() {
	router := mux.NewRouter()
	router.Use(loggingMiddleware)
	router.Handle("/a", mux.HandlerFunc(handleRequest))

	log.Fatal(coap.ListenAndServe("udp", ":5688", router))
}

Additional Configuration

To customize blockwise behavior, use the option provided in WithBlockwise. Configure the server via coap.ListenAndServeWithOptions and the client via Dial similar as is in the example. Also there is a limit for max message size (64KB by default) so If you want to send 1MB you need to set also the option WithMaxMessageSize to 1024*1024.

@EfraimManurung
Copy link
Author

Hi Jozef, thank you for your response, I will try it.

I also figured it out today from the simple examples,

it would be nice if we put our code to the examples, to help others as references.

Client

package main

import (
	"bytes"
	"context"
	"log"
	"os"
	"time"

	"github.com/plgd-dev/go-coap/v3/message"
	"github.com/plgd-dev/go-coap/v3/net/blockwise"
	"github.com/plgd-dev/go-coap/v3/options"
	"github.com/plgd-dev/go-coap/v3/udp"
)

func main() {
	// Configure blockwise options: enable=true, block size=SZX1024, timeout=5s
	blockwiseOpt := options.WithBlockwise(true, blockwise.SZX1024, 5*time.Second)

	client, err := udp.Dial("127.0.0.1:5683", blockwiseOpt)
	if err != nil {
		log.Fatalf("Error dialing: %v", err)
	}

	path := "/big-endpoint"
	if len(os.Args) > 1 {
		path = os.Args[1]
	}

	// create a 1024-byte payload
	payload := make([]byte, 1024)
	for i := 0; i < 1024; i++ {
		payload[i] = byte(i % 256) // Fill with example data
	}
	
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	resp, err := client.Post(ctx, path, message.TextPlain, bytes.NewReader(payload))
	if err != nil {
		log.Fatalf("Error sending request: %v", err)
	}
	log.Printf("Response payload: %v", resp.String())
}

Server

package main

import (
	"bytes"
	"log"

	coap "github.com/plgd-dev/go-coap/v3"
	"github.com/plgd-dev/go-coap/v3/message"
	"github.com/plgd-dev/go-coap/v3/message/codes"
	"github.com/plgd-dev/go-coap/v3/mux"
)

func loggingMiddleware(next mux.Handler) mux.Handler {
	return mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) {
		log.Printf("ClientAddress %v, %v\n", w.Conn().RemoteAddr(), r.String())
		next.ServeCOAP(w, r)
	})
}

func handleA(w mux.ResponseWriter, r *mux.Message) {
	err := w.SetResponse(codes.Content, message.TextPlain, bytes.NewReader([]byte("success")))
	if err != nil {
		log.Printf("cannot set response: %v", err)
	}
}

func handleB(w mux.ResponseWriter, r *mux.Message) {
	customResp := w.Conn().AcquireMessage(r.Context())
	defer w.Conn().ReleaseMessage(customResp)
	customResp.SetCode(codes.Content)
	customResp.SetToken(r.Token())
	customResp.SetContentFormat(message.TextPlain)
	customResp.SetBody(bytes.NewReader([]byte("B hello world")))
	err := w.Conn().WriteMessage(customResp)
	if err != nil {
		log.Printf("cannot set response: %v", err)
	}
}

func main() {
	r := mux.NewRouter()
	r.Use(loggingMiddleware)
	r.Handle("/big-endpoint", mux.HandlerFunc(handleA))
	r.Handle("/b", mux.HandlerFunc(handleB))

	log.Fatal(coap.ListenAndServe("udp", "127.0.0.1:5683", r))
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants