gnet基本使用
gnet基本使用
写在前面
工欲善其事必先利其器,阅读一个项目源码的前提是使用它,其实最好的阅读项目源码的方式是在带着目的和问题去阅读,而目的和问题往往是你在使用这个项目/工具时候积累下来的,或许是好奇它的实现方式,或者是在使用的过程中尝试解决它的一个bug。总之阅读源码的前提是了解它解决了什么问题,它的设计,它的使用,有一个全貌的认识之后再进入源代码里去窥探细节,加深理解。在gnet 介绍
一文中我们介绍了gnet
是用来干什么的,解决了什么问题,它的架构设计,本文介绍的则是gnet
的基本使用方法和一些案例,如何使用gnet
构建一个最基本的server
,为后面的源码阅读做准备。
I/O事件
gnet
是基于事件驱动的网络库,因此有一系列IO callback function
,这些IO
回调方法在不同的IO
事件发生之后被自动调用:
OnInitComplete
当server
初始化完成之后自动调用OnOpened
当连接被打开的时候自动调用Onclosed
当连接被关闭的时候自动调用OnDelete
当主动摘除连接的时候自动调用React
当server
端接收到从client
端发送来的数据的时候自动调用(核心业务代码一般都是写在这个回调方法里面的)Tick
当server
启动的时候会自动调用一次,之后就以给定的时间间隔定时调用一次,是一个定时器方法PreWrite
预先写数据,在server
端写回数据到client
之前被调用
gnet
目前支持上述的I/O
事件,对这些有了了解之后就可以基于这些I/O
事件来构建网络应用了。所以对开发者来说只要一个server struct
实现了这些回调方法,gnet
会负责在特定的时候调用server struct
的特定方法,直接拿gnet
源码给的example
代码作为示例来演示一下如何使用。
echo server 将客户端发送的数据原样响应给客户端
package main import ( "flag" "fmt" "log" "github.com/panjf2000/gnet" ) type echoServer struct { *gnet.EventServer } // OnInitComplete 实现server初始化完成之后打印日志 func (es *echoServer) OnInitComplete(srv gnet.Server) (action gnet.Action) { log.Printf("Echo server is listening on %s (multi-cores: %t, loops: %d)\n", srv.Addr.String(), srv.Multicore, srv.NumEventLoop) return } // React实现当server端接收到client端发送来的数据的时候直接原样返回 func (es *echoServer) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) { // 直接将客户端发送来的frame 赋值给out即可原样输出给客户端 out = frame return } func main() { var port int var multicore bool // Example command: go run echo.go --port 9000 --multicore=true // 端口和multicore配置 flag.IntVar(&port, "port", 9000, "--port 9000") flag.BoolVar(&multicore, "multicore", false, "--multicore true") flag.Parse() echo := new(echoServer) // 启动gnet服务 log.Fatal(gnet.Serve(echo, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore)))
push server 原样响应和定时推送
package main import ( "flag" "fmt" "log" "sync" "time" "github.com/panjf2000/gnet" ) type pushServer struct { *gnet.EventServer tick time.Duration // sync map 用来存储连接到push server的所有连接 connectedSockets sync.Map } // OnInitComplete 实现server初始化完成之后打印日志 func (ps *pushServer) OnInitComplete(srv gnet.Server) (action gnet.Action) { log.Printf("Push server is listening on %s (multi-cores: %t, loops: %d), "+ "pushing data every %s ...\n", srv.Addr.String(), srv.Multicore, srv.NumEventLoop, ps.tick.String()) return } // OnOpened实现连接建立后打印日志 func (ps *pushServer) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) { log.Printf("Socket with addr: %s has been opened...\n", c.RemoteAddr().String()) // 存储下这个连接 ps.connectedSockets.Store(c.RemoteAddr().String(), c) return } //OnClosed实现连接关闭后打印日志 func (ps *pushServer) OnClosed(c gnet.Conn, err error) (action gnet.Action) { log.Printf("Socket with addr: %s is closing...\n", c.RemoteAddr().String()) // 从map中删除调这个连接 ps.connectedSockets.Delete(c.RemoteAddr().String()) return } //Tick实现定时推送消息给客户端 func (ps *pushServer) Tick() (delay time.Duration, action gnet.Action) { log.Println("It's time to push data to clients!!!") // sync map range 遍历,推送消息给所有连接到push server 的客户端 ps.connectedSockets.Range(func(key, value interface{}) bool { addr := key.(string) c := value.(gnet.Conn) // 从map中获取gnet.Conn // gnet.Conn.AsyncWrite 异步写回响应 c.AsyncWrite([]byte(fmt.Sprintf("heart beating to %s\n", addr))) return true }) delay = ps.tick return } // React实现当server端接收到client 端发送来的数据的时候直接原样返回 func (ps *pushServer) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) { out = frame return } func main() { var port int var multicore bool var interval time.Duration var ticker bool // Example command: go run push.go --port 9000 --tick 1s --multicore=true // 端口、multicore、定时间隔配置 flag.IntVar(&port, "port", 9000, "server port") flag.BoolVar(&multicore, "multicore", true, "multicore") flag.
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> <span style="font-size:14px;">本专刊是Go开源项目源码分析专栏,共 17 篇文章,挑选了Go 开源界知名的 4 个开源项目gnet(高效的网络库)、gin(知名的Go微型web框架)、fasthttp(高性能web框架)、nsq(Go消息队列)来对它们进行源码分析,分析它们的设计思想和代码实现。每个项目的讲解都是由浅入深,由设计思想的剖析到源码实现的分析,更易于读者理解。</span> </p> <p> <br /> </p> <h2> <b><span style="font-size:16px;line-height:1;">购买须知:</span></b> </h2> <span style="font-size:14px;">订阅成功后,用户即可通过牛客网 PC 端、App 端享有永久阅读的权限;</span><br /> <span style="font-size:14px;">牛客专刊为虚拟内容服务,订阅成功后概不退款;</span><br /> <span style="font-size:14px;line-height:1;">在专刊阅</span><span style="font-size:14px;line-height:1;">读过程中,如有任何问题,可在文章评论区底部留言,或添加牛客导师,加入读者交流群;</span><br /> <span style="font-size:14px;">想成为牛客作者,请邮件联系yinxiaoxiao@nowcoder.com,邮件主题【牛客作者+写作方向】,并附上个人简历一份及近期作品一份;</span><br /> <p> <span style="font-size:14px;">牛客专刊版权归本平台所有,任何机构、媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布 / 发表,违者将依法追究责任</span><span style="font-size:14px;">。</span> </p> <p> <br /> </p>