gnet基本使用

gnet基本使用

写在前面

工欲善其事必先利其器,阅读一个项目源码的前提是使用它,其实最好的阅读项目源码的方式是在带着目的和问题去阅读,而目的和问题往往是你在使用这个项目/工具时候积累下来的,或许是好奇它的实现方式,或者是在使用的过程中尝试解决它的一个bug。总之阅读源码的前提是了解它解决了什么问题,它的设计,它的使用,有一个全貌的认识之后再进入源代码里去窥探细节,加深理解。在gnet 介绍一文中我们介绍了gnet是用来干什么的,解决了什么问题,它的架构设计,本文介绍的则是gnet 的基本使用方法和一些案例,如何使用gnet构建一个最基本的server,为后面的源码阅读做准备。

I/O事件

gnet 是基于事件驱动的网络库,因此有一系列IO callback function ,这些IO 回调方法在不同的IO 事件发生之后被自动调用:

  • OnInitCompleteserver 初始化完成之后自动调用
  • OnOpened当连接被打开的时候自动调用
  • Onclosed 当连接被关闭的时候自动调用
  • OnDelete 当主动摘除连接的时候自动调用
  • Reactserver端接收到从client 端发送来的数据的时候自动调用(核心业务代码一般都是写在这个回调方法里面的)
  • Tickserver 启动的时候会自动调用一次,之后就以给定的时间间隔定时调用一次,是一个定时器方法
  • 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%内容,订阅专栏后可继续查看/也可单篇购买

go高薪必备:面试框架17讲 文章被收录于专栏

<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>

全部评论

相关推荐

秋国🐮🐴:拿到你简历编号然后让你知道世间险恶
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务