parent
							
								
									c65fd5961b
								
							
						
					
					
						commit
						ebcbd0f88f
					
				| @ -0,0 +1,23 @@ | |||||||
|  | package nnet | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/connection" | ||||||
|  | 	"git.noahlan.cn/noahlan/ntools-go/core/nlog" | ||||||
|  | 	"net" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Dial 连接服务器
 | ||||||
|  | func (ngin *Engine) Dial(addr string) (*connection.Connection, error) { | ||||||
|  | 	err := ngin.setup() | ||||||
|  | 	if err != nil { | ||||||
|  | 		nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	conn, err := net.Dial("tcp", addr) | ||||||
|  | 	nlog.Must(err) | ||||||
|  | 
 | ||||||
|  | 	nlog.Infof("%s now connect to %s...", ngin.LogPrefix(), addr) | ||||||
|  | 
 | ||||||
|  | 	return ngin.handle(conn), nil | ||||||
|  | } | ||||||
| @ -1,286 +0,0 @@ | |||||||
| package core |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/config" |  | ||||||
| 	conn2 "git.noahlan.cn/noahlan/nnet/conn" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/entity" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/lifetime" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/packet" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/pipeline" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/scheduler" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/serialize" |  | ||||||
| 	"git.noahlan.cn/noahlan/ntools-go/core/nlog" |  | ||||||
| 	"git.noahlan.cn/noahlan/ntools-go/core/pool" |  | ||||||
| 	"github.com/gorilla/websocket" |  | ||||||
| 	"log" |  | ||||||
| 	"net" |  | ||||||
| 	"net/http" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func NotFound(conn entity.NetworkEntity, _ packet.IPacket) { |  | ||||||
| 	nlog.Error("handler not found") |  | ||||||
| 	_ = conn.SendBytes([]byte("handler not found")) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NotFoundHandler() Handler { |  | ||||||
| 	return HandlerFunc(NotFound) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type ( |  | ||||||
| 	// engine TCP-engine
 |  | ||||||
| 	engine struct { |  | ||||||
| 		conf               config.EngineConf // conf 配置
 |  | ||||||
| 		taskTimerPrecision time.Duration |  | ||||||
| 
 |  | ||||||
| 		middlewares []Middleware // 中间件
 |  | ||||||
| 		routes      []Route      // 路由
 |  | ||||||
| 		// handler 消息处理器
 |  | ||||||
| 		handler Handler |  | ||||||
| 		// dieChan 应用程序退出信号
 |  | ||||||
| 		dieChan chan struct{} |  | ||||||
| 
 |  | ||||||
| 		pipeline pipeline.Pipeline // 消息管道
 |  | ||||||
| 		lifetime *lifetime.Mgr     // 连接的生命周期管理器
 |  | ||||||
| 
 |  | ||||||
| 		packerFn   packet.NewPackerFunc // 封包、拆包器
 |  | ||||||
| 		serializer serialize.Serializer // 消息 序列化/反序列化
 |  | ||||||
| 
 |  | ||||||
| 		wsOpt wsOptions // websocket
 |  | ||||||
| 
 |  | ||||||
| 		connManager *conn2.Manager |  | ||||||
| 		sessIdMgr   *sessionIDMgr |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	wsOptions struct { |  | ||||||
| 		IsWebsocket    bool                     // 是否为websocket服务端
 |  | ||||||
| 		WebsocketPath  string                   // ws地址(ws://127.0.0.1/WebsocketPath)
 |  | ||||||
| 		TLSCertificate string                   // TLS 证书地址 (websocket)
 |  | ||||||
| 		TLSKey         string                   // TLS 证书key地址 (websocket)
 |  | ||||||
| 		CheckOrigin    func(*http.Request) bool // check origin
 |  | ||||||
| 	} |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func newEngine(conf config.EngineConf) *engine { |  | ||||||
| 	s := &engine{ |  | ||||||
| 		conf:               conf, |  | ||||||
| 		dieChan:            make(chan struct{}), |  | ||||||
| 		pipeline:           pipeline.New(), |  | ||||||
| 		middlewares:        make([]Middleware, 0), |  | ||||||
| 		routes:             make([]Route, 0), |  | ||||||
| 		taskTimerPrecision: conf.TaskTimerPrecision, |  | ||||||
| 		connManager:        conn2.NewManager(), |  | ||||||
| 		sessIdMgr:          newSessionIDMgr(), |  | ||||||
| 		lifetime:           lifetime.NewLifetime(), |  | ||||||
| 	} |  | ||||||
| 	pool.InitPool(conf.Pool) |  | ||||||
| 
 |  | ||||||
| 	return s |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) shallLogDebug() bool { |  | ||||||
| 	return config.ShallLogDebug(ng.conf.Mode) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) logPrefix() string { |  | ||||||
| 	return fmt.Sprintf("[NNet-%s]", ng.conf.Name) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) use(middleware ...Middleware) { |  | ||||||
| 	ng.middlewares = append(ng.middlewares, middleware...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) addRoutes(route ...Route) { |  | ||||||
| 	ng.routes = append(ng.routes, route...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) bindRoutes(router Router) error { |  | ||||||
| 	for _, fr := range ng.routes { |  | ||||||
| 		if err := ng.bindRoute(router, fr); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) bindRoute(router Router, route Route) error { |  | ||||||
| 	// TODO default middleware
 |  | ||||||
| 	chain := newChain() |  | ||||||
| 	// build chain
 |  | ||||||
| 	for _, middleware := range ng.middlewares { |  | ||||||
| 		chain.Append(convertMiddleware(middleware)) |  | ||||||
| 	} |  | ||||||
| 	return router.Register(route.Matches, route.Handler) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func convertMiddleware(ware Middleware) func(Handler) Handler { |  | ||||||
| 	return func(next Handler) Handler { |  | ||||||
| 		return ware(next.Handle) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) dial(addr string, router Router) (entity.NetworkEntity, error) { |  | ||||||
| 	ng.handler = router |  | ||||||
| 
 |  | ||||||
| 	if err := ng.bindRoutes(router); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	go scheduler.Schedule(ng.taskTimerPrecision) |  | ||||||
| 
 |  | ||||||
| 	// connection
 |  | ||||||
| 	conn, err := net.Dial("tcp", addr) |  | ||||||
| 	nlog.Must(err) |  | ||||||
| 
 |  | ||||||
| 	c := newConnection(ng, conn) |  | ||||||
| 	c.serve() |  | ||||||
| 	// hook
 |  | ||||||
| 	ng.lifetime.Open(c) |  | ||||||
| 	// connection manager
 |  | ||||||
| 	err = ng.connManager.Store(conn2.DefaultGroupName, c) |  | ||||||
| 	nlog.Must(err) |  | ||||||
| 
 |  | ||||||
| 	// 连接成功,客户端已启动
 |  | ||||||
| 	if ng.shallLogDebug() { |  | ||||||
| 		nlog.Debugf("now connect to %s.", addr) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return c, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) serve(router Router) error { |  | ||||||
| 	ng.handler = router |  | ||||||
| 
 |  | ||||||
| 	if err := ng.bindRoutes(router); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	go scheduler.Schedule(ng.taskTimerPrecision) |  | ||||||
| 	defer func() { |  | ||||||
| 		nlog.Infof("%s is stopping...", ng.logPrefix()) |  | ||||||
| 
 |  | ||||||
| 		ng.shutdown() |  | ||||||
| 		scheduler.Close() |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	if ng.wsOpt.IsWebsocket { |  | ||||||
| 		if len(ng.wsOpt.TLSCertificate) != 0 && len(ng.wsOpt.TLSKey) != 0 { |  | ||||||
| 			ng.listenAndServeWSTLS() |  | ||||||
| 		} else { |  | ||||||
| 			ng.listenAndServeWS() |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		ng.listenAndServe() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) close() { |  | ||||||
| 	close(ng.dieChan) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) shutdown() { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) listenAndServe() { |  | ||||||
| 	listener, err := net.Listen(ng.conf.Protocol, ng.conf.Addr) |  | ||||||
| 	nlog.Must(err) |  | ||||||
| 
 |  | ||||||
| 	// 监听成功,服务已启动
 |  | ||||||
| 	if ng.shallLogDebug() { |  | ||||||
| 		nlog.Debugf("%s now listening %s at %s.", ng.logPrefix(), ng.conf.Protocol, ng.conf.Addr) |  | ||||||
| 	} |  | ||||||
| 	defer func() { |  | ||||||
| 		_ = listener.Close() |  | ||||||
| 		ng.close() |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	for { |  | ||||||
| 		conn, err := listener.Accept() |  | ||||||
| 		if err != nil { |  | ||||||
| 			if errors.Is(err, net.ErrClosed) { |  | ||||||
| 				nlog.Errorf("%s 服务器网络错误 %+v", ng.logPrefix(), err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			nlog.Errorf("%s 监听错误 %v", ng.logPrefix(), err) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		err = pool.Submit(func() { |  | ||||||
| 			ng.handle(conn) |  | ||||||
| 		}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			nlog.Errorf("%s submit conn pool err: %ng", ng.logPrefix(), err.Error()) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) listenAndServeWS() { |  | ||||||
| 	ng.setupWS() |  | ||||||
| 	if ng.shallLogDebug() { |  | ||||||
| 		nlog.Debugf("%s now listening websocket at %s.", ng.logPrefix(), ng.conf.Addr) |  | ||||||
| 	} |  | ||||||
| 	if err := http.ListenAndServe(ng.conf.Addr, nil); err != nil { |  | ||||||
| 		log.Fatal(err.Error()) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) listenAndServeWSTLS() { |  | ||||||
| 	ng.setupWS() |  | ||||||
| 	if ng.shallLogDebug() { |  | ||||||
| 		nlog.Debugf("%s now listening websocket with tls at %s.", ng.logPrefix(), ng.conf.Addr) |  | ||||||
| 	} |  | ||||||
| 	if err := http.ListenAndServeTLS(ng.conf.Addr, ng.wsOpt.TLSCertificate, ng.wsOpt.TLSKey, nil); err != nil { |  | ||||||
| 		log.Fatal(err.Error()) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) setupWS() { |  | ||||||
| 	upgrade := websocket.Upgrader{ |  | ||||||
| 		ReadBufferSize:  1024, |  | ||||||
| 		WriteBufferSize: 1024, |  | ||||||
| 		CheckOrigin:     ng.wsOpt.CheckOrigin, |  | ||||||
| 	} |  | ||||||
| 	http.HandleFunc("/"+strings.TrimPrefix(ng.wsOpt.WebsocketPath, "/"), func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 		conn, err := upgrade.Upgrade(w, r, nil) |  | ||||||
| 		if err != nil { |  | ||||||
| 			nlog.Errorf("%s Upgrade failure, URI=%ng, Error=%ng", ng.logPrefix(), r.RequestURI, err.Error()) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		err = pool.Submit(func() { |  | ||||||
| 			ng.handleWS(conn) |  | ||||||
| 		}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatalf("%s submit conn pool err: %v", ng.logPrefix(), err.Error()) |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) handleWS(conn *websocket.Conn) { |  | ||||||
| 	c := newWSConn(conn) |  | ||||||
| 	ng.handle(c) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) handle(conn net.Conn) { |  | ||||||
| 	c := newConnection(ng, conn) |  | ||||||
| 	err := ng.connManager.Store(conn2.DefaultGroupName, c) |  | ||||||
| 	nlog.Must(err) |  | ||||||
| 
 |  | ||||||
| 	c.serve() |  | ||||||
| 	// hook
 |  | ||||||
| 	ng.lifetime.Open(c) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ng *engine) notFoundHandler(next Handler) Handler { |  | ||||||
| 	return HandlerFunc(func(entity entity.NetworkEntity, packet packet.IPacket) { |  | ||||||
| 		h := next |  | ||||||
| 		if next == nil { |  | ||||||
| 			h = NotFoundHandler() |  | ||||||
| 		} |  | ||||||
| 		// TODO write to client
 |  | ||||||
| 		h.Handle(entity, packet) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| package core |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/entity" |  | ||||||
| 	"git.noahlan.cn/noahlan/nnet/packet" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type ( |  | ||||||
| 	Handler interface { |  | ||||||
| 		Handle(entity entity.NetworkEntity, pkg packet.IPacket) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	HandlerFunc func(entity entity.NetworkEntity, pkg packet.IPacket) |  | ||||||
| 
 |  | ||||||
| 	Middleware func(next HandlerFunc) HandlerFunc |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func (f HandlerFunc) Handle(entity entity.NetworkEntity, pkg packet.IPacket) { |  | ||||||
| 	f(entity, pkg) |  | ||||||
| } |  | ||||||
| @ -0,0 +1,135 @@ | |||||||
|  | package nnet | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/config" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/connection" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/lifetime" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/packet" | ||||||
|  | 	rt "git.noahlan.cn/noahlan/nnet/router" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/scheduler" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/serialize" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/session" | ||||||
|  | 	"git.noahlan.cn/noahlan/ntools-go/core/nlog" | ||||||
|  | 	"github.com/panjf2000/ants/v2" | ||||||
|  | 	"math" | ||||||
|  | 	"net" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Engine 引擎
 | ||||||
|  | type Engine struct { | ||||||
|  | 	config.EngineConf                      // 引擎配置
 | ||||||
|  | 	middlewares       []rt.Middleware      // 中间件
 | ||||||
|  | 	routes            []rt.Route           // 路由
 | ||||||
|  | 	router            rt.Router            // 消息处理器
 | ||||||
|  | 	dieChan           chan struct{}        // 应用程序退出信号
 | ||||||
|  | 	pipeline          connection.Pipeline  // 消息管道
 | ||||||
|  | 	packerBuilder     packet.PackerBuilder // 封包、拆包器
 | ||||||
|  | 	serializer        serialize.Serializer // 消息 序列化/反序列化
 | ||||||
|  | 	goPool            *ants.Pool           // goroutine池
 | ||||||
|  | 	connManager       *connection.Manager  // 连接管理器
 | ||||||
|  | 	lifetime          *lifetime.Mgr        // 生命周期
 | ||||||
|  | 	sessIdMgr         *session.IDMgr       // SessionId管理器
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewEngine(conf config.EngineConf, opts ...RunOption) *Engine { | ||||||
|  | 	ngin := &Engine{ | ||||||
|  | 		EngineConf:    conf, | ||||||
|  | 		middlewares:   make([]rt.Middleware, 0), | ||||||
|  | 		routes:        make([]rt.Route, 0), | ||||||
|  | 		router:        rt.NewDefaultRouter(), | ||||||
|  | 		packerBuilder: nil, | ||||||
|  | 		serializer:    nil, | ||||||
|  | 		dieChan:       make(chan struct{}), | ||||||
|  | 		pipeline:      connection.NewPipeline(), | ||||||
|  | 		connManager:   connection.NewManager(), | ||||||
|  | 		lifetime:      lifetime.NewLifetime(), | ||||||
|  | 		sessIdMgr:     session.NewSessionIDMgr(), | ||||||
|  | 		goPool:        nil, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, opt := range opts { | ||||||
|  | 		opt(ngin) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if ngin.goPool == nil { | ||||||
|  | 		ngin.goPool, _ = ants.NewPool(math.MaxInt32) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ngin | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) Use(middleware ...rt.Middleware) { | ||||||
|  | 	ngin.middlewares = append(ngin.middlewares, middleware...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) AddRoutes(rs ...rt.Route) { | ||||||
|  | 	ngin.routes = append(ngin.routes, rs...) | ||||||
|  | 	err := ngin.bindRoutes() | ||||||
|  | 	nlog.Must(err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) bindRoutes() error { | ||||||
|  | 	for _, fr := range ngin.routes { | ||||||
|  | 		if err := ngin.bindRoute(fr); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) bindRoute(route rt.Route) error { | ||||||
|  | 	// TODO default middleware
 | ||||||
|  | 	chain := rt.NewChain() | ||||||
|  | 	// build chain
 | ||||||
|  | 	for _, middleware := range ngin.middlewares { | ||||||
|  | 		chain.Append(rt.ConvertMiddleware(middleware)) | ||||||
|  | 	} | ||||||
|  | 	return ngin.router.Register(route.Matches, route.Handler) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) setup() error { | ||||||
|  | 	if err := ngin.bindRoutes(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := ngin.goPool.Submit(func() { | ||||||
|  | 		scheduler.Schedule(ngin.TaskTimerPrecision) | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) Stop() { | ||||||
|  | 	nlog.Infof("%s is stopping...", ngin.LogPrefix()) | ||||||
|  | 	close(ngin.dieChan) | ||||||
|  | 	scheduler.Close() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) handle(conn net.Conn) *connection.Connection { | ||||||
|  | 	nc := connection.NewConnection( | ||||||
|  | 		ngin.sessIdMgr.SessionID(), | ||||||
|  | 		conn, | ||||||
|  | 		connection.Config{LogDebug: ngin.ShallLogDebug(), LogPrefix: ngin.LogPrefix()}, | ||||||
|  | 		ngin.packerBuilder, ngin.serializer, ngin.pipeline, | ||||||
|  | 		ngin.router.Handle, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	nc.Serve() | ||||||
|  | 
 | ||||||
|  | 	err := ngin.connManager.Store(connection.DefaultGroupName, nc) | ||||||
|  | 	nlog.Must(err) | ||||||
|  | 
 | ||||||
|  | 	// dieChan
 | ||||||
|  | 	go func() { | ||||||
|  | 		// lifetime
 | ||||||
|  | 		ngin.lifetime.Open(nc) | ||||||
|  | 
 | ||||||
|  | 		select { | ||||||
|  | 		case <-nc.DieChan(): | ||||||
|  | 			scheduler.PushTask(func() { ngin.lifetime.Close(nc) }) | ||||||
|  | 			_ = ngin.connManager.Remove(nc) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 
 | ||||||
|  | 	return nc | ||||||
|  | } | ||||||
| @ -1,26 +0,0 @@ | |||||||
| package entity |  | ||||||
| 
 |  | ||||||
| type Session interface { |  | ||||||
| 	// ID 获取 Session ID
 |  | ||||||
| 	ID() int64 |  | ||||||
| 	// UID 获取UID
 |  | ||||||
| 	UID() string |  | ||||||
| 	// Bind 绑定uid
 |  | ||||||
| 	Bind(uid string) |  | ||||||
| 	// Attribute 获取指定key对应参数
 |  | ||||||
| 	Attribute(key string) interface{} |  | ||||||
| 	// Keys 获取所有参数key
 |  | ||||||
| 	Keys() []string |  | ||||||
| 	// Exists 指定key是否存在
 |  | ||||||
| 	Exists(key string) bool |  | ||||||
| 	// Attributes 获取所有参数
 |  | ||||||
| 	Attributes() map[string]interface{} |  | ||||||
| 	// RemoveAttribute 移除指定key对应参数
 |  | ||||||
| 	RemoveAttribute(key string) |  | ||||||
| 	// SetAttribute 设置参数
 |  | ||||||
| 	SetAttribute(key string, value interface{}) |  | ||||||
| 	// Invalidate 清理
 |  | ||||||
| 	Invalidate() |  | ||||||
| 	// Close 关闭 Session
 |  | ||||||
| 	Close() |  | ||||||
| } |  | ||||||
| @ -0,0 +1,43 @@ | |||||||
|  | package modbus | ||||||
|  | 
 | ||||||
|  | import "sync" | ||||||
|  | 
 | ||||||
|  | var crcTable []uint16 | ||||||
|  | var mu sync.Mutex | ||||||
|  | 
 | ||||||
|  | // crcModbus 计算modbus的crc
 | ||||||
|  | func crcModbus(data []byte) (crc uint16) { | ||||||
|  | 	if crcTable == nil { | ||||||
|  | 		mu.Lock() | ||||||
|  | 		if crcTable == nil { | ||||||
|  | 			initCrcTable() | ||||||
|  | 		} | ||||||
|  | 		mu.Unlock() | ||||||
|  | 	} | ||||||
|  | 	crc = 0xffff | ||||||
|  | 	for _, v := range data { | ||||||
|  | 		crc = (crc >> 8) ^ crcTable[(crc^uint16(v))&0x00FF] | ||||||
|  | 	} | ||||||
|  | 	return crc | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // initCrcTable 初始化crcTable
 | ||||||
|  | func initCrcTable() { | ||||||
|  | 	crc16IBM := uint16(0xA001) | ||||||
|  | 	crcTable = make([]uint16, 256) | ||||||
|  | 
 | ||||||
|  | 	for i := uint16(0); i < 256; i++ { | ||||||
|  | 		crc := uint16(0) | ||||||
|  | 		c := i | ||||||
|  | 
 | ||||||
|  | 		for j := uint16(0); j < 8; j++ { | ||||||
|  | 			if ((crc ^ c) & 0x0001) > 0 { | ||||||
|  | 				crc = (crc >> 1) ^ crc16IBM | ||||||
|  | 			} else { | ||||||
|  | 				crc = crc >> 1 | ||||||
|  | 			} | ||||||
|  | 			c = c >> 1 | ||||||
|  | 		} | ||||||
|  | 		crcTable[i] = crc | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | package modbus | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/protocol/modbus/internal" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestCRC(t *testing.T) { | ||||||
|  | 	got := crcModbus([]byte{0x01, 0x04, 0x02, 0xFF, 0xFF}) | ||||||
|  | 	expect := uint16(0x80B8) | ||||||
|  | 
 | ||||||
|  | 	assert := internal.NewAssert(t, "TestCRC") | ||||||
|  | 	assert.Equal(expect, got) | ||||||
|  | } | ||||||
| @ -0,0 +1,167 @@ | |||||||
|  | package internal | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"runtime" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	compareNotEqual int = iota - 2 | ||||||
|  | 	compareLess | ||||||
|  | 	compareEqual | ||||||
|  | 	compareGreater | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Assert is a simple implementation of assertion, only for internal usage
 | ||||||
|  | type Assert struct { | ||||||
|  | 	T        *testing.T | ||||||
|  | 	CaseName string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewAssert return instance of Assert
 | ||||||
|  | func NewAssert(t *testing.T, caseName string) *Assert { | ||||||
|  | 	return &Assert{T: t, CaseName: caseName} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Equal check if expected is equal with actual
 | ||||||
|  | func (a *Assert) Equal(expected, actual any) { | ||||||
|  | 	if compare(expected, actual) != compareEqual { | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, expected, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NotEqual check if expected is not equal with actual
 | ||||||
|  | func (a *Assert) NotEqual(expected, actual any) { | ||||||
|  | 	if compare(expected, actual) == compareEqual { | ||||||
|  | 		expectedInfo := fmt.Sprintf("not %v", expected) | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, expectedInfo, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Greater check if expected is greate than actual
 | ||||||
|  | func (a *Assert) Greater(expected, actual any) { | ||||||
|  | 	if compare(expected, actual) != compareGreater { | ||||||
|  | 		expectedInfo := fmt.Sprintf("> %v", expected) | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, expectedInfo, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GreaterOrEqual check if expected is greate than or equal with actual
 | ||||||
|  | func (a *Assert) GreaterOrEqual(expected, actual any) { | ||||||
|  | 	isGreatOrEqual := compare(expected, actual) == compareGreater || compare(expected, actual) == compareEqual | ||||||
|  | 	if !isGreatOrEqual { | ||||||
|  | 		expectedInfo := fmt.Sprintf(">= %v", expected) | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, expectedInfo, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Less check if expected is less than actual
 | ||||||
|  | func (a *Assert) Less(expected, actual any) { | ||||||
|  | 	if compare(expected, actual) != compareLess { | ||||||
|  | 		expectedInfo := fmt.Sprintf("< %v", expected) | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, expectedInfo, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // LessOrEqual check if expected is less than or equal with actual
 | ||||||
|  | func (a *Assert) LessOrEqual(expected, actual any) { | ||||||
|  | 	isLessOrEqual := compare(expected, actual) == compareLess || compare(expected, actual) == compareEqual | ||||||
|  | 	if !isLessOrEqual { | ||||||
|  | 		expectedInfo := fmt.Sprintf("<= %v", expected) | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, expectedInfo, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsNil check if value is nil
 | ||||||
|  | func (a *Assert) IsNil(value any) { | ||||||
|  | 	if value != nil { | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, nil, value) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsNotNil check if value is not nil
 | ||||||
|  | func (a *Assert) IsNotNil(value any) { | ||||||
|  | 	if value == nil { | ||||||
|  | 		makeTestFailed(a.T, a.CaseName, "not nil", value) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // compare x and y return :
 | ||||||
|  | // x > y -> 1, x < y -> -1, x == y -> 0, x != y -> -2
 | ||||||
|  | func compare(x, y any) int { | ||||||
|  | 	vx := reflect.ValueOf(x) | ||||||
|  | 	vy := reflect.ValueOf(y) | ||||||
|  | 
 | ||||||
|  | 	if vx.Type() != vy.Type() { | ||||||
|  | 		return compareNotEqual | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch vx.Kind() { | ||||||
|  | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||||
|  | 		xInt := vx.Int() | ||||||
|  | 		yInt := vy.Int() | ||||||
|  | 		if xInt > yInt { | ||||||
|  | 			return compareGreater | ||||||
|  | 		} | ||||||
|  | 		if xInt == yInt { | ||||||
|  | 			return compareEqual | ||||||
|  | 		} | ||||||
|  | 		if xInt < yInt { | ||||||
|  | 			return compareLess | ||||||
|  | 		} | ||||||
|  | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||||||
|  | 		xUint := vx.Uint() | ||||||
|  | 		yUint := vy.Uint() | ||||||
|  | 		if xUint > yUint { | ||||||
|  | 			return compareGreater | ||||||
|  | 		} | ||||||
|  | 		if xUint == yUint { | ||||||
|  | 			return compareEqual | ||||||
|  | 		} | ||||||
|  | 		if xUint < yUint { | ||||||
|  | 			return compareLess | ||||||
|  | 		} | ||||||
|  | 	case reflect.Float32, reflect.Float64: | ||||||
|  | 		xFloat := vx.Float() | ||||||
|  | 		yFloat := vy.Float() | ||||||
|  | 		if xFloat > yFloat { | ||||||
|  | 			return compareGreater | ||||||
|  | 		} | ||||||
|  | 		if xFloat == yFloat { | ||||||
|  | 			return compareEqual | ||||||
|  | 		} | ||||||
|  | 		if xFloat < yFloat { | ||||||
|  | 			return compareLess | ||||||
|  | 		} | ||||||
|  | 	case reflect.String: | ||||||
|  | 		xString := vx.String() | ||||||
|  | 		yString := vy.String() | ||||||
|  | 		if xString > yString { | ||||||
|  | 			return compareGreater | ||||||
|  | 		} | ||||||
|  | 		if xString == yString { | ||||||
|  | 			return compareEqual | ||||||
|  | 		} | ||||||
|  | 		if xString < yString { | ||||||
|  | 			return compareLess | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		if reflect.DeepEqual(x, y) { | ||||||
|  | 			return compareEqual | ||||||
|  | 		} | ||||||
|  | 		return compareNotEqual | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return compareNotEqual | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // logFailedInfo make test failed and log error info
 | ||||||
|  | func makeTestFailed(t *testing.T, caseName string, expected, actual any) { | ||||||
|  | 	_, file, line, _ := runtime.Caller(2) | ||||||
|  | 	errInfo := fmt.Sprintf("Case %v failed. file: %v, line: %v, expected: %v, actual: %v.", caseName, file, line, expected, actual) | ||||||
|  | 	t.Error(errInfo) | ||||||
|  | 	t.FailNow() | ||||||
|  | } | ||||||
| @ -0,0 +1,50 @@ | |||||||
|  | package internal | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestAssert(t *testing.T) { | ||||||
|  | 	assert := NewAssert(t, "TestAssert") | ||||||
|  | 	assert.Equal(0, 0) | ||||||
|  | 	assert.NotEqual(1, 0) | ||||||
|  | 
 | ||||||
|  | 	assert.NotEqual("1", 1) | ||||||
|  | 	var uInt1 uint | ||||||
|  | 	var uInt2 uint | ||||||
|  | 	var uInt8 uint8 | ||||||
|  | 	var uInt16 uint16 | ||||||
|  | 	var uInt32 uint32 | ||||||
|  | 	var uInt64 uint64 | ||||||
|  | 	assert.NotEqual(uInt1, uInt8) | ||||||
|  | 	assert.NotEqual(uInt8, uInt16) | ||||||
|  | 	assert.NotEqual(uInt16, uInt32) | ||||||
|  | 	assert.NotEqual(uInt32, uInt64) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal(uInt1, uInt2) | ||||||
|  | 
 | ||||||
|  | 	uInt1 = 1 | ||||||
|  | 	uInt2 = 2 | ||||||
|  | 	assert.Less(uInt1, uInt2) | ||||||
|  | 
 | ||||||
|  | 	assert.Greater(1, 0) | ||||||
|  | 	assert.GreaterOrEqual(1, 1) | ||||||
|  | 	assert.Less(0, 1) | ||||||
|  | 	assert.LessOrEqual(0, 0) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal(0.1, 0.1) | ||||||
|  | 	assert.Greater(1.1, 0.1) | ||||||
|  | 	assert.Less(0.1, 1.1) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal("abc", "abc") | ||||||
|  | 	assert.NotEqual("abc", "abd") | ||||||
|  | 	assert.Less("abc", "abd") | ||||||
|  | 	assert.Greater("abd", "abc") | ||||||
|  | 
 | ||||||
|  | 	assert.Equal([]int{1, 2, 3}, []int{1, 2, 3}) | ||||||
|  | 	assert.NotEqual([]int{1, 2, 3}, []int{1, 2}) | ||||||
|  | 
 | ||||||
|  | 	assert.IsNil(nil) | ||||||
|  | 	assert.IsNotNil("abc") | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -1,15 +1,15 @@ | |||||||
| package plain | package plain | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"git.noahlan.cn/noahlan/nnet/core" | 	"git.noahlan.cn/noahlan/nnet" | ||||||
| 	"git.noahlan.cn/noahlan/nnet/packet" | 	"git.noahlan.cn/noahlan/nnet/packet" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func WithPlainProtocol() []core.RunOption { | func WithPlainProtocol() []nnet.RunOption { | ||||||
| 	opts := []core.RunOption{ | 	opts := []nnet.RunOption{ | ||||||
| 		withPipeline(), | 		withPipeline(), | ||||||
| 		core.WithRouter(NewRouter()), | 		nnet.WithRouter(NewRouter()), | ||||||
| 		core.WithPacker(func() packet.Packer { return NewPacker() }), | 		nnet.WithPackerBuilder(func() packet.Packer { return NewPacker() }), | ||||||
| 	} | 	} | ||||||
| 	return opts | 	return opts | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,30 @@ | |||||||
|  | package router | ||||||
|  | 
 | ||||||
|  | // ToMiddleware converts the given handler to a Middleware.
 | ||||||
|  | func ToMiddleware(handler func(next Handler) Handler) Middleware { | ||||||
|  | 	return func(next HandlerFunc) HandlerFunc { | ||||||
|  | 		return handler(next).Handle | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // WithMiddlewares adds given middlewares to given routes.
 | ||||||
|  | func WithMiddlewares(ms []Middleware, rs ...Route) []Route { | ||||||
|  | 	for i := len(ms) - 1; i >= 0; i-- { | ||||||
|  | 		rs = WithMiddleware(ms[i], rs...) | ||||||
|  | 	} | ||||||
|  | 	return rs | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // WithMiddleware adds given middleware to given route.
 | ||||||
|  | func WithMiddleware(middleware Middleware, rs ...Route) []Route { | ||||||
|  | 	routes := make([]Route, len(rs)) | ||||||
|  | 
 | ||||||
|  | 	for i := range rs { | ||||||
|  | 		route := rs[i] | ||||||
|  | 		routes[i] = Route{ | ||||||
|  | 			Matches: route.Matches, | ||||||
|  | 			Handler: middleware(route.Handler), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return routes | ||||||
|  | } | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | package router | ||||||
|  | 
 | ||||||
|  | func ConvertMiddleware(ware Middleware) func(Handler) Handler { | ||||||
|  | 	return func(next Handler) Handler { | ||||||
|  | 		return ware(next.Handle) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,46 @@ | |||||||
|  | package nnet | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/config" | ||||||
|  | 	"git.noahlan.cn/noahlan/ntools-go/core/nlog" | ||||||
|  | 	"net" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) ListenTCP(conf config.TCPServerConf) error { | ||||||
|  | 	err := ngin.setup() | ||||||
|  | 	if err != nil { | ||||||
|  | 		nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	listener, err := net.Listen(conf.Protocol, conf.Addr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		nlog.Errorf("%s failed to listening at [%s %s] %v", ngin.LogPrefix(), conf.Protocol, conf.Addr, err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	nlog.Infof("%s now listening %s at %s...", ngin.LogPrefix(), conf.Protocol, conf.Addr) | ||||||
|  | 	defer func() { | ||||||
|  | 		_ = listener.Close() | ||||||
|  | 		ngin.Stop() | ||||||
|  | 	}() | ||||||
|  | 	for { | ||||||
|  | 		conn, err := listener.Accept() | ||||||
|  | 		if err != nil { | ||||||
|  | 			if errors.Is(err, net.ErrClosed) { | ||||||
|  | 				nlog.Errorf("%s connection closed, err:%v", ngin.LogPrefix(), err) | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			nlog.Errorf("%s accept connection failed, err:%v", ngin.LogPrefix(), err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err = ngin.goPool.Submit(func() { | ||||||
|  | 			ngin.handle(conn) | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			nlog.Errorf("%s submit conn pool err: %ng", ngin.LogPrefix(), err.Error()) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | package nnet | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/config" | ||||||
|  | 	"git.noahlan.cn/noahlan/nnet/connection" | ||||||
|  | 	"git.noahlan.cn/noahlan/ntools-go/core/nlog" | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // ListenWebsocket 开始监听Websocket
 | ||||||
|  | func (ngin *Engine) ListenWebsocket(conf config.WSServerConf) error { | ||||||
|  | 	err := ngin.setup() | ||||||
|  | 	if err != nil { | ||||||
|  | 		nlog.Errorf("%s failed to setup server, err:%v", ngin.LogPrefix(), err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	nlog.Infof("%s now listening websocket at %s.", ngin.LogPrefix(), conf.Addr) | ||||||
|  | 	ngin.upgradeWebsocket(conf) | ||||||
|  | 	if conf.IsTLS() { | ||||||
|  | 		if err := http.ListenAndServeTLS(conf.Addr, conf.TLSCertificate, conf.TLSKey, nil); err != nil { | ||||||
|  | 			nlog.Errorf("%s failed to listening websocket with TLS at %s %v", ngin.LogPrefix(), conf.Addr, err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if err := http.ListenAndServe(conf.Addr, nil); err != nil { | ||||||
|  | 			nlog.Errorf("%s failed to listening websocket at %s %v", ngin.LogPrefix(), conf.Addr, err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) handleWS(conn *websocket.Conn) { | ||||||
|  | 	wsConn := connection.NewWSConn(conn) | ||||||
|  | 	ngin.handle(wsConn) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ngin *Engine) upgradeWebsocket(conf config.WSServerConf) { | ||||||
|  | 	upgrade := websocket.Upgrader{ | ||||||
|  | 		HandshakeTimeout:  conf.HandshakeTimeout, | ||||||
|  | 		ReadBufferSize:    conf.ReadBufferSize, | ||||||
|  | 		WriteBufferSize:   conf.WriteBufferSize, | ||||||
|  | 		CheckOrigin:       conf.CheckOrigin, | ||||||
|  | 		EnableCompression: conf.Compression, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	path := fmt.Sprintf("/%s", strings.TrimPrefix(conf.Path, "/")) | ||||||
|  | 	http.HandleFunc(path, func(writer http.ResponseWriter, request *http.Request) { | ||||||
|  | 		conn, err := upgrade.Upgrade(writer, request, nil) | ||||||
|  | 		if err != nil { | ||||||
|  | 			nlog.Errorf("%s Upgrade failure, URI=%ng, Error=%ng", ngin.LogPrefix(), request.RequestURI, err.Error()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		err = ngin.goPool.Submit(func() { | ||||||
|  | 			ngin.handleWS(conn) | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			nlog.Errorf("%s submit conn pool err: %v", ngin.LogPrefix(), err.Error()) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | package session | ||||||
|  | 
 | ||||||
|  | import "sync/atomic" | ||||||
|  | 
 | ||||||
|  | type IDMgr struct { | ||||||
|  | 	count int64 | ||||||
|  | 	sid   int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewSessionIDMgr() *IDMgr { | ||||||
|  | 	return &IDMgr{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Increment the connection count
 | ||||||
|  | func (c *IDMgr) Increment() { | ||||||
|  | 	atomic.AddInt64(&c.count, 1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Decrement the connection count
 | ||||||
|  | func (c *IDMgr) Decrement() { | ||||||
|  | 	atomic.AddInt64(&c.count, -1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Count returns the connection numbers in current
 | ||||||
|  | func (c *IDMgr) Count() int64 { | ||||||
|  | 	return atomic.LoadInt64(&c.count) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reset the connection service status
 | ||||||
|  | func (c *IDMgr) Reset() { | ||||||
|  | 	atomic.StoreInt64(&c.count, 0) | ||||||
|  | 	atomic.StoreInt64(&c.sid, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SessionID returns the session id
 | ||||||
|  | func (c *IDMgr) SessionID() int64 { | ||||||
|  | 	return atomic.AddInt64(&c.sid, 1) | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in New Issue