这里先看一下Zinx最简单的Server雏形。

1. Zinx-V0.1-基础Server

为了更好的看到Zinx框架,首先Zinx构建Zinx的最基本的两个模块zifaceznet

ziface主要是存放一些Zinx框架的全部模块的抽象层接口类,Zinx框架的最基本的是服务类接口iserver,定义在ziface模块中。

znet模块是zinx框架中网络相关功能的实现,所有网络相关模块都会定义在znet模块中。

1.1 Zinx-V0.1 代码实现

A) 创建zinx框架

在$GOPATH/src下创建zinx文件夹

B) 创建ziface、znet模块

在zinx/下 创建ziface、znet文件夹, 使当前的文件路径如下:

  1. └── zinx
  2. ├── ziface
  3. └──
  4. └── znet
  5. ├──

C) 在ziface下创建服务模块抽象层iserver.go

zinx/ziface/iserver.go

  1. package ziface
  2. //定义服务器接口
  3. type IServer interface{
  4. //启动服务器方法
  5. Start()
  6. //停止服务器方法
  7. Stop()
  8. //开启业务服务方法
  9. Serve()
  10. }

D) 在znet下实现服务模块server.go
package znet

import (
    "fmt"
    "net"
    "time"
    "zinx/ziface"
)

//iServer 接口实现,定义一个Server服务类
type Server struct {
    //服务器的名称
    Name string
    //tcp4 or other
    IPVersion string
    //服务绑定的IP地址
    IP string
    //服务绑定的端口
    Port int
}


//============== 实现 ziface.IServer 里的全部接口方法 ========

//开启网络服务
func (s *Server) Start() {
    fmt.Printf("[START] Server listenner at IP: %s, Port %d, is starting\n", s.IP, s.Port)

    //开启一个go去做服务端Linster业务
    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 addr err: ", 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  ", s.Name, " succ, now listenning...")

        //3 启动server网络连接业务
        for {
            //3.1 阻塞等待客户端建立连接请求
            conn, err := listenner.AcceptTCP()
            if err != nil {
                fmt.Println("Accept err ", err)
                continue
            }

            //3.2 TODO Server.Start() 设置服务器最大连接控制,如果超过最大连接,那么则关闭此新的连接

            //3.3 TODO Server.Start() 处理该新连接请求的 业务 方法, 此时应该有 handler 和 conn是绑定的

            //我们这里暂时做一个最大512字节的回显服务
            go func () {
                //不断的循环从客户端获取数据
                for  {
                    buf := make([]byte, 512)
                    cnt, err := conn.Read(buf)
                    if err != nil {
                        fmt.Println("recv buf err ", err)
                        continue
                    }
                    //回显
                    if _, err := conn.Write(buf[:cnt]); err !=nil {
                        fmt.Println("write back buf err ", err)
                        continue
                    }
                }
            }()
        }
    }()
}

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) ziface.IServer {
    s:= &Server {
        Name :name,
        IPVersion:"tcp4",
        IP:"0.0.0.0",
        Port:7777,
    }

    return s
}

好了,以上我们已经完成了Zinx-V0.1的基本雏形了,虽然只是一个基本的回写客户端数据(我们之后会自定义处理客户端业务方法),那么接下来我们就应该测试我们当前的zinx-V0.1是否可以使用了。

1.2 Zinx框架单元测试样例

理论上我们应该可以现在导入zinx框架,然后写一个服务端程序,再写一个客户端程序进行测试,但是我们可以通过Go的单元Test功能,进行单元测试

创建zinx/znet/server_test.go

package znet

import (
    "fmt"
    "net"
    "testing"
    "time"
)

/*
    模拟客户端
 */
 func ClientTest() {

     fmt.Println("Client Test ... start")
     //3秒之后发起测试请求,给服务端开启服务的机会
     time.Sleep(3 * time.Second)

     conn,err := net.Dial("tcp", "127.0.0.1:7777")
     if err != nil {
         fmt.Println("client start err, exit!")
        return
    }

     for {
         _, err := conn.Write([]byte("hello ZINX"))
         if err !=nil {
             fmt.Println("write error 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 = %d\n", buf,  cnt)

         time.Sleep(1*time.Second)
    }
 }

//Server 模块的测试函数
func TestServer(t *testing.T) {

    /*
        服务端测试
    */
    //1 创建一个server 句柄 s
    s := NewServer("[zinx V0.1]")

    /*
        客户端测试
    */
    go ClientTest()

    //2 开启服务
    s.Serve()
}

在zinx/znet下执行

$ go test

执行结果,如下:

[START] Server listenner at IP: 0.0.0.0, Port 7777, is starting
Client Test ... start
listen tcp4 err listen tcp4 0.0.0.0:7777: bind: address already in use
 server call back : hello ZINX, cnt = 6
 server call back : hello ZINX, cnt = 6
 server call back : hello ZINX, cnt = 6
 server call back : hello ZINX, cnt = 6

说明我们的zinx框架已经可以使用了。

1.3 使用Zinx-V0.1完成应用程序

当然,如果感觉go test 好麻烦,那么我们可以完全基于zinx写两个应用程序,Server.go , Client.go

Server.go

package main

import (
    "zinx/znet"
)

//Server 模块的测试函数
func main() {

    //1 创建一个server 句柄 s
    s := znet.NewServer("[zinx V0.1]")

    //2 开启服务
    s.Serve()
}

启动Server.go

go run Server.go

Client.go

package main

import (
    "fmt"
    "net"
    "time"
)

func main() {

    fmt.Println("Client Test ... start")
    //3秒之后发起测试请求,给服务端开启服务的机会
    time.Sleep(3 * time.Second)

    conn,err := net.Dial("tcp", "127.0.0.1:7777")
    if err != nil {
        fmt.Println("client start err, exit!")
        return
    }

    for {
        _, err := conn.Write([]byte("hahaha"))
        if err !=nil {
            fmt.Println("write error 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 = %d\n", buf,  cnt)

        time.Sleep(1*time.Second)
    }
}

启动Client.go进行测试

go run Client.go