-
-
Notifications
You must be signed in to change notification settings - Fork 49
Getting started
Neffos provides a comprehensive API to work with. Identical API and keywords between server and client sides.
Let's start by building a small echo application between server and a client. Client will send something and server will respond with a prefix of "echo back: "
.
Supposedly we have both server and client side in the same project (or package).
Create a new echo.go
file.
package main
Import the neffos
go package and, optionally, the log
standard go package in order to help us print incoming messages.
import (
"log"
"github.com/kataras/neffos"
)
Incoming events are being captured through callbacks. A callback declaration is a type of func(*neffos.NSConn, neffos.Message) error
.
Note that in this example, we share the same event's callback across client and server sides, depending on your app's requirements you may want to separate those events.
func onEcho(c *neffos.NSConn, msg neffos.Message) error {
body := string(msg.Body)
log.Println(body)
if !c.Conn.IsClient() {
// this block will only run on a server-side callback.
newBody := append([]byte("echo back: "), msg.Body...)
return neffos.Reply(newBody)
}
return nil
}
The neffos.Reply
is just a helper function which writes back to the connection the same incoming neffos.Message
with a custom body data.
Alternatively you may Emit
a remote event, depending on your app's needs, this is how:
c.Emit("echo", newBody)
Which is the same as:
c.Conn.Write(neffos.Message{Namespace: "v1", Event: "echo", Body: newBody})
Callbacks are registered via the Namespaces & Events
event-driven API. Let's add an "echo"
event on a namespace called "v1"
. Note that Events
can be registered without a namespace as well but it's highly recommented that you put them under a non-empty namespace for future maintainability.
var events = neffos.Namespaces{
"v1": neffos.Events{
"echo": onEcho,
},
}
A neffos (Go) client expects a compatible neffos.Dialer
to dial the neffos websocket server.
Among the neffos import statement, add one of the two built-in compatible Upgraders & Dialers. For example the neffos/gorilla
sub-package helps us to adapt the gorilla websocket implementation into neffos.
Read more about Upgraders and Dialers.
import (
// [...]
"github.com/kataras/neffos/gorilla"
)
Creating a websocket client is done by the Dial
package-level function.
The Dial
function accepts a context.Context
as its first parameter which you can use to manage dialing timeout.
The url
as its third input parameter is the endpoint which our websocket server waits for incoming requests, i.e ws://localhost:8080/echo
.
The final parameter you have to pass to create both client and server is a ConnHandler
(Events
or Namespaces with Events
).
ctx := context.Background()
client, err := neffos.Dial(ctx, gorilla.DefaultDialer, "ws://localhost:8080/echo", events)
The client must be connected to one or more server namespaces (even if a namespace is empty ""
) in order to initialize the communication between them.
Let's connect our client to the "v1"
namespace and emit the remote (in this case, server side's) "echo"
event with the data of "Greeetings!"
.
c, err := client.Connect(ctx, "v1")
c.Emit("echo", []byte("Greetings!"))
The client's onEcho
will be fired and it will print the message of echo back: Greetings!
.
Note that event emitting and firing are asynchronous operations, if you exit the program immediately after it, there is not enough time to fire back the "echo"
event that the server-side remotely fired from its side.
However, if you need to block until response is received use the c.Ask
instead:
responseMessage, err := c.Ask(ctx, "echo", []byte("Greetings!"))
In that case, the responseMessage.Body
is expected to be []byte("echo back: Greetings!")
.
Creating a websocket server is done by the neffos.New
package-level function.
A neffos server expects a compatible neffos.Upgrader
to upgrade HTTP incoming connection to WebSocket.
websocketServer := neffos.New(gorilla.DefaultUpgrader, events)
A websocket server should run inside an http endpoint, so clients can connect. We need to create an http web server and add a route which will serve and upgrade the incoming requests using the neffos server. For that, you can choose between standard package net/http and a high-performant package like iris.
The neffos.Server
completes the http.Handler
interface.
func (*Server) ServeHTTP(http.ResponseWriter, *http.Request)
Assuming that we want our websocket clients to communicate with our websocket server through the localhost:8080/echo
endpoint, follow the below guide.
If you choose net/http
, this is how you register the neffos server to an http endpoint:
import (
// [...]
"net/http"
)
func main() {
// [websocketServer := neffos.New...]
router := http.NewServeMux()
router.Handle("/echo", websocketServer)
http.ListenAndServe(":8080", router)
}
Otherwise install iris using the go get -u github.com/kataras/iris
terminal command.
Iris has its own adapter for neffos, it lives inside its iris/websocket
sub-package, we need to import that as well.
import (
// [...]
"github.com/kataras/iris"
"github.com/kataras/iris/websocket"
)
func main() {
// [websocketServer := neffos.New...]
app := iris.New()
app.Get("/echo", websocket.Handler(websocketServer))
app.Run(iris.Addr(":8080"))
}
The final program, which contains both server and client side is just 75 lines of code.
package main
import (
"context"
"log"
"net/http"
"os"
"github.com/kataras/neffos"
"github.com/kataras/neffos/gorilla"
)
var events = neffos.Namespaces{
"v1": neffos.Events{
"echo": onEcho,
},
}
func onEcho(c *neffos.NSConn, msg neffos.Message) error {
body := string(msg.Body)
log.Println(body)
if !c.Conn.IsClient() {
newBody := append([]byte("echo back: "), msg.Body...)
return neffos.Reply(newBody)
}
return nil
}
func main() {
args := os.Args[1:]
if len(args) == 0 {
log.Fatalf("expected program to start with 'server' or 'client' argument")
}
side := args[0]
switch side {
case "server":
runServer()
case "client":
runClient()
default:
log.Fatalf("unexpected argument, expected 'server' or 'client' but got '%s'", side)
}
}
func runServer() {
websocketServer := neffos.New(gorilla.DefaultUpgrader, events)
router := http.NewServeMux()
router.Handle("/echo", websocketServer)
log.Println("Serving websockets on localhost:8080/echo")
log.Fatal(http.ListenAndServe(":8080", router))
}
func runClient() {
ctx := context.Background()
client, err := neffos.Dial(ctx,gorilla.DefaultDialer,"ws://localhost:8080/echo",events)
if err != nil {
panic(err)
}
c, err := client.Connect(ctx, "v1")
if err != nil {
panic(err)
}
c.Emit("echo", []byte("Greetings!"))
// a channel that blocks until client is terminated,
// i.e by CTRL/CMD +C.
<-client.NotifyClose
}
Home | About | Project | Getting Started | Technical Docs | Copyright © 2019-2023 Gerasimos Maropoulos. Documentation terms of use.