A modern, fast and scalable websocket framework with elegant API written in Go

Overview

neffos chat example

build status report card view examples chat frontend pkg

About neffos

Neffos is a cross-platform real-time framework with expressive, elegant API written in Go. Neffos takes the pain out of development by easing common tasks used in real-time backend and frontend applications such as:

  • Scale-out using redis or nats*
  • Adaptive request upgradation and server dialing
  • Acknowledgements
  • Namespaces
  • Rooms
  • Broadcast
  • Event-Driven architecture
  • Request-Response architecture
  • Error Awareness
  • Asynchronous Broadcast
  • Timeouts
  • Encoding
  • Reconnection
  • Modern neffos API client for Browsers, Nodejs* and Go

Learning neffos

Qick View

Server

import (
    // [...]
    "github.com/kataras/neffos"
    "github.com/kataras/neffos/gorilla"
)

func runServer() {
    events := make(neffos.Namespaces)
    events.On("/v1", "workday", func(ns *neffos.NSConn, msg neffos.Message) error {
        date := string(msg.Body)

        t, err := time.Parse("01-02-2006", date)
        if err != nil {
            if n := ns.Conn.Increment("tries"); n >= 3 && n%3 == 0 {
                // Return custom error text to the client.
                return fmt.Errorf("Why not try this one? 06-24-2019")
            } else if n >= 6 && n%2 == 0 {
                // Fire the "notify" client event.
                ns.Emit("notify", []byte("What are you doing?"))
            }
            // Return the parse error back to the client.
            return err
        }

        weekday := t.Weekday()

        if weekday == time.Saturday || weekday == time.Sunday {
            return neffos.Reply([]byte("day off"))
        }

        // Reply back to the client.
        responseText := fmt.Sprintf("it's %s, do your job.", weekday)
        return neffos.Reply([]byte(responseText))
    })

    websocketServer := neffos.New(gorilla.DefaultUpgrader, events)

    // Fire the "/v1:notify" event to all clients after server's 1 minute.
    time.AfterFunc(1*time.Minute, func() {
        websocketServer.Broadcast(nil, neffos.Message{
            Namespace: "/v1",
            Event:     "notify",
            Body:      []byte("server is up and running for 1 minute"),
        })
    })

    router := http.NewServeMux()
    router.Handle("/", websocketServer)

    log.Println("Serving websockets on localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", router))
}

Go Client

func runClient() {
    ctx := context.TODO()
    events := make(neffos.Namespaces)
    events.On("/v1", "notify", func(c *neffos.NSConn, msg neffos.Message) error {
        log.Printf("Server says: %s\n", string(msg.Body))
        return nil
    })

    // Connect to the server.
    client, err := neffos.Dial(ctx,
        gorilla.DefaultDialer,
        "ws://localhost:8080",
        events)
    if err != nil {
        panic(err)
    }

    // Connect to a namespace.
    c, err := client.Connect(ctx, "/v1")
    if err != nil {
        panic(err)
    }

    fmt.Println("Please specify a date of format: mm-dd-yyyy")

    for {
        fmt.Print(">> ")
        var date string
        fmt.Scanf("%s", &date)

        // Send to the server and wait reply to this message.
        response, err := c.Ask(ctx, "workday", []byte(date))
        if err != nil {
            if neffos.IsCloseError(err) {
                // Check if the error is a close signal,
                // or make use of the `<- client.NotifyClose`
                // read-only channel instead.
                break
            }

            // >> 13-29-2019
            // error received: parsing time "13-29-2019": month out of range
            fmt.Printf("error received: %v\n", err)
            continue
        }

        // >> 06-29-2019
        // it's a day off!
        //
        // >> 06-24-2019
        // it's Monday, do your job.
        fmt.Println(string(response.Body))
    }
}

Javascript Client

Navigate to: https://github.com/kataras/neffos.js

Neffos contains extensive and thorough wiki making it easy to get started with the framework.

For a more detailed technical documentation you can head over to our godocs. And for executable code you can always visit the _examples repository's subdirectory.

Do you like to read while traveling?

You can request a PDF version of the E-Book today and be participated in the development of neffos.

https://iris-go.com/images/neffos-book-overview.png

Contributing

We'd love to see your contribution to the neffos real-time framework! For more information about contributing to the neffos project please check the CONTRIBUTING.md file.

  • neffos-contrib github organisation for more programming languages support, please invite yourself.

Security Vulnerabilities

If you discover a security vulnerability within neffos, please send an e-mail to [email protected]. All security vulnerabilities will be promptly addressed.

License

The word "neffos" has a greek origin and it is translated to "cloud" in English dictionary.

This project is licensed under the MIT license.

Issues
  • [Question\Websocket] How to send binary data with neffos(websocket)?

    [Question\Websocket] How to send binary data with neffos(websocket)?

    #387 I have read this issue. But cannot found, websocket.Config

    Then i found that, Iris websocket library is now merged with the neffos real-time framework But i cann't find a correct way to send binary data with neffos.

    Is there any config for neffos to send binary instead of string?

    opened by StartAt24 13
  • Problem with Broadcasting

    Problem with Broadcasting

    @kataras I have Issue with

    server.Broadcast(nil,neffos.Message{
    To: userID,
    Namespace: variable.Agent,
    Event:     "notif",
    Body:      neffos.Marshal(nf),	
    })
    

    i use gobwas.DefaultUpgrader

    when i use server.Broadcast in loop it is work but But not very good. You can test yourself. i open multiple browser tap with same userId(socketId)

    server.brodacast not send all message in loop;

    you can see cron example .

    c.write(neffos.Message{
    To: userID,
    Namespace: variable.Agent,
    Event:     "notif",
    Body:      neffos.Marshal(nf),	
    })
    
    

    write() mthod work very very good but it send message to one tap (last) with same userId .

    I'm going to send it to all the tabs. with same userId

    opened by majidbigdeli 12
  • Problem with CORS and websockets

    Problem with CORS and websockets

    Describe the bug I have an issue with cors, when i run it the example in 2 diferents ports (3000, 3001) and i try to conect a new socket from 3001 to 3000 then the console throw the next error: WebSocket connection to 'ws://localhost:3001/echo?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU' failed: Error during WebSocket handshake: Unexpected response code: 403.

    To Reproduce Use the example server.go on the route: _examples/websocket/basic/

    package main
    
    import (
    	"log"
    
    	"github.com/kataras/iris"
    	"github.com/kataras/iris/websocket"
    
    	// Used when "enableJWT" constant is true:
    	"github.com/iris-contrib/middleware/jwt"
    )
    
    // values should match with the client sides as well.
    const enableJWT = true
    const namespace = "default"
    
    // if namespace is empty then simply websocket.Events{...} can be used instead.
    var serverEvents = websocket.Namespaces{
    	namespace: websocket.Events{
    		websocket.OnNamespaceConnected: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    			// with `websocket.GetContext` you can retrieve the Iris' `Context`.
    			ctx := websocket.GetContext(nsConn.Conn)
    
    			log.Printf("[%s] connected to namespace [%s] with IP [%s]",
    				nsConn, msg.Namespace,
    				ctx.RemoteAddr())
    			return nil
    		},
    		websocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    			log.Printf("[%s] disconnected from namespace [%s]", nsConn, msg.Namespace)
    			return nil
    		},
    		"chat": func(nsConn *websocket.NSConn, msg websocket.Message) error {
    			// room.String() returns -> NSConn.String() returns -> Conn.String() returns -> Conn.ID()
    			log.Printf("[%s] sent: %s", nsConn, string(msg.Body))
    
    			// Write message back to the client message owner with:
    			// nsConn.Emit("chat", msg)
    			// Write message to all except this client with:
    			nsConn.Conn.Server().Broadcast(nsConn, msg)
    			return nil
    		},
    	},
    }
    
    func main() {
    	app := iris.New()
    	websocketServer := websocket.New(
    		websocket.DefaultGorillaUpgrader, /* DefaultGobwasUpgrader can be used too. */
    		serverEvents)
    
    	j := jwt.New(jwt.Config{
    		// Extract by the "token" url,
    		// so the client should dial with ws://localhost:8080/echo?token=$token
    		Extractor: jwt.FromParameter("token"),
    
    		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
    			return []byte("My Secret"), nil
    		},
    
    		// When set, the middleware verifies that tokens are signed
    		// with the specific signing algorithm
    		// If the signing method is not constant the
    		// `Config.ValidationKeyGetter` callback field can be used
    		// to implement additional checks
    		// Important to avoid security issues described here:
    		// https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
    		SigningMethod: jwt.SigningMethodHS256,
    	})
    
    	idGen := func(ctx iris.Context) string {
    		if username := ctx.GetHeader("X-Username"); username != "" {
    			return username
    		}
    
    		return websocket.DefaultIDGenerator(ctx)
    	}
    
    	// serves the endpoint of ws://localhost:8080/echo
    	// with optional custom ID generator.
    	websocketRoute := app.Get("/echo", websocket.Handler(websocketServer, idGen))
    
    	if enableJWT {
    		// Register the jwt middleware (on handshake):
    		websocketRoute.Use(j.Serve)
    		// OR
    		//
    		// Check for token through the jwt middleware
    		// on websocket connection or on any event:
    		/* websocketServer.OnConnect = func(c *websocket.Conn) error {
    		ctx := websocket.GetContext(c)
    		if err := j.CheckJWT(ctx); err != nil {
    			// will send the above error on the client
    			// and will not allow it to connect to the websocket server at all.
    			return err
    		}
    
    		user := ctx.Values().Get("jwt").(*jwt.Token)
    		// or just: user := j.Get(ctx)
    
    		log.Printf("This is an authenticated request\n")
    		log.Printf("Claim content:")
    		log.Printf("%#+v\n", user.Claims)
    
    		log.Printf("[%s] connected to the server", c.ID())
    
    		return nil
    		} */
    	}
    
    	// serves the browser-based websocket client.
    	app.Get("/", func(ctx iris.Context) {
    		ctx.ServeFile("./browser/index.html", false)
    	})
    
    	// serves the npm browser websocket client usage example.
    	app.HandleDir("/browserify", "./browserify")
    
    	app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
    }
    

    Expected behavior I would like to make petitions from the 2 servers

    Posible fix To catch this i made a middleware function wich delete the origin header (but this will be a rellay bad practice), code above:

            app.Use(func(ctx iris.Context) {
    		ctx.Request().Header.Del("Origin")
    		ctx.Next()
    	})
    
    opened by xXLokerXx 9
  • [BUG] the neffos server hang up whole goroutines

    [BUG] the neffos server hang up whole goroutines

    Describe the bug After upgrade lastest version, it still happened on productions. Debugging by dlv tool, I found the neffos block whole process.

    Screenshots 4011585729190_ pic_hd 4021585729215_ pic_hd

    Desktop (please complete the following information):

    • OS: Centos
    • Version 7.7

    Could you have time to handle this questions?

    bug pending-submitter-response 
    opened by LeeEirc 7
  • [BUG] the process hang up, CPU become 100%

    [BUG] the process hang up, CPU become 100%

    Describe the bug

    I use neffos as WebSocket server to handler all WebSocket connects from browser. It is running well when first deploy the web Application on production server. However it became weird at some moment. The CPU became 100% usage, and the server could not handle any incoming WebSocket connection. Debugging by dlv tool, I found the neffos conn's goroutine block main process. I don't know how reproduce this bug, but it happened several times on productions environment.

    To Reproduce

    Expected behavior I hope how to avoid this situation or this is the bug.

    Screenshots image

    image

    Desktop (please complete the following information):

    • OS: Centos
    • Version 7
    opened by LeeEirc 3
  •   在v12版本中,neffos的websocket在前后端分离的情况下提示403 In v12 version, the websocket of neffos prompts 403 when the front and back ends are separated

    在v12版本中,neffos的websocket在前后端分离的情况下提示403 In v12 version, the websocket of neffos prompts 403 when the front and back ends are separated

    WebSocket connection to 'ws://127.0.0.1:8888/echo?X-Websocket-Header-X-Username=yhm' failed: Error during WebSocket handshake: Unexpected response code: 403

    opened by mualala 3
  • Can you provide more programming language examples

    Can you provide more programming language examples

    I just use example in https://github.com/kataras/iris/blob/master/_examples/websocket/basic/server.go

    After run this server, I cannot access from python script.

    The Python Script:

    from websocket import create_connection
    ws = create_connection("ws://127.0.0.1:8080/echo?X-Websocket-Header-X-Username=ok&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU")
    print("Sending 'Hello, World'...")
    ws.send("Hello, World")
    print("Sent")
    print("Receiving...")
    result =  ws.recv()
    print("Received '%s'" % result)
    ws.close()
    

    And the result shows that script can establish a connection, but when the client begins to receive , it shows that "WebSocketConnectionClosedException: Connection is already closed."

    This problem also happens when I serve "./broswer/index.html" at another port using another web quick server (the index.html file has been modified so that the websocket port is still the same as server-side, that is, 8080.), it shows that

    neffos.min.js:1 WebSocket connection to 'ws://0.0.0.0:8080/echo?X-Websocket-Header-X-Username=jhi&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU' failed: Error during WebSocket handshake: Unexpected response code: 403
    

    So what should I do? And can you help me with a python websocket client script? Because I will use python script to listen and handle the websocket broadcasting message. Thank you.

    enhancement 
    opened by BuGTEa 3
  • [BUG]Binary data contains

    [BUG]Binary data contains ";" will be droped!

    Sorry to bother you. But i have found the bug. It is in neffos.js.

    I'm using protobuf with neffos.js. The data that protobuf make counld contain ;. So that my message will be drop in neffos.js in here.

      var isArrayBuffer = data instanceof ArrayBuffer;
        var dts;
    
        console.log(isArrayBuffer)
    
        if (isArrayBuffer) {
            var arr = new Uint8Array(data);
            var sepCount = 1;
            var lastSepIndex = 0;
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] == messageSeparatorCharCode) { // sep char.
                    sepCount++;
                    lastSepIndex = i;
                }
            }
           // Drop here!!!!!!!!!!!!
            if (sepCount != validMessageSepCount) {
                msg.isInvalid = true;
                console.log("return at validMessageSepCount")
                return msg;
            }
            dts = splitN(textDecoder.decode(arr.slice(0, lastSepIndex)), messageSeparator, validMessageSepCount - 2);
            dts.push(data.slice(lastSepIndex + 1, data.length));
            msg.SetBinary = true;
        }
        else {
            dts = splitN(data, messageSeparator, validMessageSepCount - 1);
        }
    

    I thank it is a bug. Cannot just split message by ;.

    Here is the reproducible code.

    Server

    package main
    
    import (
    	"github.com/kataras/iris"
    	"fmt"
    	"github.com/kataras/iris/websocket"
    	"github.com/kataras/neffos"
    	"time"
    )
    
    // 全局变量
    var page = struct {
        Title string
    }{"Collector"}
    
    func main(){
    	app := iris.New()
    
    	ws_server := startWebSocketServer(app)
    
    	go pub_thread(ws_server)
    
    	app.RegisterView(iris.HTML("./static", ".html"))
    	app.Get("/" ,func(ctx iris.Context){
    		ctx.ViewData("Page", page)
    		ctx.View("index.html")
    	})
    
    	app.HandleDir("/", "./static")	
        app.Run(iris.Addr(":5000"), iris.WithoutPathCorrection)
    }
    
    var serverEvents = websocket.Namespaces{
    	"default": websocket.Events{
    		websocket.OnNamespaceConnected: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    			// with `websocket.GetContext` you can retrieve the Iris' `Context`.
    			ctx := websocket.GetContext(nsConn.Conn)
    
    			fmt.Printf("[%s] connected to namespace [%s] with IP [%s]\n",
    				nsConn, msg.Namespace,
    				ctx.RemoteAddr())
    			return nil
    		},
    		websocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    			fmt.Printf("[%s] disconnected from namespace [%s]\n", nsConn, msg.Namespace)
    			return nil
    		},
    		"stream": func(nsConn *websocket.NSConn, msg websocket.Message) error {
    			// room.String() returns -> NSConn.String() returns -> Conn.String() returns -> Conn.ID()
    			fmt.Printf("[%s] sent: %s", nsConn, string(msg.Body))
    
    			// Write message back to the client message owner with:
    			// nsConn.Emit("chat", msg)
    			// Write message to all except this client with:
    			nsConn.Conn.Server().Broadcast(nsConn, msg)
    			return nil
    		},
    	},
    }
    
    func startWebSocketServer(app *iris.Application) *neffos.Server{
    	server := websocket.New(websocket.DefaultGorillaUpgrader, serverEvents)
    	server.OnConnect = func(c *websocket.Conn) error {
    		fmt.Printf("[%s] connected to the server.\n", c)
    		
    		return nil
    	}
    
    	server.OnDisconnect = func(c *websocket.Conn){
    		fmt.Printf("[%s] disconnected from the server.", c)
    	}
    
    	fmt.Printf("Listening on: %d\nPress CTRL/CMD+C to interrupt.\n", 5000)
    
    	idGen := func(ctx iris.Context) string {
    		if username := ctx.GetHeader("X-Username"); username != "" {
    			return username
    		}
    
    		return websocket.DefaultIDGenerator(ctx)
    	}
    
    	app.Get("/stream", websocket.Handler(server, idGen))
    
    	return server
    }
    
    func pub_thread(serve *neffos.Server){
    	png:= [...] byte{';',';',';',';',';'}
    	slice := png[:]
    	for{
    		serve.Broadcast(nil, neffos.Message{SetBinary: true,  Body:slice, Namespace: "default"})
    		time.Sleep(1*time.Second)
    	}
    }
    

    Client

    <html>
        <button> useless button</button>
    
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/neffos.js"></script>
        <script>
        
            var scheme = document.location.protocol == "https:" ? "wss" : "ws";
            var port = document.location.port ? ":" + document.location.port : "";
            var wsURL = scheme + "://" + document.location.hostname + port + "/stream";
            
            function handleError(reason) {
                console.log(reason);
            }
            
            function handleNamespaceConnectedConn(nsConn) {
    
            }
            // const username = window.prompt("Your username?");
            async function runExample() {
                // You can omit the "default" and simply define only Events, the namespace will be an empty string"",
                // however if you decide to make any changes on this example make sure the changes are reflecting inside the ../server.go file as well.
                try {
                    const conn = await neffos.dial(wsURL, {
                        default: { // "default" namespace.
                            _OnNamespaceConnected: function (nsConn, msg) {
                                handleNamespaceConnectedConn(nsConn)
                            },
                            _OnNamespaceDisconnect: function (nsConn, msg) {
                            },
                            stream: function (nsConn, msg) { // "stream" event.
                                console.log(msg.Body);
                                console.log(msg)
                            }
                        }
                    },{
                        headers: {
                            "X-Username": "",
                        }
                    });
                    // You can either wait to conenct or just conn.connect("connect")
                    // and put the `handleNamespaceConnectedConn` inside `_OnNamespaceConnected` callback instead.
                    // const nsConn = await conn.connect("default");
                    // nsConn.emit(...); handleNamespaceConnectedConn(nsConn);
                    conn.connect("default");
                } catch (err) {
                    handleError(err);
                }
            }
    
            runExample()
        </script>
    </html>
    
    bug 
    opened by StartAt24 3
  • Is neffos.Conn.Socket().WriteText() Atomic?

    Is neffos.Conn.Socket().WriteText() Atomic?

    If I start several goroutines calling neffos.Conn.Socket().WriteText(text, timeout) concurrently, would the texts be blended? Thanks for your attention!

    opened by Rossil2012 2
  • Hello, excuse me, I want to transfer my application to neffos. How does neffos send messages to websocket thread in web thread? Just like the socket.io-emitter package of node.js socketio

    Hello, excuse me, I want to transfer my application to neffos. How does neffos send messages to websocket thread in web thread? Just like the socket.io-emitter package of node.js socketio

    Hello, excuse me, I want to transfer my application to neffos. How does neffos send messages to websocket thread in web thread? Just like the socket.io-emitter package of node.js socketio

    opened by ezewu 2
  • [question] Why brower loop print re-connected and send head endpoint request?

    [question] Why brower loop print re-connected and send head endpoint request?

    I'm use neffos.js code :

    image

    But i'm find console loop print re-connected , time is i'm set reconnect , This is keeplive?

    image

    And find console print send HEAD methods to set endpoint , I'm set iris to Any but is not work

    image

    The Head request how to success?

    I'm client how to keeplive? if use reconnect , I'm iris log all reconnected log , is not good~

    thanks for help ~ @kataras

    opened by 23233 2
  • [BUG] basic in example, broswer message must be sent twice

    [BUG] basic in example, broswer message must be sent twice

    Describe the bug ① in app.js 45line toId is not defined! ②using example basic, broswer every send message need two times it worked, why it happend? server only recived An odd number of times message,the phenomena is every stable

    Note that if a bug is a browser-relative please report it at the neffos.js repository instead. Thanks! chrome and firefox

    To Reproduce Steps to reproduce the behavior: reproduce:https://github.com/kataras/neffos/tree/master/_examples/basic

    bug 
    opened by Iteravse 0
  • [BUG] Broadcast deadlocks when concurrency

    [BUG] Broadcast deadlocks when concurrency

    Describe the bug I found in the actual application that the Broadcast() method will cause a deadlock

    To Reproduce Steps to reproduce the behavior:

    1. When nsConn is disconnected, it will cause Server.Broadcast() internal blocking'' when broadcasting a message on the websocket.OnNamespaceDisconnect or websocket.OnRoomLeft event
    2. code
    package main
    
    import (
    	"fmt"
    	gorilla "github.com/gorilla/websocket"
    	"github.com/kataras/iris/v12"
    	"github.com/kataras/iris/v12/websocket"
    	"github.com/kataras/neffos"
    	"net/http"
    	"time"
    )
    
    func main() {
    	namespace, room, event := "test", "ttt", "ch"
    	chatServer := websocket.New(
    		websocket.GorillaUpgrader(gorilla.Upgrader{
    			ReadBufferSize:  1024,
    			WriteBufferSize: 1024,
    			// todo 允许所有的CORS 跨域请求,正式环境可以关闭
    			CheckOrigin: func(r *http.Request) bool {
    				return true
    			},
    		}),
    		websocket.WithTimeout{
    			WriteTimeout: time.Second * 60,
    			ReadTimeout:  time.Second * 60,
    			Namespaces: websocket.Namespaces{
    				namespace: websocket.Events{
    					websocket.OnNamespaceConnected: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    						fmt.Println("OnNamespaceConnected", nsConn.Conn.ID())
    						nsConn.JoinRoom(nil, room)
    						return nil
    					},
    					websocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    						fmt.Println("OnNamespaceDisconnect", nsConn.Conn.ID())
    						// Todo The broadcast here will cause blocking in `server.start()`
    						//nsConn.Conn.Server().Broadcast(nsConn, neffos.Message{
    						//	Body:      []byte("我离开了room" + nsConn.Conn.ID()),
    						//	Namespace: namespace,
    						//	Room:      room,
    						//	To:        "",
    						//	Event:     event,
    						//})
    
    						// Todo Add a certain delay and everything is normal
    						time.AfterFunc(time.Millisecond*50, func() {
    							nsConn.Conn.Server().Broadcast(nsConn, neffos.Message{
    								Body:      []byte("我离开了room" + nsConn.Conn.ID()),
    								Namespace: namespace,
    								Room:      room,
    								To:        "",
    								Event:     event,
    							})
    						})
    
    						return nil
    					},
    					websocket.OnRoomJoined: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    						fmt.Println("OnRoomJoined", nsConn.Conn.ID())
    						nsConn.Emit(event, []byte("我是单独消息"))
    						nsConn.Conn.Server().Broadcast(nil, neffos.Message{
    							Body:      []byte("我加入了room" + nsConn.Conn.ID()),
    							Namespace: namespace,
    							Room:      room,
    							To:        "",
    							Event:     event,
    						})
    
    						return nil
    					},
    					websocket.OnRoomLeft: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    						fmt.Println("OnRoomLeft", nsConn.Conn.ID(), nsConn.Conn.IsClosed())
    						// Todo The broadcast here will cause blocking in `server.start()`
    						//nsConn.Conn.Server().Broadcast(nsConn, neffos.Message{
    						//	Body:      []byte("我离开了room" + nsConn.Conn.ID()),
    						//	Namespace: namespace,
    						//	Room:      room,
    						//	To:        "",
    						//	Event:     event,
    						//})
    
    						// Todo Add a certain delay and everything is normal
    						time.AfterFunc(time.Millisecond*50, func() {
    							nsConn.Conn.Server().Broadcast(nsConn, neffos.Message{
    								Body:      []byte("我离开了room" + nsConn.Conn.ID()),
    								Namespace: namespace,
    								Room:      room,
    								To:        "",
    								Event:     event,
    							})
    						})
    						return nil
    					},
    				},
    			},
    		},
    	)
    
    	app := iris.New()
    	app.Get("/e", websocket.Handler(chatServer, func(ctx iris.Context) string {
    		return ctx.URLParam("id")
    	}))
    
    	// Todo If you don't want to lose messages, please turn it on
    	chatServer.SyncBroadcaster = true
    
    	app.Listen("0.0.0.0:8090")
    }
    
    
    
    bug 
    opened by AlanTianx 2
  • [BUG] Socket  missed a lot of msgs

    [BUG] Socket missed a lot of msgs

    Describe the bug neffos.Conn.Socket().ReadData(0) There is a case of missing messages, for example, I send 1-10, but it only receives 2,3

    To Reproduce

    package main
    
    import (
    	"fmt"
    	websocket2 "github.com/gorilla/websocket"
    	"github.com/kataras/iris/v12"
    	"github.com/kataras/iris/v12/context"
    	"github.com/kataras/iris/v12/websocket"
    	"github.com/kataras/neffos"
    	"github.com/kataras/neffos/gorilla"
    	uuid "github.com/satori/go.uuid"
    	"log"
    	"net/http"
    	"time"
    )
    
    var ws *neffos.Server
    
    func main() {
    	upgrade := websocket2.Upgrader{
    		// 允许跨域
    		CheckOrigin: func(r *http.Request) bool {
    			return true
    		},
    	}
    
    	ws = neffos.New(gorilla.Upgrader(upgrade), neffos.Namespaces{})
    	app := iris.New()
    	app.Get("/websocket_endpoint", Hello)
    	log.Fatal(app.Run(iris.Addr(":9999")))
    }
    
    // Hello
    func Hello(ctx iris.Context) {
    	conn := websocket.Upgrade(ctx, func(ctx context.Context) string {
    		return uuid.NewV4().String()
    	}, ws)
    	go production(conn)
    	go consumer(conn)
    }
    
    // production 生产
    func production(conn *neffos.Conn) {
    	for {
    		time.Sleep(1 * time.Second)
    		if err := conn.Socket().WriteText([]byte("111"), 0); err != nil {
    			if conn.IsClosed() {
    				return
    			}
    			fmt.Println("err: " + err.Error())
    		}
    	}
    }
    
    // consumer 消费 TODO 存在漏接消息的情况
    func consumer(conn *neffos.Conn) {
    	for {
    		data, _, err := conn.Socket().ReadData(0)
    		if err != nil {
    			if conn.IsClosed() {
    				return
    			}
    			fmt.Println("consumer err: " + err.Error())
    			continue
    		}
    		fmt.Println(fmt.Sprintf("consumer data: %v", string(data)))
    	}
    }
    
    

    Expected behavior

    Screenshots I missed a lot of msgs

    bug 
    opened by shixiaofeia 2
  • [BUG] The Message.FromStackExchange field do not perform as expected when using StackExchange

    [BUG] The Message.FromStackExchange field do not perform as expected when using StackExchange

    In my case fromStackExchange is always false:

    const waitComesFromStackExchange = '!'
    var wait = "1620719461266218800"
    fmt.Printf("waitComesFromStackExchange=%s wait=%s\n", string(waitComesFromStackExchange), wait)
    
    //wait[0]: type = uint8
    //waitComesFromStackExchange: type = int32
    //string(wait[0]+waitComesFromStackExchange) = 'R'
    
    //await := genWaitStackExchange(wait) // R620719461266218800
    fmt.Printf("genWaitStackExchange(wait) = %s\n", string(wait[0]+waitComesFromStackExchange)+wait[1:])
    
    //fromStackExchange = false
    var await = "R620719461266218800"
    fromStackExchange := len(await) > 2 && await[1] == waitComesFromStackExchange
    fmt.Printf("fromStackExchange = %v\n", fromStackExchange)
    

    https://github.com/kataras/neffos/blob/2221a9afc8392b9a3e984473dd34f8380ce80840/message.go#L237-L246 https://github.com/kataras/neffos/blob/2221a9afc8392b9a3e984473dd34f8380ce80840/message.go#L349-L375 https://github.com/kataras/neffos/blob/2221a9afc8392b9a3e984473dd34f8380ce80840/conn.go#L428-L432

    bug 
    opened by bigBron 0
  • [BUG] redis.NewStackExchange got radis err: NOAUTH Authentication required.

    [BUG] redis.NewStackExchange got radis err: NOAUTH Authentication required.

    I can confirm that the configuration is correct and the same configuration can connect to the cluster using go-redis. @kataras can you add a go-redis dependency?

    Locate where the error occurred: https://github.com/kataras/neffos/blob/2221a9afc8392b9a3e984473dd34f8380ce80840/stackexchange/redis/stackexchange_redis.go#L106-L111

    ref: https://github.com/mediocregopher/radix/blob/edd71ccab1b4dadde5b6e422e7af086230c0cf54/cluster.go#L203-L208

    bug 
    opened by bigBron 0
Releases(v0.0.19)
  • v0.0.18(Nov 17, 2020)

  • v0.0.16(May 18, 2020)

    Add 'Server.FireDisconnectAlways' option to enable calling the local OnDisconnectevent if the server closed the connection from itsOnConnect` event, as requested at: #41

    Source code(tar.gz)
    Source code(zip)
  • v0.0.15(Apr 28, 2020)

  • v0.0.14(Feb 11, 2020)

  • v0.0.12(Dec 10, 2019)

  • v0.0.11(Dec 10, 2019)

    • fix https://github.com/kataras/neffos/issues/20
    • give more respect to the Message.SetBinary
    • set Message.SetBinary for incoming messages using the operation code that the lower level protocol gave us

    Relative: https://github.com/kataras/neffos.js/commit/b5a916ec41193f320d5bcd276598e2e5ecfc795c

    Source code(tar.gz)
    Source code(zip)
  • v0.0.10(Aug 10, 2019)

    Contains two fixes

    1. Fix Server.Broadcast call inside Server.OnConnect callback when Server.UseStackExchange is used.
    2. Add Server.SyncBroadcaster which, if set to true, changes the default behavior and sync Server.Broadcast calls when Server.StackExchange is nil (or Server.UseStackExchange is not used). When StackExchange is used then this field is ignored, published messages were correctly handled by Redis or Nats already.
    Source code(tar.gz)
    Source code(zip)
  • v0.0.9(Aug 3, 2019)

  • v0.0.8(Jul 19, 2019)

    • Add On helper method on Events and Namespaces.
    • Make NSConn.JoinRoom and LeaveRoom to work synchronously inside event message handlers/callbacks.
    • Exports the Subject prefix of the stackexchange/nats.
    Source code(tar.gz)
    Source code(zip)
  • v0.0.7(Jul 14, 2019)

    • Add support for scaling-out using Nats with the stackexchange/nats subpackage, among with Redis from previous version.
    • Add support to register many StackExchanges.
    • Implement a Server.Ask which will block until/wait for a response from a specific client if msg.To is filled or from the first responder from either the cloud(if StackExchange is used) or from this server instance's connected clients, ofc you should still use the Conn.Ask if you know the client to ask from.
    • Update gobwas/ws dependency.

    Commits log:

    • https://github.com/kataras/neffos/compare/v0.0.6...v0.0.7
    Source code(tar.gz)
    Source code(zip)
  • v0.0.6(Jul 11, 2019)

    Add support for scaling-out with 3 lines of code. A new interface type of StackExchange is created and it's used on server and server connections if registered through Server.StackExchange field. A built-in redis StackExchange using a lightweight library is available for use.

    Example:

    • https://github.com/kataras/neffos/tree/master/_examples/redis
    import "github.com/kataras/neffos/stackexchange/redis"
    
    // [server := neffos.New...]
    
    exc, err := redis.NewStackExchange(redis.Config{}, "MyChatApp")
    if err != nil {
    	// [...]
    }
    server.StackExchange = exc
    

    Critical Commits:

    • https://github.com/kataras/neffos/commit/4de9b6608fc914beceeb7eedbd2ab1680635b094

    Request by:

    • https://github.com/kataras/neffos/issues/3
    Source code(tar.gz)
    Source code(zip)
  • v0.0.5(Jul 9, 2019)

    Export the ability of custom ConnHandlers and a lot of improvements that allow external packages to interact with the (NewStruct) feature, an example of this is the Iris' new v11.2+ fully featured MVC websocket controller.

    Minimum Go Installed required version is set to 1.12.

    Critical commits:

    • https://github.com/kataras/neffos/commit/26005b6ec445554c1155d1373a3605e23ad16c55
    • https://github.com/kataras/iris/commit/ab321a602639c7fcfde51bed7315cfbf8cdbb112
    Source code(tar.gz)
    Source code(zip)
  • v0.0.4(Jul 7, 2019)

    Improvements of the neffos.NewStruct connection handler.

    • With the new EnableDebug(nil_or_custom_printer) the neffos.NewStruct will log which field is marked as static, which method is handling a specific event and etc.
    • A new method Events() neffos.Events is added to the return value of the neffos.NewStruct in order to handle that struct as raw neffos.Events - this adds support to wrap one or more "controllers" on a neffos.Namespaces or neffos.WithTimeout connection handler. Note that when Events() is used then its SetNamespace and SetTimeouts have no effect because they should be handled by the parent level instead.

    Example Code:

    var events = neffos.Namespaces{
        "ns1": neffos.NewStruct(new(myWebsocketController)).Events(),
        "ns2": neffos.Events{...},
    }
    

    Have fun!

    Source code(tar.gz)
    Source code(zip)
  • v0.0.3(Jul 5, 2019)

    Add a neffos.NewStruct to convert struct's methods to events. Read more at: https://github.com/kataras/neffos/wiki/Getting-started#using-a-struct-value

    And add a customID string input argument to the neffos.Upgrade which iris is using to make it compatible. The neffos users can use the whole Server as a http.Handler through its ServeHTTP method ofc.

    Source code(tar.gz)
    Source code(zip)
  • v0.0.2(Jun 30, 2019)

Owner
Gerasimos (Makis) Maropoulos
🥇 That Greek Gopher | 💨 Senior Backend Engineer at PNOĒ | 🎓My dream is to create an international IT university that will produce flawless developers!
Gerasimos (Makis) Maropoulos
Fast and Scalable RPC Framework

Rony (Fast and Scalable RPC Framework) About Rony lets you create a clustered aware service easily. Checkout Wiki Performance Rony is very fast and wi

Ronak Software Group 40 Jun 13, 2022
Go-random-chat - Fast and scalable real-time random chat written in go

Go Random Chat Fast and scalable real-time random chat written in go. Features:

Hao-Ming, Hsu 7 Jun 12, 2022
High-performance, non-blocking, event-driven, easy-to-use networking framework written in Go, support tls/http1.x/websocket.

High-performance, non-blocking, event-driven, easy-to-use networking framework written in Go, support tls/http1.x/websocket.

lesismal 697 Jun 21, 2022
Simple, fast and scalable golang rpc library for high load

gorpc Simple, fast and scalable golang RPC library for high load and microservices. Gorpc provides the following features useful for highly loaded pro

Aliaksandr Valialkin 651 Jun 20, 2022
🖥️ Fast, modern and cross-platform SSH client

??️ Fast, modern and cross-platform SSH client Installation Build from source Requirements for building the project from source: Node 16 Go >= 1.17 Wa

Kamil Marut 2 Mar 20, 2022
:vulcan_salute: Fast, modern, easy-to-use network scanner

sx is the command-line network scanner designed to follow the UNIX philosophy. The goal of this project is to create the fastest network scanner with

null 937 Jun 10, 2022
PlanB: a HTTP and websocket proxy backed by Redis and inspired by Hipache.

PlanB: a distributed HTTP and websocket proxy What Is It? PlanB is a HTTP and websocket proxy backed by Redis and inspired by Hipache. It aims to be f

vinay badhan 1 Mar 20, 2022
Minimal and idiomatic WebSocket library for Go

websocket websocket is a minimal and idiomatic WebSocket library for Go. Install go get nhooyr.io/websocket Highlights Minimal and idiomatic API First

Anmol Sethi 2.3k Jun 24, 2022
Scalable WebRTC Signaling Server with ayame-like protocol.

ayu ayu is WebRTC Signaling Server with ayame-like protocol. Scalable: ayu uses Redis to store room states, so it can be used on serverless platforms

castaneai 23 Jun 3, 2022
Go-Web-Dev - Golang helps the developer to develop highly scalable applications

Go-Web-Dev Golang helps the developer to develop highly scalable applications. T

Mohamed Dhik 0 Feb 5, 2022
sonarbyte is a simple and fast subdomain scanner written in go to extract subdomain from Rapid7's DNS Database using omnisint's api.

sonarbyte Description Sonarbyte is a simple and fast subdomain scanner written in go to extract subdomains from Rapid7's DNS Database using omnisint's

Chan Nyein Wai 24 Mar 31, 2022
webrpc is a schema-driven approach to writing backend services for modern Web apps and networks

webrpc is a schema-driven approach to writing backend servers for the Web. Write your server's api interface in a schema format of RIDL or JSON, and t

null 453 Jun 20, 2022
Simple, secure and modern Go HTTP server to serve static sites, single-page applications or a file with ease

srv srv is a simple, secure and modern HTTP server, written in Go, to serve static sites, single-page applications or a file with ease. You can use it

Kevin Pollet 55 May 23, 2022
TCPProbe is a modern TCP tool and service for network performance observability.

TCPProbe is a modern TCP tool and service for network performance observability. It exposes information about socket’s underlying TCP session, TLS and HTTP (more than 60 metrics). you can run it through command line or as a service. the request is highly customizable and you can integrate it with your application through gRPC. it runs in a Kubernetes cluster as cloud native application and by adding annotations on pods allow a fine control of the probing process.

Mehrdad Arshad Rad 322 May 11, 2022
gobetween - modern & minimalistic load balancer and reverse-proxy for the ☁️ Cloud era.

gobetween - modern & minimalistic load balancer and reverse-proxy for the ☁️ Cloud era. Current status: Maintenance mode, accepting PRs. Currently in

Yaroslav Pogrebnyak 1.7k Jun 27, 2022
Simple and modern beanstalkd library for Golang

go-jackd package main import "github.com/getjackd/go-jackd" func main() {

null 5 Feb 16, 2022
go websocket, a better way to buid your IM server

Your star is my power!! ?? ⭐ ⭐ ⭐ ⭐ ⭐ Discribe lhttp is a http like protocol using websocket to provide long live, build your IM service quickly scalab

中弈 672 Jun 22, 2022
开箱即用的基于命令的消息处理框架,让 websocket 和 tcp 开发就像 http 那样简单

Cmd Srv 开箱即用的基于命令的消息处理框架,让 websocket 和 tcp 开发就像 http 那样简单

Eyas 19 Apr 13, 2022
HTTP, HTTP2, HTTPS, Websocket debugging proxy

English | 简体中文 We recommend updating whistle and Node to ensure that you receive important features, bugfixes and performance improvements. Some versi

avenwu 10.6k Jun 26, 2022