建站系统是什么,兰州网络推广,网络架构有哪几层,网站如何改首页模块#x1f4d5;作者简介#xff1a; 过去日记#xff0c;致力于Java、GoLang,Rust等多种编程语言#xff0c;热爱技术#xff0c;喜欢游戏的博主。 #x1f4d7;本文收录于Ainx系列#xff0c;大家有兴趣的可以看一看 #x1f4d8;相关专栏Rust初阶教程、go语言基础系列… 作者简介 过去日记致力于Java、GoLang,Rust等多种编程语言热爱技术喜欢游戏的博主。 本文收录于Ainx系列大家有兴趣的可以看一看 相关专栏Rust初阶教程、go语言基础系列、spring教程等大家有兴趣的可以看一看 Java并发编程系列设计模式系列、go web开发框架 系列正在发展中喜欢JavaGoLangRust的朋友们可以关注一下哦 本文大部分都是借鉴刘丹冰大佬的zinx框架和文章更推荐大家去读大佬的原文,本文只是个人学习的记录 文章目录 Ainx-V0.2-简单的连接封装与业务绑定aiface创建iconnection.goanet 创建iconnection.go重新更更正⼀一下Server.go中 处理理conn的连接业务 Ainx-V0.2-简单的连接封装与业务绑定
V0.1版本我们已经实现了了⼀一个基础的Server框架现在我们需要对客户端链接和不不同的客户端链接所处 理理的不不同业务再做⼀一层接⼝口封装当然我们先是把架构搭建起来。 现在在 ainterface 下创建⼀一个属于链接的接⼝⽂文件 iconnection.go 当然他的实现⽂文件我们放在 anet 下的 connection.go 中。
aiface创建iconnection.go ainx/ainterface/iconnection.go package ainterfaceimport nettype IConnection interface {// 启动连接让当前连接开始工作Start()// 停止链接结束当前连接状态Stop()//从当前连接获取原始的socket TCPConn GetTCPConnection() *net.TCPConn //获取当前连接IDGetConnID() uint32 //获取远程客户端地址信息 RemoteAddr() net.Addr
}// 定义⼀一个统⼀一处理理链接业务的接⼝口
type HandFunc func(*net.TCPConn, []byte, int) error
该接⼝的⼀些基础方法代码注释已经介绍的很清楚这里先简单说明⼀个HandFunc这个函数类型 这个是所有conn链接在处理业务的函数接⼝第⼀参数是socket原⽣链接第二个参数是客户端请求的数据第三个参数是客户端请求的数据长度。这样如果我们想要指定⼀一个conn的处理业务只要定义一个HandFunc类型的函数然后和该链接绑定就可以了了。
anet 创建iconnection.go ainx/anet/connection.go package anetimport (ainx/ainterfacefmtnet
)type Connection struct {//当前链接的socket TCP套接字Conn *net.TCPConn// 当前链接的ID也可以称作SessionIDID全局唯一ConnID uint32// 当前链接的关闭状态isClosed bool// 处理该链接方法的APIhandleAPI ainterface.HandFunc// 告知该链接已经退出/停止的channelExitBuffChan chan bool
}// 创建链接的方法
func NewConnection(conn *net.TCPConn, connID uint32, callback_api ainterface.HandFunc) *Connection {c : Connection{Conn: conn,ConnID: connID,isClosed: false,handleAPI: callback_api,ExitBuffChan: make(chan bool, 1),}return c
}// 处理conn读数据的Goroutine
func (c *Connection) StartReader() {fmt.Println(Reader Goroutine is running)defer fmt.Println(c.RemoteAddr().String(), conn reader exit!)defer c.Stop()for {buf : make([]byte, 512)cnt, err : c.Conn.Read(buf)// 读取数据失败退出连接if err ! nil {fmt.Println(recv buf err , err)c.ExitBuffChan - truecontinue}// 调用当前业务链这里执行的是当前conn的绑定的handle方法if err : c.handleAPI(c.Conn, buf, cnt); err ! nil {fmt.Println(connID , c.ConnID, handle is err)c.ExitBuffChan - truereturn}}
}// 启动连接让当前链接工作
func (c *Connection) Start() {// 开启处理该链接读取到客户端数据之后的请求业务go c.StartReader()for {select {case -c.ExitBuffChan:// 得到退出消息不再阻塞return}}
}// 停止链接结束当前链接状态M
func (c *Connection) Stop() {//1.如果当前链接关闭if c.isClosed true {return}c.isClosed true//TODO Connection Stop() 如果用户注册了该链接的关闭回调业务那么在此刻应该显示调用// 关闭socket链接err : c.Conn.Close()if err ! nil {return}//通知从缓冲队列读数据的业务该链接已经关闭c.ExitBuffChan - true//关闭该链接全部管道close(c.ExitBuffChan)
}// 从当前链接获取原始的socket TCPConn
func (c *Connection) GetTCPConnection() *net.TCPConn {return c.Conn
}// 获取当前链接ID
func (c *Connection) GetConnID() uint32 {return c.ConnID
}// 获取远程客户端地址信息
func (c *Connection) RemoteAddr() net.Addr {return c.Conn.RemoteAddr()
}
重新更更正⼀一下Server.go中 处理理conn的连接业务 ainx/anet/server.go package anetimport (ainx/ainterfaceerrorsfmtnettime
)type Server struct {// 设置服务器名称Name string// 设置网络协议版本IPVersion string// 设置服务器绑定IPIP string// 设置端口号Port string
}// 定义当前客户端链接的handle api
func CallBackToClient(conn *net.TCPConn, data []byte, cnt int) error {//回显业务fmt.Println([Conn Handle] CallBackToClient ...)if _, err : conn.Write(data[:cnt]); err ! nil {fmt.Println(write back buf err, err)return errors.New(CallBackToClient error)}return nil
}// 实现 ainterface.IServer 里的全部接口方法
// 开启网络服务
func (s *Server) Start() {fmt.Printf([START] Server listenner at IP: %s, Port %s, is starting\n, s.IP, s.Port)// 开启一个go去做服务端的Listener业务// todo 未来目标是提供更多协议可以利用if或者switch对IPVersion进行判断而选择采取哪种协议下面整个方法要重写go func() {//1 获取一个TCP的Addraddr, err : net.ResolveTCPAddr(s.IPVersion, s.IP:s.Port)if err ! nil {fmt.Println(resolve tcp addr err: , err)return}// 2 监听服务器地址listener, err : net.ListenTCP(s.IPVersion, addr)if err ! nil {fmt.Println(listen, s.IPVersion, err, err)return}// 已经成功监听fmt.Println(start Ainx server , s.Name, success, now listenning...)//TODO server.go 应该有一个自动生成ID的方法var cid uint32cid 0//3 启动server网络连接业务for {//3.1 阻塞等待客户端建立连接请求conn, err : listener.AcceptTCP()if err ! nil {fmt.Println(Accept err , err)continue}//3.2 TODO Server.Start() 设置服务器最大连接控制,如果超过最大连接那么则关闭此新的连接//3.3 处理该新连接请求的 业务 方法 此时应该有 handler 和 conn是绑定的dealConn : NewConnection(conn, cid, CallBackToClient)cid//3.4 启动当前链接的处理业务go dealConn.Start()}}()
}
func (s *Server) Stop() {fmt.Println([STOP] Zinx server , name , s.Name)//TODO Server.Stop() 将其他需要清理的连接信息或者其他信息 也要一并停止或者清理
}
func (s *Server) Serve() {s.Start()//TODO Server.Serve() 是否在启动服务的时候 还要处理其他的事情呢 可以在这里添加//阻塞,否则主Go退出 listenner的go将会退出for {time.Sleep(10 * time.Second)}
}/*
创建一个服务器句柄
*/
func NewServer(name string) ainterface.IServer {s : Server{Name: name,IPVersion: tcp4,IP: 0.0.0.0,Port: 8080,}return s
}CallBackToClient 是我们给当前客户端conn对象绑定的handle⽅方法当然⽬目前是server端强制绑定 的回显业务我们之后会丰富框架让这个⽤用户可以让⽤用户⾃自定义指定handle。 在 start() ⽅方法中我们主要做了了如下的修改: //3.3 处理该新连接请求的 业务 方法 此时应该有 handler 和 conn是绑定的dealConn : NewConntion(conn, cid, CallBackToClient)cid //3.4 启动当前链接的处理业务go dealConn.Start()实际上⽬目前Zinx框架的对外接⼝口并未改变所以V0.1的测试依然有效。我们依然可以启动V0.1的测试类经行测试测试结果如下 RUN TestServer
[START] Server listenner at IP: 0.0.0.0, Port 8080, is starting
Client Test ... start
start Ainx server first success, now listenning...
Reader Goroutine is running
[Conn Handle] CallBackToClient ...
Server call back : hello word,cnt 10 [Conn Handle] CallBackToClient ...
Server call back : hello word,cnt 10 [Conn Handle] CallBackToClient ...
Server call back : hello word,cnt 10 [Conn Handle] CallBackToClient ...
Server call back : hello word,cnt 10 [Conn Handle] CallBackToClient ...
Server call back : hello word,cnt 10 [Conn Handle] CallBackToClient ...
Server call back : hello word,cnt 10