目录
- 前言
- 一、IRequest消息请求抽象类
- 1 - IRequest需求分析
- 2 - irequest.go
- 3 - request.go
- 二、router路由模块
- 1 - router模块分析
- 2 - irouter.go
- 3 - router.go
- 三、源码修改
- 四、Router模块总结
-
- 六、完整源码
- 1 - ziface接口
- 2 - znet实现
- 3 - 测试类
前言
- 在zinxV0.2中conn链接的业务处理HandleFunc是固定写死的,接下来我们需要自定义一个conn处理业务的接口
- 需要定义一些interface来让用户填写任意格式的链接处理业务方法
一、IRequest消息请求抽象类
1 - IRequest需求分析
- IRequest:把客户端请求的连接信息 和 请求的数据,放在一个叫Request的请求类里,这样的好处是我们可以从Request里得到全部客户端的请求信息;每次客户端的全部请求数据,Zinx都会把它们一起放到一个Request结构体里
2 - irequest.go
package ziface
type IRequest interface {
//得到当前链接
GetConnection() IConneciton
//得到请求的消息数据
GetData() []byte
}
3 - request.go
package znet
import (
"zinx/ziface"
)
type Request struct {
//已经和客户端建立好的链接
conn ziface.IConneciton
//客户端请求的数据
data []byte
}
//得到当前链接
func (r *Request) GetConnection() ziface.IConneciton {
return r.conn
}
//得到请求的消息数据
func (r *Request) GetData() []byte {
return r.data
}
二、router路由模块
1 - router模块分析
- router模块的作用:服务端应该给zinx框架配置当前链接的处理业务方法;之前conn链接的业务处理HandleFunc是固定写死的,现在是可以⾃定义,并且有3种接⼝可以重写(每个⽅法都有⼀个唯⼀的形参 IRequest 对象,也就是客户端请求过来的连接和请求数据,作为业务⽅法的输⼊数据)
- Handle :是处理当前链接的主业务函数
- PreHandle :如果需要在主业务函数之前有前置业务,可以重写这个⽅法
- PostHandle :如果需要在主业务函数之后⼜后置业务,可以重写这个⽅法
2 - irouter.go
package ziface
type IRouter interface {
//在处理conn业务之前的钩子方法Hook
PreHandle(request IRequest)
//在处理conn业务的主方法hook
Handle(request IRequest)
//在处理conn业务之后的钩子方法Hook
PostHandle(request IRequest)
}
3 - router.go
package znet
import "zinx/ziface"
//实现router时, 先嵌入这个BaseRouter基类, 然后根据需要对这个基类的方法进行重写就好了
type BaseRouter struct{}
//这里之所以BaseRouter的方法都为空
//是因为有的Router不希望有PreHandle、PostHandle这两个业务
//所以Router全部继承BaseRouter的好处就是, 不需要实现PreHandle,PostHandle
//在处理conn业务之前的钩子方法Hook
func (br *BaseRouter) PreHandle(request ziface.IRequest) {}
//在处理conn业务的主方法hook
func (br *BaseRouter) Handle(request ziface.IRequest) {}
//在处理conn业务之后的钩子方法Hook
func (br *BaseRouter) PostHandle(request ziface.IRequest) {}
三、源码修改
- 1-新增请求接口ziface/irequest.go
- 2-新增请求接口实现znet/request.go
- 3-新增路由接口ziface/irouter.go
- 4-新增路由接口实现znet/router.go
- 5-ziface/iserver.go增加抽象方法AddRouter:让zinx使用者可以自定义一个router处理业务方法
package ziface
//定义一个服务器接口
type IServer interface {
//启动服务器
Start()
//停止服务器
Stop()
//运行服务器
Serve()
//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
AddRouter(router IRouter)
}
- 6-znet/server.go实现抽象方法AddRouter
//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
func (s *Server) AddRouter(router ziface.IRouter) {
s.Router = router
fmt.Println("Add Router Succ!!")
}
- 7-znet/server.go添加router成员
//iServer的接口实现,定义一个Server的服务器模块
type Server struct {
//服务器的名称
Name string
//服务器绑定的ip版本
IPVersion string
//服务器监听的IP
IP string
//服务器监听的端口
Port int
//当前的Server添加一个router,server注册的链接对应的处理业务
Router ziface.IRouter
}
- 8-znet/connection.go添加router成员并修改初始化链接方法
type Connection struct {
//当前链接的socket TCP套接字
Conn *net.TCPConn
//链接的ID
ConnID uint32
//当前的链接状态
isClosed bool
//告知当前链接已经退出的/停止 channel
ExitChan chan bool
//该链接处理的方法Router
Router ziface.IRouter
}
//初始化链接模块的方法
func NewConnection(conn *net.TCPConn, connID uint32, router ziface.IRouter) *Connection {
c := &Connection{
Conn: conn,
ConnID: connID,
Router: router,
isClosed: false,
ExitChan: make(chan bool, 1),
}
return c
}
- 9-znet/connection.go的StartReader方法中:初始化一个request,开启goroutine调用路由业务
//链接的读业务方法
func (c *Connection) StartReader() {
fmt.Println(" Reader Goroutine is running...")
defer fmt.Println("connID = ", c.ConnID, " Reader is exit, remote addr is ", c.RemoteAddr().String())
defer c.Stop()
for {
//读取客户端的数据到buf中, 最大512字节
buf := make([]byte, 512)
_, err := c.Conn.Read(buf)
if err != nil {
fmt.Println("recv buf err", err)
continue
}
//得到当前conn数据的Request请求数据
req := Request{
conn: c,
data: buf,
}
//从路由中,找到注册绑定的Conn对应的router调用
//执行注册的路由方法
go func(request ziface.IRequest) {
c.Router.PreHandle(request)
c.Router.Handle(request)
c.Router.PostHandle(request)
}(&req)
}
}
- 10-修改测试类server.go:实现自定义的Router
package main
import (
"fmt"
"zinx/ziface"
"zinx/znet"
)
//ping test 自定义路由
type PingRouter struct {
znet.BaseRouter
}
//Test PreHandle
func (this *PingRouter) PreHandle(request ziface.IRequest) {
fmt.Println("Call Router PreHandle...")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("before ping...n"))
if err != nil {
fmt.Println("call back before ping error")
}
}
//Test Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
fmt.Println("Call Router Handle...")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("ping...ping...pingn"))
if err != nil {
fmt.Println("call back ping...ping...ping error")
}
}
//Test PostHandle
func (this *PingRouter) PostHandle(request ziface.IRequest) {
fmt.Println("Call Router PostHandle...")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("after pingn"))
if err != nil {
fmt.Println("call back after ping error")
}
}
func main() {
//1 创建一个server句柄,使用Zinx的api
s := znet.NewServer("[zinx V0.3]")
//2 给当前zinx框架添加一个自定义的router
s.AddRouter(&PingRouter{})
//3 启动server
s.Serve()
}
四、Router模块总结
1 - 总结xmind
2 - 项目结构
六、完整源码
1 - ziface接口
package ziface
//定义一个服务器接口
type IServer interface {
//启动服务器
Start()
//停止服务器
Stop()
//运行服务器
Serve()
//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
AddRouter(router IRouter)
}
package ziface
type IRouter interface {
//在处理conn业务之前的钩子方法Hook
PreHandle(request IRequest)
//在处理conn业务的主方法hook
Handle(request IRequest)
//在处理conn业务之后的钩子方法Hook
PostHandle(request IRequest)
}
package ziface
type IRequest interface {
//得到当前链接
GetConnection() IConneciton
//得到请求的消息数据
GetData() []byte
}
package ziface
import "net"
//定义链接模块的抽象层
type IConneciton interface {
//启动链接 让当前的链接准备开始工作
Start()
//停止链接 结束当前链接的工作
Stop()
//获取当前链接的绑定socket conn
GetTCPConnection() *net.TCPConn
//获取当前链接模块的链接ID
GetConnID() uint32
//获取远程客户端的 TCP状态 IP port
RemoteAddr() net.Addr
//发送数据, 将数据发送给远程的客户端
Send(data []byte) error
}
//定义一个处理链接业务的方法
type HandleFunc func(*net.TCPConn, []byte, int) error
2 - znet实现
package znet
import (
"fmt"
"net"
"zinx/ziface"
)
//iServer的接口实现,定义一个Server的服务器模块
type Server struct {
//服务器的名称
Name string
//服务器绑定的ip版本
IPVersion string
//服务器监听的IP
IP string
//服务器监听的端口
Port int
//当前的Server添加一个router,server注册的链接对应的处理业务
Router ziface.IRouter
}
//启动服务器
func (s *Server) Start() {
fmt.Printf("[Start] Server Listenner at IP :%s, Port %d, is startingn", s.IP, s.Port)
go func() {
// 1 获取一个TCP的Addr
addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
if err != nil {
fmt.Println("resolve tcp addt error : ", err)
return
}
//2 监听服务器的地址
listenner, err := net.ListenTCP(s.IPVersion, addr)
if err != nil {
fmt.Println("listen ", s.IPVersion, " err ", err)
return
}
fmt.Println("start Zinx server succ, ", s.Name, " succ, Listenning...")
var cid uint32
cid = 0
//3 阻塞的等待客户端链接,处理客户端链接业务(读写)
for {
//如果有客户端链接过来,阻塞会返回
conn, err := listenner.AcceptTCP()
if err != nil {
fmt.Println("Accept err", err)
continue
}
// 将处理新连接的业务方法 和 conn 进行绑定 得到我们的链接模块
dealConn := NewConnection(conn, cid, s.Router)
cid++
// 启动当前的链接业务处理
go dealConn.Start()
}
}()
}
//停止服务器
func (s *Server) Stop() {
//TODO 将一些服务器的资源、状态或者一些已经开辟的链接信息 进行停止或者回收
}
//运行服务器
func (s *Server) Serve() {
//启动server的服务功能
s.Start()
//TODO 做一些启动服务器之后的额外业务
//阻塞状态
select {}
}
//路由功能:给当前的服务注册一个路由方法,供客户端的链接处理使用
func (s *Server) AddRouter(router ziface.IRouter) {
s.Router = router
fmt.Println("Add Router Succ!!")
}
func NewServer(name string) ziface.IServer {
s := &Server{
Name: name,
IPVersion: "tcp4",
IP: "0.0.0.0",
Port: 8999,
Router: nil,
}
return s
}
package znet
import "zinx/ziface"
//实现router时, 先嵌入这个BaseRouter基类, 然后根据需要对这个基类的方法进行重写就好了
type BaseRouter struct{}
//这里之所以BaseRouter的方法都为空
//是因为有的Router不希望有PreHandle、PostHandle这两个业务
//所以Router全部继承BaseRouter的好处就是, 不需要实现PreHandle,PostHandle
//在处理conn业务之前的钩子方法Hook
func (br *BaseRouter) PreHandle(request ziface.IRequest) {}
//在处理conn业务的主方法hook
func (br *BaseRouter) Handle(request ziface.IRequest) {}
//在处理conn业务之后的钩子方法Hook
func (br *BaseRouter) PostHandle(request ziface.IRequest) {}
package znet
import (
"zinx/ziface"
)
type Request struct {
//已经和客户端建立好的链接
conn ziface.IConneciton
//客户端请求的数据
data []byte
}
//得到当前链接
func (r *Request) GetConnection() ziface.IConneciton {
return r.conn
}
//得到请求的消息数据
func (r *Request) GetData() []byte {
return r.data
}
package znet
import (
"fmt"
"net"
"zinx/ziface"
)
type Connection struct {
//当前链接的socket TCP套接字
Conn *net.TCPConn
//链接的ID
ConnID uint32
//当前的链接状态
isClosed bool
//告知当前链接已经退出的/停止 channel
ExitChan chan bool
//该链接处理的方法Router
Router ziface.IRouter
}
//初始化链接模块的方法
func NewConnection(conn *net.TCPConn, connID uint32, router ziface.IRouter) *Connection {
c := &Connection{
Conn: conn,
ConnID: connID,
Router: router,
isClosed: false,
ExitChan: make(chan bool, 1),
}
return c
}
//链接的读业务方法
func (c *Connection) StartReader() {
fmt.Println(" Reader Goroutine is running...")
defer fmt.Println("connID = ", c.ConnID, " Reader is exit, remote addr is ", c.RemoteAddr().String())
defer c.Stop()
for {
//读取客户端的数据到buf中, 最大512字节
buf := make([]byte, 512)
_, err := c.Conn.Read(buf)
if err != nil {
fmt.Println("recv buf err", err)
continue
}
//得到当前conn数据的Request请求数据
req := Request{
conn: c,
data: buf,
}
//从路由中,找到注册绑定的Conn对应的router调用
//执行注册的路由方法
go func(request ziface.IRequest) {
c.Router.PreHandle(request)
c.Router.Handle(request)
c.Router.PostHandle(request)
}(&req)
}
}
//启动链接 让当前的链接准备开始工作
func (c *Connection) Start() {
fmt.Println("Conn Start() ... ConnID = ", c.ConnID)
//启动从当前链接的读数据的业务
go c.StartReader()
//TODO 启动从当前链接写数据的业务
}
//停止链接 结束当前链接的工作
func (c *Connection) Stop() {
fmt.Println("Conn Stop().. ConnID = ", c.ConnID)
//如果当前链接已经关闭
if c.isClosed == true {
return
}
c.isClosed = true
//关闭socket链接
c.Conn.Close()
//回收资源
close(c.ExitChan)
}
//获取当前链接的绑定socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
return c.Conn
}
//获取当前链接模块的链接ID
func (c *Connection) GetConnID() uint32 {
return c.ConnID
}
//获取远程客户端的 TCP状态 IP port
func (c *Connection) RemoteAddr() net.Addr {
return c.Conn.RemoteAddr()
}
//发送数据, 将数据发送给远程的客户端
func (c *Connection) Send(data []byte) error {
return nil
}
3 - 测试类
package main
import (
"fmt"
"zinx/ziface"
"zinx/znet"
)
//ping test 自定义路由
type PingRouter struct {
znet.BaseRouter
}
//Test PreHandle
func (this *PingRouter) PreHandle(request ziface.IRequest) {
fmt.Println("Call Router PreHandle...")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("before ping...n"))
if err != nil {
fmt.Println("call back before ping error")
}
}
//Test Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
fmt.Println("Call Router Handle...")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("ping...ping...pingn"))
if err != nil {
fmt.Println("call back ping...ping...ping error")
}
}
//Test PostHandle
func (this *PingRouter) PostHandle(request ziface.IRequest) {
fmt.Println("Call Router PostHandle...")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("after pingn"))
if err != nil {
fmt.Println("call back after ping error")
}
}
func main() {
//1 创建一个server句柄,使用Zinx的api
s := znet.NewServer("[zinx V0.3]")
//2 给当前zinx框架添加一个自定义的router
s.AddRouter(&PingRouter{})
//3 启动server
s.Serve()
}
package main
import (
"fmt"
"net"
"time"
)
func main() {
fmt.Println("client start...")
time.Sleep(1 * time.Second)
//1 直接链接远程服务器,得到一个conn链接
conn, err := net.Dial("tcp", "127.0.0.1:8999")
if err != nil {
fmt.Println("client start err, exit!")
return
}
for {
//2 链接调用Write 写数据
_, err := conn.Write([]byte("Hello Zinx V0.2.."))
if err != nil {
fmt.Println("write conn err", err)
return
}
buf := make([]byte, 512)
cnt, err := conn.Read(buf)
if err != nil {
fmt.Println("read buf error")
return
}
fmt.Printf(" server call back: %s, cnt = %dn", buf, cnt)
//cpu阻塞
time.Sleep(1 * time.Second)
}
}