← Back to list

go-socket.io 源码分析

go-socket.io 的源码很简单,读起来其实不费力(因为API很少)

先自顶向下看看我们启动一个 socket io server 都经过了哪些流程。

先看看 server 启动前调用的 api👀

import (
    "net/http"
	engineio "github.com/googollee/go-engine.io"
)

// Server is a go-socket.io server.
type Server struct {
	handlers map[string]*namespaceHandler
	eio      *engineio.Server
}

// NewServer returns a server.
func NewServer(c *engineio.Options) (*Server, error) {
	eio, err := engineio.NewServer(c)
	if err != nil {
		return nil, err
	}
	return &Server{
		handlers: make(map[string]*namespaceHandler),
		eio:      eio,
	}, nil
}

NewServer 首先调用 go-engine.io 中的 NewServer, 将得到的 eio 赋值给了 Server{},并返回该 Server 的指针。(这才是代码少真正的原因😂,很多实现都是在 go-engine.io 库里)

这里我们仅关注 Server 中的 handlers,毕竟 eio 是另一个库的。

handlers 的数据结构为 map[string]*namespaceHandler

type namespaceHandler struct {
	onConnect    func(c Conn) error
	onDisconnect func(c Conn, msg string)
	onError      func(c Conn, err error)
	events       map[string]*funcHandler
	broadcast    Broadcast
}

上面是 namespaceHandler 的定义,这其实就是一个实现了 go-socketio 中各个方法的结构体。

那么 handlers 是用来干嘛的呢?

以 onConnect 为例子

// OnConnect set a handler function f to handle open event for
// namespace nsp.
func (s *Server) OnConnect(nsp string, f func(Conn) error) {
	h := s.getNamespace(nsp, true)
	h.OnConnect(f)
}

onConnect 先获取 namespace 然后执行 h.OnConnect()

func (s *Server) getNamespace(nsp string, create bool) *namespaceHandler {
	if nsp == "/" {
		nsp = ""
	}
	ret, ok := s.handlers[nsp]
	if ok {
		return ret
	}
	if create {
		handler := newHandler()
		s.handlers[nsp] = handler
		return handler
	} else {
		return nil
	}
}

getNamespace 接受两个参数 nsp 和 create, 当 nsp 为 “/” 会被重写为 ““。然后判 handlers 该 nsp 是否存在,存在就直接返回。再判断是否 create,如果为 true 则创建一个 handler 再将 nsp 和改 handler 加入 handlers 中。否则返回 nil。

func newHandler() *namespaceHandler {
	return &namespaceHandler{
		events:    make(map[string]*funcHandler),
		broadcast: NewBroadcast(),
	}
}

func (h *namespaceHandler) OnDisconnect(f func(Conn, string)) {
	h.onDisconnect = f
}

从上面看出 newHandler() 实际上就是创建一个 namespaceHandler 。而 OnDisconnect 就是将 namespaceHandler 复制给 namespaceHandler 中的 onDisconnect 字段。

func (h *namespaceHandler) OnDisconnect(f func(Conn, string)) {
	h.onDisconnect = f
}

func (h *namespaceHandler) OnError(f func(Conn, error)) {
	h.onError = f
}

OnDisconnect 和 OnError 与 onConnect 是同样的实现方法。

以上我们已经了解了 handlers 的作用之一 :储存 nsp 和 与之对应的 func

OnEvent 与其他三个的方法相同,只是要存储许多 events 所以 namespaceHandler 中 events 字段采用了 map[string]*funchandler 的数据结构。

type namespaceHandler struct {
	---
	events       map[string]*funcHandler
    ---
}

func (h *namespaceHandler) OnEvent(event string, f interface{}) {
	h.events[event] = newEventFunc(f)
}

创建 events 时,将 event 作为key, 相应的 func 作为 value 插入 map 中。

newEventFunc 函数位于 handler.go 文件中,具体实现如下:

func newEventFunc(f interface{}) *funcHandler {
	fv := reflect.ValueOf(f)
    // 判断该 reflect 的类型是否为 func
	if fv.Kind() != reflect.Func {
		panic("event handler must be a func.")
	}
	ft := fv.Type()
    // 判断参数的个数是否小于 1, 和参数的类型是否为 Coon
	if ft.NumIn() < 1 || ft.In(0).Name() != "Conn" {
		panic("handler function should be like func(socketio.Conn, ...)")
	}
	argTypes := make([]reflect.Type, ft.NumIn()-1)
	for i := range argTypes {
		argTypes[i] = ft.In(i + 1)
	}
	if len(argTypes) == 0 {
		argTypes = nil
	}
	return &funcHandler{
		argTypes: argTypes,
		f:        fv,
	}
}

以上就是 go-socket.io 启动前调用的 api 代码了,后面的下次一定😴。