Giter VIP home page Giter VIP logo

socket.io's Introduction

socket.io for golang

Build Status GoDoc

Features

Socket.IO enables real-time bidirectional event-based communication. It consists of:

  • Support Socket.IO v4+ 🚀🚀🚀
  • a Golang server (this repository)
  • a Javascript client library for the browser (or a Node.js client)

Some implementations in other languages are also available:

Its main features are:

Reliability

Connections are established even in the presence of:

  • proxies and load balancers.
  • personal firewall and antivirus software.

For this purpose, it relies on Engine.IO for golang, which first establishes a long-polling connection, then tries to upgrade to better transports that are "tested" on the side, like WebSocket. Please see the Goals section for more information.

Auto-reconnection support

Unless instructed otherwise a disconnected client will try to reconnect forever, until the server is available again. Please see the available reconnection options here.

Disconnection detection

A heartbeat mechanism is implemented at the Engine.IO level, allowing both the server and the client to know when the other one is not responding anymore.

That functionality is achieved with timers set on both the server and the client, with timeout values (the pingInterval and pingTimeout parameters) shared during the connection handshake. Those timers require any subsequent client calls to be directed to the same server, hence the sticky-session requirement when using multiples nodes.

Binary support

Any serializable data structures can be emitted, including:

  • []byte and io.Reader

Simple and convenient API

Sample code:

import (
    "github.com/zishang520/socket.io/v2/socket"
)
io.On("connection", func(clients ...any) {
    client := clients[0].(*socket.Socket)
    client.Emit("request" /* … */)                       // emit an event to the socket
    io.Emit("broadcast" /* … */)                         // emit an event to all connected sockets
    client.On("reply", func(...any) { /* … */ }) // listen to the event
})

Multiplexing support

In order to create separation of concerns within your application (for example per module, or based on permissions), Socket.IO allows you to create several Namespaces, which will act as separate communication channels but will share the same underlying connection.

Room support

Within each Namespace, you can define arbitrary channels, called Rooms, that sockets can join and leave. You can then broadcast to any given room, reaching every socket that has joined it.

This is a useful feature to send notifications to a group of users, or to a given user connected on several devices for example.

Note: Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server (like ws://echo.websocket.org) either. Please see the protocol specification here.

How to use

The following example attaches socket.io to a plain engine.io *types.CreateServer listening on port 3000.

package main

import (
    "github.com/zishang520/engine.io/v2/types"
    "github.com/zishang520/engine.io/v2/utils"
    "github.com/zishang520/socket.io/v2/socket"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    httpServer := types.CreateServer(nil)
    io := socket.NewServer(httpServer, nil)
    io.On("connection", func(clients ...any) {
        client := clients[0].(*socket.Socket)
        client.On("event", func(datas ...any) {
        })
        client.On("disconnect", func(...any) {
        })
    })
    httpServer.Listen("127.0.0.1:3000", nil)

    exit := make(chan struct{})
    SignalC := make(chan os.Signal)

    signal.Notify(SignalC, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
    go func() {
        for s := range SignalC {
            switch s {
            case os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
                close(exit)
                return
            }
        }
    }()

    <-exit
    httpServer.Close(nil)
    os.Exit(0)
}

other: Use http.Handler interface

package main

import (
    "net/http"
    "os"
    "os/signal"
    "syscall"

    "github.com/zishang520/socket.io/v2/socket"
)

func main() {
    io := socket.NewServer(nil, nil)
    http.Handle("/socket.io/", io.ServeHandler(nil))
    go http.ListenAndServe(":3000", nil)

    io.On("connection", func(clients ...any) {
        client := clients[0].(*socket.Socket)
        client.On("event", func(datas ...any) {
        })
        client.On("disconnect", func(...any) {
        })
    })

    exit := make(chan struct{})
    SignalC := make(chan os.Signal)

    signal.Notify(SignalC, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
    go func() {
        for s := range SignalC {
            switch s {
            case os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
                close(exit)
                return
            }
        }
    }()

    <-exit
    io.Close(nil)
    os.Exit(0)
}

Documentation

Please see the documentation here.

Debug / logging

In order to see all the debug output, run your app with the environment variable DEBUG including the desired scope.

To see the output from all of Socket.IO's debugging scopes you can use:

DEBUG=socket.io*

Testing

make test

License

MIT

socket.io's People

Contributors

dependabot[bot] avatar goxiaoy avatar mutefire avatar zishang520 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

socket.io's Issues

怎么添加这个库为依赖

如题,刚开始用go不久,go挺奇怪的,go.mod里完全没模板的版本信息,也不知道版本信息存哪里~ 我看了这个项目的go.mod,发现模块名是github.com/zishang520/socket.io/v2
于是我就执行了

go get github.com/zishang520/socket.io/v2

安装确实是安装了,require里多了github.com/zishang520/socket.io/v2,问题是只多了这一条~ 我安装其他第三方依赖时,间接依赖都是会自动加上的,但只有这个项目,就只装了本身~ 是我命令输错了吗?

EmitwithAck never listens for the callback from the client so it always timed out

This is a snippet from my code,
I'm porting a socket service already written in nodejs to go...
and this emitWithAck never returns a response ...it times out.

yet, the client automatically acknowledge the message as soon as it gets it
(it's instantaneous btw)

io.Timeout(180*time.Second).EmitWithAck("this-rider-location", receivedMessage)(
				func(args []any, err error) {
					fmt.Print(err)
					if err == nil {
                                        fmt.Println("we got a response")
                                        }
}
                                        

Store context on sockets

I would like to be able to store context on a connected socket instance and retrieve it later.
Example of what it would look like:

type SocketContext struct {
	UserId string `json:"userId"`
}
io.On("connection", func(clients ...any) {
    client := clients[0].(*socket.Socket)
    client.SetContext(&SocketContext {UserId : "some-user-id"})
    client.On("reply", func(...any) {
        ctx := client.Context().(*SocketContext)
        userId := ctx.UserId    
    })
})

Does this project have Discord or Telegram group?

I am very interested in your socket.io project written in Golang.
Do you have any social media platforms where people can contribute or ask questions?

Maybe I could provide some examples instead of reading the documents from the Node.js version.

Session ID unknown

Interesting issue found when integrating the library,

2023-05-25T09:00:05.708Z	DEBUG	HTTPS request received:	{"method": "", "RemoteURI": "/notifications/?transport=polling&EIO=3&t=1685005205.6987593", "ip": "", "status": 200}
2023-05-25T09:00:05.708Z	DEBUG	web/server.go:82	BodyDumpMiddleware: /notifications/ transport=polling&EIO=3&t=1685005205.6987593 GET 121:0{"maxPayload":100000,"pingInterval":25000,"pingTimeout":20000,"sid":"NGbaL9GzPY47nQAAAAAAAAAA","upgrades":["websocket"]}
2023-05-25T09:00:05.708Z	DEBUG	pubsub/pubsub.go:72	socketIO client NGbaL9GzPY47nQAAAAAAAAAA connected
2023-05-25T09:00:05.712Z	DEBUG	web/server.go:118	wrapBroadcastServerAndLogStatus: /notifications/ status 400
2023-05-25T09:00:05.712Z	DEBUG	HTTPS request received:	{"method": "", "RemoteURI": "/notifications/?transport=polling&EIO=3&sid=NGbaL9GzPY47nQAAAAAAAAAA", "ip": "", "status": 400}
2023-05-25T09:00:05.712Z	DEBUG	web/server.go:82	BodyDumpMiddleware: /notifications/ transport=polling&EIO=3&sid=NGbaL9GzPY47nQAAAAAAAAAA POST {"code":1,"message":"Session ID unknown"}
2023-05-25T09:00:05.723Z	DEBUG	web/server.go:118	wrapBroadcastServerAndLogStatus: /notifications/ status 400
2023-05-25T09:00:05.723Z	DEBUG	HTTPS request received:	{"method": "", "RemoteURI": "/notifications/?transport=polling&EIO=3&sid=NGbaL9GzPY47nQAAAAAAAAAA&t=1685005205.711163", "ip": "", "status": 400}
2023-05-25T09:00:05.723Z	DEBUG	web/server.go:82	BodyDumpMiddleware: /notifications/ transport=polling&EIO=3&sid=NGbaL9GzPY47nQAAAAAAAAAA&t=1685005205.711163 GET {"code":1,"message":"Session ID unknown"}

Seems like the first request and second request sent the same sid. The first request returned 200 but the second returned Session ID unknown".

Please feel free to let me know if you have any ideas! Thanks! @zishang520

join not working

This code has an error

			strRoom := fmt.Sprintf("user_%d", 1234)
			client.Join(Room)

Any example ?

I'm trying to migrate from https://github.com/googollee/go-socket.io. But I'm currently using gin-gonic as http handler, and it would help a lot if there's base example how to use event and namespace, because in the readmemd it only use (...any) as parameter.

how to fetch /socket.io/socket.io.js ?

I use you example, it work for connecting and sending message, but when I try to get the JS client code, using url /socket.io/socket.io.js
I always get error 400 with message below. any example i can follow to get the client JS ?

{
"code": 0,
"message": "Transport unknown"
}

how to join you

Recently used socketio in my go project,I feel unfriend;
So it took me a lot of time to rewrite it using nodejs,
until I see your project ,I very interested in your project,
I hope to do a little bit of my little effort to advance your project, so that other people who have suffered the same way as me will avoid detours

How to get the clients or one of them

socketio.io.On("connect", socketio.SocketIOOnConnect)
func (socketio *SocketIO) SocketIOOnConnect(clients ...any) {
	client := clients[0].(*socket.Socket)
	client.On("disconnect", socketio.SocketIOOnDisconnect)
        client.Emit("report", data) // it can emit
	zaplog.Logger.Info("connect ", client.Id())
}

I want to client.Emit() in the other routing.

go func() {
		for socketio.running {
			select {
			case <-socketio.stopChan:
				socketio.running = false
			case msg := <-socketio.message:
				switch m := msg.(type) {
				case []models.Result:
					clients := socket.io.Engine().Clients()
					jsonData, err := json.Marshal(m)
					if err != nil {
						fmt.Println("JSON marshaling failed:", err)
						return
					}
					 for _, client := range clients.Values() {
					 	client.Emit("report", string(jsonData)) // can't emit
					 }
				}
			}

		}
	}()

client.Emit("report", string(jsonData)) can't do it. I find that the client.ID() is different.

性能问题

生产环境使用过不? 不知道每一万连接需要占用多少内存, 还有多 redis 是否支持

how to use middle in Use funtion, stop this downward bubbling or next

func socketioWithGin() {
	g := gin.Default()
	io := socket.NewServer(nil, nil)
        // use Use funciton or in the second parameter of the Of method
	io.Of("/iot", nil).Use(middle).On("connection", func(clients ...any) {
		client := clients[0].(*socket.Socket)
		client.On("ping", func(datas ...any) {
			log.Println("heart")
			client.Emit("pong", "pong")
		})
		client.On("disconnect", func(...any) {
			log.Println("disconnect")
		})
	})
	sock := io.ServeHandler(nil)
	g.GET("/socket.io/", gin.WrapH(sock))
	g.POST("/socket.io/", gin.WrapH(sock))
	g.Run(":5005")
}

func middle(client *socket.Socket, e func(*socket.ExtendedError)) {
	name, ok := client.Handshake().Query.Get("name")
	if !ok {
		// break, how to jump out of this logic
		e(socket.NewExtendedError("name err", nil))
		return
	}
	// next
	log.Println("name==:", name)
}

support for acknowledgements

support for socket.io's request-response "acknowledgements", which allows for an optional callback at the end of an event listeners parameters ( more at https://socket.io/docs/v4/emitting-events ). Unless there is already a existing api that this library uses which does something similar, in which case could I please be redirected to what i need to use for it.

如何实现回调功能

Listener func(...any)
没有返回参数
怎么实现如下的回调功能
`
server.On("some:event", func(msg string) string {
return msg //Sending ack with data in msg back to client, using "return statement"
})

`

Do you mind looking into Fiber adaptor for net/http interface to make this library work with fiber applications?

here's the link: https://docs.gofiber.io/api/middleware/adaptor/

`go

package main

import (
"fmt"
"os"

"github.com/danielAsaboro/controllers"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/zishang520/engine.io/types"
"github.com/zishang520/socket.io/socket"

)

type Message struct {
Name string json:"name"
Message string json:"message"
}

func main() {
app := fiber.New()
var opts *socket.ServerOptions = socket.DefaultServerOptions()
var _ = &socket.ServerOptions{
// cors: {
// origin: "http://localhost:8100",
// methods: ["GET", "POST"],
// credentials: true
// },
// transports: ['websocket', 'polling'],
// allowEIO3: true

}
_ = &types.Cors{
	Origin:      "*",
	Methods:     []string{"GET", "POST"},
	Credentials: false,
}

// opts.SetCors(corsOptions)

app.Use(cors.New())
fmt.Print("printing addresses")
fmt.Println(opts)

io := socket.NewServer(nil, opts)
// io.of
// http.

app.Use("/socket.io/", adaptor.HTTPHandler(io.ServeHandler(opts)))
// http.Handle("/socket.io/", io.ServeHandler(nil))
// go http.ListenAndServe(":3000", nil)

io.On("connection", func(clients ...any) {
	client := clients[0].(*socket.Socket)
	client.On("event", func(datas ...any) {
	})
	client.On("disconnect", func(...any) {
	})
})
app.Get("/", controllers.HomeRouteHandler)

app.Listen(":" + os.Getenv("PORT"))

`

printing opts gave me nil all through...

&{{ 0xc0001703f0 } { } }

请问socketio的reconnect机制到底啥玩意

socketio的版本太多,除了js的版本,大多数其他语言只支持特别老的版本,导致文档也乱七八糟~
我原本放弃websocket选择socketio,是觉得这东西功能比websocket更强大,有类似房间的机制,最重要的是有自动重连机制,可靠性更高。实际用了才发现全是坑~ 主要是自动重连机制文档语焉不详,我怎么知道重连了?重连有没有回调?socket.on("connection")的回调包含重连吗?如果包含,如何区别第一次连接还是重新连接?最近才发现个大坑,好像重连是客户端机制,服务端是没有的,所以服务端的emit可能都是不可靠的~ 于是我得加代码自己实现在断连期间服务端消息缓存。这就设计到这所谓的重连对于服务的到底是什么情况?
每次重连,服务的都获得一个新的连接?还是重用已有的socket?
目前我的使用场景其实感觉socketio已经不划算了,因为socketio关注的是客户端到客户端,而我这是客户端到服务端集群,然后服务端通过mq消息总线转发消息给集群里的其他服务端,然后再通过某个服务端发送给客户端,这导致基本都是客户端和服务端的交互。这和socketio的设计**差别有点大。但现在换也来不及了,只能凑合用用~
总之求教下socketio的reconnect机制在服务端的角度到底是个啥玩意

Cannot find a way to successfully broadcast

❓Broadcast

I see in the source code there is a method exposed for this. It just does not seem to work.

this just ends up working like a regular emit

client.Broadcast().To(ROOM).Emit("hi")

I also saw that adapter has a broadcast. However, I get nil errors with this one.

nil error

io.Sockets().Adapter().Broadcast(&parser.Packet{
    Type: parser.EVENT,
    Data: "hi",
}, &server.BroadcastOptions{
    Rooms: types.NewSet(ROOM),
})

The repository is awesome I have been enjoying it greatly 😎

Sets up socket middleware returns an error

This code returns an error:

        httpServer := types.CreateServer(nil)
 	io := socket.NewServer(httpServer, nil)
 	err = io.On("connection", func(clients ...any) {
 		client := clients[0].(*socket.Socket)
 		client.Use(func(anies []any, f func(error)) {
 			spew.Dump(anies)
 			f(nil)
 		})
 	})
 	if err != nil {
 		fmt.Println("err on conn", err)
 	}

Error text : panic: runtime error: index out of range [1] with length 1
go/pkg/mod/github.com/zishang520/[email protected]/socket/socket.go:517 +0x10d

I think the error is in this line https://github.com/zishang520/socket.io/blob/main/socket/socket.go#L524
if i >= length {
Should take length - 1
if i >= length - 1 {

Otherwise, we will get a call to a non-existent i: https://github.com/zishang520/socket.io/blob/main/socket/socket.go#L529

Good job

This looks good so far :) Keep it up and have a nice day!

Golang client

Hello,

For my project, I need to connect as a client with my Golang app. Is it possible to add a golang client implementation ?

Thank you !

please add example list ,for example "gin", or export a socketio serverMux

	g := gin.Default()
	router := http.NewServeMux()
	io := socket.NewServer(nil, nil)
	io.Of("/user", nil).On("connection", func(clients ...any) {
		log.Println("connect")
		client := clients[0].(*socket.Socket)
		client.On("ping", func(datas ...any) {
			log.Println("heart")
			client.Emit("pong", "pong")
		})
		client.On("disconnect", func(...any) {
			log.Println("disconnect")
		})
	})
	router.Handle("/", io.ServeHandler(nil))

	g.GET("/socket.io/", gin.WrapH(router))
	g.POST("/socket.io/", gin.WrapH(router)) // it work
	// g.GET("/socket.io/", gin.WrapH(io.ServeHandler(nil))) it can not work
	// g.POST("/socket.io/", gin.WrapH(io.ServeHandler(nil)))
	// because A new router must be created for gin to use
	g.Run()

Benchmarking

Could you share some benchmarks and testing numbers if you have them please? I am considering using this in production and want to know if it is battle tested and production ready. Thanks!

How to call join in the EventListenser?

Hi,

I'm considering using this go lib to replace the old one.
Currently, in the code

// 'join' event handler,
// The 'join' workflow is as follows,
// 1. FE client got ack from server. Client side `connected` event is triggered.
// 2. In client connect event handler,
// socket.on('connect', () => {
// 	socket.emit('join', jwtToken);
// });
// 3. this event handler is triggered, and the connection is joined to a room called `email`
broadcastServer.OnEvent("", "join", func(s socketio.Conn, user string) {
	s.join(user)
})

How will this usage be supported by socket.io?

Thanks,

Support for gofiber.

Is there a working example for using this library with gofiber?

I tried this:

import (
	"github.com/gofiber/adaptor/v2"
	"github.com/zishang520/socket.io/socket"
)

// ...
io := socket.NewServer(nil, nil)
App.Use("/socket.io", adaptor.HTTPHandler(io.ServeHandler(nil)))

and socket.io can not connect to the websocket. (it resorts to using polling)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.