go-tcp フレームワークを 0 から 1 まで開発 [1-サーバーの構築、接続とビジネス バインディングのカプセル化、基本的なルーターの実装、グローバル構成ファイルの抽出]

go-tcp フレームワークを 0 から 1 まで開発 [1-サーバーの構築、接続とビジネスのバインディングをパッケージ化、基本的なルーターを実装]

この問題では主に、サーバーの構築、接続とビジネス バインディングのカプセル化、基本的なルーター (ビジネスを処理する部分) の実装、およびフレームワークのグローバル構成ファイルの抽出が完了します。

  • 設定ファイル (サーバーのリスニング ポート、リスニング IP など) からデータを読み取り、カスタム ルーターを通じて特定のビジネス操作を完了します。

最初のバージョンの最終プロジェクト構造:
ここに画像の説明を挿入

1 基本サーバーの構築【V1.0】

1.1 サーバー側の書き込み

  • iserver.go を記述してサーバーインターフェースを定義します
  • server.go を作成し、サーバー構造を定義し、インターフェイスを実装します。

①/zinx/ziface/iserver.go:

package ziface

type IServer interface {
    
    
	Start()
	Stop()
	Serve()
}

②/zinx/znet/server.go

package znet

import (
	"fmt"
	"net"
)

type Server struct {
    
    
	Name      string
	IPVersion string
	IP        string
	Port      int
}

func NewServer(name string) *Server {
    
    
	s := &Server{
    
    
		Name:      name,
		IPVersion: "tcp4",
		IP:        "0.0.0.0",
		Port:      8090,
	}
	return s
}

func (s *Server) Start() {
    
    
	//启动服务监听端口
	fmt.Printf("[start] Server listener at IP:%s, Port %d is starting\n", s.IP, s.Port)

	go func() {
    
    
		addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
		if err != nil {
    
    
			fmt.Printf("resolve tcp addr error %v\n", err)
			return
		}
		listener, err := net.ListenTCP(s.IPVersion, addr)
		if err != nil {
    
    
			fmt.Println("listen ", s.IPVersion, " err ", err)
			return
		}
		fmt.Println("[start] Zinx server success ", s.Name, "Listening...")
		//阻塞连接,处理业务
		for {
    
    
			conn, err := listener.AcceptTCP()
			if err != nil {
    
    
				fmt.Println("Accept err ", err)
				continue
			}
			//处理业务:回显消息
			go func() {
    
    
				for {
    
    
					buf := make([]byte, 512)
					cnt, err := conn.Read(buf)
					if err != nil {
    
    
						fmt.Println("read buf err ", err)
						continue
					}
					fmt.Printf("receive client buf %s, cnt %d \n", buf, cnt)
					//回显读取到的字节数
					if _, err := conn.Write(buf[:cnt]); err != nil {
    
    
						fmt.Println("write buf err ", err)
						continue
					}
				}

			}()
		}
	}()
}

func (s *Server) Stop() {
    
    

}

func (s *Server) Serve() {
    
    
	s.Start()
	//阻塞,一直读取客户端所发送过来的消息
	select {
    
    }
}

1.2 サーバー側機能のテスト

① Server.goとClient.goを作成する

  1. myDemo/zinxV1.0/Server.go を書き込みます
package main

import "myTest/zinx/znet"

func main() {
    
    
	s := znet.NewServer("[Zinx v1.0]")
	s.Serve()
}
  1. myDemo/zinxV1.0/Client.go を書き込みます
package main

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

/*
模拟客户端
*/
func main() {
    
    
	fmt.Println("client start...")
	time.Sleep(time.Second * 1)
	//1 创建服务器连接
	conn, err := net.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
    
    
		fmt.Println("client start err ", err)
		return
	}
	for {
    
    
		//2 调用连接向服务器发数据
		_, err := conn.Write([]byte("Hello Zinx v0.1"))
		if err != nil {
    
    
			fmt.Println("write conn err ", err)
			return
		}
		// 3 读取服务器返回的数据
		buf := make([]byte, 512)
		cnt, err := conn.Read(buf)
		if err != nil {
    
    
			fmt.Println("client read buf err ", err)
			return
		}
		fmt.Printf("server call back:%s, cnt=%d\n", buf, cnt)
		//cpu阻塞,让出cpu时间片,避免无限for循环导致其他程序无法获取cpu时间片
		time.Sleep(time.Second * 1)
	}
}

②検査結果

ここに画像の説明を挿入

サーバーがクライアントからデータを受信し、それを 1 秒ごとにエコーしていることがわかります。

2 パッケージ接続接続、ビジネスバインディング [V2.0]

バージョン V0.1 では、基本的なサーバー フレームワークを実装しました。今度は、クライアント リンクと、さまざまなクライアント リンクによって処理されるさまざまなビジネス用に、インターフェイスのカプセル化の別の層を作成する必要があります。もちろん、最初に構造を構築します。
次に、ziface の下にインターフェイス ファイル iconnection.go を作成します。もちろん、その実装ファイルは znet の下の connection.go に置きます。

必要な方法:

  1. 接続を開始する
  2. 接続を停止する
  3. 接続された conn オブジェクトを取得する
  4. 接続のIDを取得します
  5. クライアント接続のアドレスとポートを取得します。
  6. データの送信方法
  7. つながりに縛られた業務処理機能

2.1 パッケージ接続

  • iconnection インターフェイスを定義する
  • 接続構造を作成し、iconnection を実装する
  1. /zinx/ziface/iconnection.go を作成します。
package ziface

import "net"

type IConnection interface {
    
    
	//启动连接
	Start()
	//停止连接
	Stop()
	//获取当前连接的Conn对象
	GetTCPConnection() *net.TCPConn
	//获取当前连接模块的id
	GetConnectionID() uint32
	//获取远程客户端的TCP状态 IP:Port
	RemoteAddr() net.Addr
	//发送数据
	Send()
}

//定义一个处理连接业务的方法
type HandleFunc func(*net.TCPConn, []byte, int) error
  1. /zinx/znet/connection.go を作成します
package znet

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

type Connection struct {
    
    
	Conn      *net.TCPConn
	ConnID    uint32
	isClosed  bool
	handleAPI ziface.HandleFunc
	//告知当前的连接已经退出
	ExitChan chan bool
}

func NewConnection(conn *net.TCPConn, connID uint32, callback_api ziface.HandleFunc) *Connection {
    
    
	c := &Connection{
    
    
		Conn:      conn,
		ConnID:    connID,
		handleAPI: callback_api,
		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 := make([]byte, 512)
		cnt, err := c.Conn.Read(buf)
		if err != nil {
    
    
			fmt.Printf("connID %d receive buf err %s\n", c.ConnID, err)
			continue
		}
		//调用当前所绑定的处理业务的方法HandleAPI
		if err := c.handleAPI(c.Conn, buf, cnt); err != nil {
    
    
			fmt.Println("ConnID", c.ConnID, " handle is err ", err)
			break
		}
	}
}

//启动连接
func (c *Connection) Start() {
    
    
	fmt.Printf("ConnID %d is Start...", c.ConnID)
	go c.StartReader()
}

//停止连接
func (c *Connection) Stop() {
    
    
	fmt.Println("Connection Stop()...ConnectionID = ", c.ConnID)
	if c.isClosed {
    
    
		return
	}
	c.isClosed = true
	c.Conn.Close()
	close(c.ExitChan)
}

//获取当前连接的Conn对象
func (c *Connection) GetTCPConnection() *net.TCPConn {
    
    
	return c.Conn
}

//获取当前连接模块的id
func (c *Connection) GetConnectionID() uint32 {
    
    
	return c.ConnID
}

//获取远程客户端的TCP状态 IP:Port
func (c *Connection) RemoteAddr() net.Addr {
    
    
	return c.Conn.RemoteAddr()
}

//发送数据
func (c *Connection) Send() {
    
    

}

2.2 server.goの変更(カプセル化されたconnによる業務処理の実現)

特定のビジネスを実現するためにserver.goを修正し、CallBackToClientメソッドを追加します
ここに画像の説明を挿入


ここに画像の説明を挿入
ZinxV1.0 バージョンの server.go の処理ビジネス ロジック部分を、すべてのコード
/zinx/znet/server.goを呼び出すカプセル化された Conn に置き換えます。

package znet

import (
	"fmt"
	"github.com/kataras/iris/v12/x/errors"
	"net"
)

type Server struct {
    
    
	Name      string
	IPVersion string
	IP        string
	Port      int
}

func NewServer(name string) *Server {
    
    
	s := &Server{
    
    
		Name:      name,
		IPVersion: "tcp4",
		IP:        "0.0.0.0",
		Port:      8090,
	}
	return s
}

//定义当前客户端连接所绑定的handleAPI(暂时写死处理业务逻辑:数据回显)
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 buf err ", err)
		return errors.New("CallBackToClient error")
	}
	return nil
}

func (s *Server) Start() {
    
    
	//启动服务监听端口
	fmt.Printf("[start] Server listener at IP:%s, Port %d is starting\n", s.IP, s.Port)

	go func() {
    
    
		addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
		if err != nil {
    
    
			fmt.Printf("resolve tcp addr error %v\n", err)
			return
		}
		listener, err := net.ListenTCP(s.IPVersion, addr)
		if err != nil {
    
    
			fmt.Println("listen ", s.IPVersion, " err ", err)
			return
		}
		fmt.Println("[start] Zinx server success ", s.Name, "Listening...")
		//阻塞连接,处理业务
		for {
    
    
			conn, err := listener.AcceptTCP()
			if err != nil {
    
    
				fmt.Println("Accept err ", err)
				continue
			}
			var cid uint32 = 0
			dealConn := NewConnection(conn, cid, CallBackToClient)
			cid++
			//开启goroutine处理启动当前conn
			go dealConn.Start()
			处理业务:回显消息
			//go func() {
    
    
			//	for {
    
    
			//		buf := make([]byte, 512)
			//		cnt, err := conn.Read(buf)
			//		if err != nil {
    
    
			//			fmt.Println("read buf err ", err)
			//			continue
			//		}
			//		fmt.Printf("receive client buf %s, cnt %d \n", buf, cnt)
			//		//回显读取到的字节数
			//		if _, err := conn.Write(buf[:cnt]); err != nil {
    
    
			//			fmt.Println("write buf err ", err)
			//			continue
			//		}
			//	}
			//
			//}()
		}
	}()
}

func (s *Server) Stop() {
    
    

}

func (s *Server) Serve() {
    
    
	s.Start()
	//阻塞,一直读取客户端所发送过来的消息
	select {
    
    }
}

2.3 ZinxV2.0 機能のテスト

①Server.goとClient.goのログ出力を修正

/myDemo/ZinxV2.0/Client.go と /myDemo/ZinxV2.0/Server.go を作成します。テスト コードのこの部分は V1.0 と同じです。印刷ログを Zinx2.0 に置き換えるだけです。

  • Client.go
package main

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

/*
模拟客户端
*/
func main() {
    
    
	fmt.Println("client start...")
	time.Sleep(time.Second * 1)
	//1 创建服务器连接
	conn, err := net.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
    
    
		fmt.Println("client start err ", err)
		return
	}
	for {
    
    
		//2 调用连接向服务器发数据
		_, err := conn.Write([]byte("Hello Zinx v0.2"))
		if err != nil {
    
    
			fmt.Println("write conn err ", err)
			return
		}
		// 3 读取服务器返回的数据
		buf := make([]byte, 512)
		cnt, err := conn.Read(buf)
		if err != nil {
    
    
			fmt.Println("client read buf err ", err)
			return
		}
		fmt.Printf("server call back:%s, cnt=%d\n", buf, cnt)
		//cpu阻塞,让出cpu时间片,避免无限for循环导致其他程序无法获取cpu时间片
		time.Sleep(time.Second * 1)
	}
}
  • Server.go
package main

import "myTest/zinx/znet"

func main() {
    
    
	s := znet.NewServer("[Zinx v2.0]")
	s.Serve()
}

②検査結果

ここに画像の説明を挿入

3 基本ルーターの実現[V3.0]

3.1 リクエストのカプセル化

接続とデータをバインドする

zinx/ziface/irequest.go:

package ziface

import "net"

type IRequest interface {
    
    
	GetConnection() *net.TCPConn
	GetData() []byte
}

zinx/znet/request.go:

package znet

import "net"

type Request struct {
    
    
	conn *net.TCPConn
	data []byte
}

func (r *Request) GetConnection() *net.TCPConn {
    
    
	return r.conn
}

func (r *Request) GetData() []byte {
    
    
	return r.data
}

3.2 ルーターモジュール

zinx/ziface/irouter.go

package ziface

type IRouter interface {
    
    
	//处理请求之前的方法
	PreHandle(request IRequest)
	Handler(request IRequest)
	//处理请求之后的方法
	PostHandler(request IRequest)
}

zinx/znet/router.go

package znet

import "myTest/zinx/ziface"

type BaseRouter struct {
    
    
}

//这里做了空实现,直接让后续Router继承BaseRouter,然后根据需要重写对应方法即可
func (br *BaseRouter) PreHandle(request ziface.IRequest) {
    
    }
func (br *BaseRouter) Handler(request ziface.IRequest)   {
    
    }

func (br *BaseRouter) PostHandler(request ziface.IRequest) {
    
    }

3.3 フレームワーク統合ルーターモジュール

  • znet/server.go の HandlerFunc モジュールをキャンセルし、Router に変更します。server.go に Router プロパティを追加
    ここに画像の説明を挿入
    ここに画像の説明を挿入
  • znet/connection.go の callback_api ziface.HandleFunc パラメータを Router に変更します。
    ここに画像の説明を挿入

zinx/znet/connection.go

package znet

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

type Connection struct {
    
    
	Conn     *net.TCPConn
	ConnID   uint32
	isClosed bool
	//告知当前的连接已经退出
	ExitChan chan bool
	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 := make([]byte, 512)
		_, err := c.Conn.Read(buf)
		if err != nil {
    
    
			fmt.Printf("connID %d receive buf err %s\n", c.ConnID, err)
			continue
		}
		//封装请求,改为router处理
		r := Request{
    
    
			conn: c.Conn,
			data: buf,
		}
		go func(request ziface.IRequest) {
    
    
			c.Router.PreHandle(request)
			c.Router.Handler(request)
			c.Router.PostHandler(request)
		}(&r)
	}
}

//启动连接
func (c *Connection) Start() {
    
    
	fmt.Printf("ConnID %d is Start...", c.ConnID)
	go c.StartReader()
}

//停止连接
func (c *Connection) Stop() {
    
    
	fmt.Println("Connection Stop()...ConnectionID = ", c.ConnID)
	if c.isClosed {
    
    
		return
	}
	c.isClosed = true
	c.Conn.Close()
	close(c.ExitChan)
}

//获取当前连接的Conn对象
func (c *Connection) GetTCPConnection() *net.TCPConn {
    
    
	return c.Conn
}

//获取当前连接模块的id
func (c *Connection) GetConnectionID() uint32 {
    
    
	return c.ConnID
}

//获取远程客户端的TCP状态 IP:Port
func (c *Connection) RemoteAddr() net.Addr {
    
    
	return c.Conn.RemoteAddr()
}

//发送数据
func (c *Connection) Send() {
    
    

}

zinx/znet/server.go

package znet

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

type Server struct {
    
    
	Name      string
	IPVersion string
	IP        string
	Port      int
	Router    ziface.IRouter
}

func NewServer(name string) *Server {
    
    
	s := &Server{
    
    
		Name:      name,
		IPVersion: "tcp4",
		IP:        "0.0.0.0",
		Port:      8090,
		Router:    nil,
	}
	return s
}

func (s *Server) Start() {
    
    
	//启动服务监听端口
	fmt.Printf("[start] Server listener at IP:%s, Port %d is starting\n", s.IP, s.Port)

	go func() {
    
    
		addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
		if err != nil {
    
    
			fmt.Printf("resolve tcp addr error %v\n", err)
			return
		}
		listener, err := net.ListenTCP(s.IPVersion, addr)
		if err != nil {
    
    
			fmt.Println("listen ", s.IPVersion, " err ", err)
			return
		}
		fmt.Println("[start] Zinx server success ", s.Name, "Listening...")
		//阻塞连接,处理业务
		for {
    
    
			conn, err := listener.AcceptTCP()
			if err != nil {
    
    
				fmt.Println("Accept err ", err)
				continue
			}
			var cid uint32 = 0
			dealConn := NewConnection(conn, cid, s.Router)
			cid++
			//开启goroutine处理启动当前conn
			go dealConn.Start()
		}
	}()
}

func (s *Server) Stop() {
    
    

}

func (s *Server) Serve() {
    
    
	s.Start()
	//阻塞,一直读取客户端所发送过来的消息
	select {
    
    }
}

func (s *Server) AddRouter(router ziface.IRouter) {
    
    
	s.Router = router
}

テストフレームワーク統合ルーター効果

myDemo/ZinxV3.0/client.go
package main

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

/*
模拟客户端
*/
func main() {
    
    
	fmt.Println("client start...")
	time.Sleep(time.Second * 1)
	//1 创建服务器连接
	conn, err := net.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
    
    
		fmt.Println("client start err ", err)
		return
	}
	for {
    
    
		//2 调用连接向服务器发数据
		_, err := conn.Write([]byte("Hello Zinx v0.3"))
		if err != nil {
    
    
			fmt.Println("write conn err ", err)
			return
		}
		// 3 读取服务器返回的数据
		buf := make([]byte, 512)
		cnt, err := conn.Read(buf)
		if err != nil {
    
    
			fmt.Println("client read buf err ", err)
			return
		}
		fmt.Printf("server call back:%s, cnt=%d\n", buf, cnt)
		//cpu阻塞,让出cpu时间片,避免无限for循环导致其他程序无法获取cpu时间片
		time.Sleep(time.Second * 1)
	}
}
myDemo/ZinxV3.0/server.go
package main

import (
	"fmt"
	"myTest/zinx/ziface"
	"myTest/zinx/znet"
)

//自定义一个Router,测试路由功能
type PingRouter struct {
    
    
	znet.BaseRouter
}

func (pr *PingRouter) PreHandle(request ziface.IRequest) {
    
    
	_, err := request.GetConnection().Write([]byte("pre handle success..."))
	if err != nil {
    
    
		fmt.Println("server call pre handle err ", err)
		return
	}
	fmt.Println("server call pre handle...")
}

func (pr *PingRouter) Handler(request ziface.IRequest) {
    
    
	_, err := request.GetConnection().Write([]byte("handle success..."))
	if err != nil {
    
    
		fmt.Println("server call handle err ", err)
		return
	}
	fmt.Println("server call handler....")
}

func (pr *PingRouter) PostHandler(request ziface.IRequest) {
    
    
	_, err := request.GetConnection().Write([]byte("post handle success..."))
	if err != nil {
    
    
		fmt.Println("server call post handle err ", err)
		return
	}
	fmt.Println("server call post handler...")
}

func main() {
    
    
	s := znet.NewServer("[Zinx v3.0]")
	//添加自定义路由
	router := &PingRouter{
    
    }
	s.AddRouter(router)
	s.Serve()
}

最終的な効果:
ここに画像の説明を挿入

テンプレートメソッドの設計パターンに従って、呼び出しが完了します

4 グローバル設定ファイルを解凍する【V4.0】

4.1 /zinx/util/globalobj.go を書き込む

主にzinx設定ファイルの情報を読み取るために使用されます。

package util

import (
	"encoding/json"
	"io/ioutil"
	"myTest/zinx/ziface"
)

type GlobalObj struct {
    
    
	TCPServer ziface.IServer //当前全局Zinx的server对象
	Host      string         //当前服务器主机监听的ip
	TcpPort   int            //当前服务器主机监听的端口号
	Name      string         //当前服务器的名称

	Version        string //当前Zinx的版本号
	MaxConn        int    //当前服务器所允许的最大连接数
	MaxPackageSize uint32 //当前Zinx框架数据包的最大值
}

var GlobalObject *GlobalObj

//从配置文件中重新加载GlobalObject的信息
func (g *GlobalObj) Reload() {
    
    
	data, err := ioutil.ReadFile("conf/zinx.json")
	if err != nil {
    
    
		panic(err)
	}
	//将json文件数据解析到struct中
	err = json.Unmarshal(data, &GlobalObject)
	if err != nil {
    
    
		panic(err)
	}
}

//在其他文件导入该util包的时候会加载init
func init() {
    
    
	GlobalObject = &GlobalObj{
    
    
		Name:           "ZinxServerApp",
		Version:        "V0.4",
		TcpPort:        8090,
		Host:           "0.0.0.0",
		MaxConn:        120,
		MaxPackageSize: 4096,
	}
	//尝试从conf/zinx.json中去加载用户自定义的参数
	GlobalObject.Reload()
}

4.2 以前のserver.goのハードコードを置き換える

/zinx/znet/server.go セクションと /zinx/znet/connection.go セクションを含めます

  • サーバ:
    ここに画像の説明を挿入
  • 繋がり:ここに画像の説明を挿入

4.3 テスト

myDemo/ZinxV4.0 を書く

  • そして、対応する .json 設定ファイルを記述します (Client.go と Server.go は V3.0 と同じです)。

ここに画像の説明を挿入
ジンクス.json

{
    
    
  "Name": "Zinx Server Application",
  "Version": "V0.4",
  "Host": "0.0.0.0",
  "TcpPort": 8091,
  "MaxConn": 30,
  "MaxPackageSize": 1024
}

最終的な効果:
ここに画像の説明を挿入

参考:https://www.yuque.com/aceld/npyr8s/bgftov

おすすめ

転載: blog.csdn.net/weixin_45565886/article/details/131973229