diff --git a/README.md b/README.md index 2d8eb70..1edea79 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # -[![License](https://img.shields.io/badge/License-GPL%203.0-blue.svg)](LICENSE) [![Gitter](https://img.shields.io/badge/在线交流-Gitter-green.svg)](https://gitter.im/zinx_go/community) [![zinx详细教程](https://img.shields.io/badge/zinx详细教程-简书-red.svg)](https://www.jianshu.com/p/23d07c0a28e5) [![zinx原创书籍下载](https://img.shields.io/badge/原创书籍下载-Gitbook-black.svg)](https://legacy.gitbook.com/book/aceld/zinx/details) +[![License](https://img.shields.io/badge/License-GPL%203.0-blue.svg)](LICENSE) [![Gitter](https://img.shields.io/badge/在线交流-Gitter-green.svg)](https://gitter.im/zinx_go/community) [![zinx详细教程](https://img.shields.io/badge/zinx详细教程-简书-red.svg)](https://www.kancloud.cn/aceld/zinx) [![zinx原创书籍下载](https://img.shields.io/badge/原创书籍下载-KanCloud-black.svg)](https://www.kancloud.cn/@aceld) Zinx 是一个基于Golang的轻量级并发服务器框架 @@ -91,6 +91,59 @@ Zinx框架的项目制作采用编码和学习教程同步进行,将开发的 ### 快速开始 +#### 示例演示 +1. 编译demo示例,会在`zinx/example/zinx_server`下得到`server`, 在`zinx/example/zinx_client`下得到`client`. +```bash +$ cd zinx/ +$ make +``` +2. 启动Demo server, 该终端不要关闭 +```bash +$ cd example/zinx_server +$ ./server + + ██ + ▀▀ + ████████ ████ ██▄████▄ ▀██ ██▀ + ▄█▀ ██ ██▀ ██ ████ + ▄█▀ ██ ██ ██ ▄██▄ + ▄██▄▄▄▄▄ ▄▄▄██▄▄▄ ██ ██ ▄█▀▀█▄ + ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀ ▀▀ ▀▀▀ ▀▀▀ + +┌───────────────────────────────────────────────────┐ +│ [Github] https://github.com/aceld │ +│ [tutorial] https://www.kancloud.cn/aceld/zinx │ +└───────────────────────────────────────────────────┘ +[Zinx] Version: V0.11, MaxConn: 3, MaxPacketSize: 4096 +Add api msgId = 0 +Add api msgId = 1 +[START] Server name: zinx server Demo,listenner at IP: 127.0.0.1, Port 8999 is starting +Worker ID = 0 is started. +Worker ID = 1 is started. +Worker ID = 2 is started. +Worker ID = 3 is started. +Worker ID = 4 is started. +Worker ID = 7 is started. +Worker ID = 6 is started. +Worker ID = 8 is started. +Worker ID = 9 is started. +Worker ID = 5 is started. +start Zinx server zinx server Demo succ, now listenning... +... +``` + +3. 再打开新终端,启动client Demo测试通信 +```bash +$ cd example/zinx_client +$ ./client +==> Test Router:[Ping] Recv Msg: ID= 2 , len= 21 , data= DoConnection BEGIN... ==> Test Router:[Ping] Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping +==> Test Router:[Ping] Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping +==> Test Router:[Ping] Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping +... +t + +``` + #### server 基于Zinx框架开发的服务器应用,主函数步骤比较精简,最多只需要3步即可。 1. 创建server句柄 diff --git a/examples/zinx_client/main.go b/examples/zinx_client/main.go new file mode 100644 index 0000000..d4a05b4 --- /dev/null +++ b/examples/zinx_client/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "fmt" + "github.com/aceld/zinx/znet" + "io" + "net" + "time" +) + +/* + 模拟客户端 +*/ +func main() { + + conn, err := net.Dial("tcp", "127.0.0.1:8999") + if err != nil { + fmt.Println("client start err, exit!") + return + } + + for { + //发封包message消息 + dp := znet.NewDataPack() + msg, _ := dp.Pack(znet.NewMsgPackage(0, []byte("Zinx client Demo Test MsgID=0, [Ping]"))) + _, err := conn.Write(msg) + if err != nil { + fmt.Println("write error err ", err) + return + } + + //先读出流中的head部分 + headData := make([]byte, dp.GetHeadLen()) + _, err = io.ReadFull(conn, headData) //ReadFull 会把msg填充满为止 + if err != nil { + fmt.Println("read head error") + break + } + //将headData字节流 拆包到msg中 + msgHead, err := dp.Unpack(headData) + if err != nil { + fmt.Println("server unpack err:", err) + return + } + + if msgHead.GetDataLen() > 0 { + //msg 是有data数据的,需要再次读取data数据 + msg := msgHead.(*znet.Message) + msg.Data = make([]byte, msg.GetDataLen()) + + //根据dataLen从io中读取字节流 + _, err := io.ReadFull(conn, msg.Data) + if err != nil { + fmt.Println("server unpack data err:", err) + return + } + + fmt.Println("==> Test Router:[Ping] Recv Msg: ID=", msg.Id, ", len=", msg.DataLen, ", data=", string(msg.Data)) + } + + time.Sleep(1 * time.Second) + } +} diff --git a/examples/zinx_server/conf/zinx.json b/examples/zinx_server/conf/zinx.json new file mode 100644 index 0000000..e06991a --- /dev/null +++ b/examples/zinx_server/conf/zinx.json @@ -0,0 +1,9 @@ +{ + "Name":"zinx server Demo", + "Host":"127.0.0.1", + "TcpPort":8999, + "MaxConn":3, + "WorkerPoolSize":10, + "LogDir": "./mylog", + "LogFile":"zinx.log" +} diff --git a/examples/zinx_server/main.go b/examples/zinx_server/main.go new file mode 100644 index 0000000..f7f9e00 --- /dev/null +++ b/examples/zinx_server/main.go @@ -0,0 +1,60 @@ +/** +* @Author: Aceld +* @Date: 2020/12/24 00:24 +* @Mail: danbing.at@gmail.com +* zinx server demo + */ +package main + +import ( + "github.com/aceld/zinx/examples/zinx_server/zrouter" + "github.com/aceld/zinx/ziface" + "github.com/aceld/zinx/zlog" + "github.com/aceld/zinx/znet" +) + + +//创建连接的时候执行 +func DoConnectionBegin(conn ziface.IConnection) { + zlog.Debug("DoConnecionBegin is Called ... ") + + //设置两个链接属性,在连接创建之后 + zlog.Debug("Set conn Name, Home done!") + conn.SetProperty("Name", "Aceld") + conn.SetProperty("Home", "https://www.jianshu.com/u/35261429b7f1") + + err := conn.SendMsg(2, []byte("DoConnection BEGIN...")) + if err != nil { + zlog.Error(err) + } +} + +//连接断开的时候执行 +func DoConnectionLost(conn ziface.IConnection) { + //在连接销毁之前,查询conn的Name,Home属性 + if name, err := conn.GetProperty("Name"); err == nil { + zlog.Error("Conn Property Name = ", name) + } + + if home, err := conn.GetProperty("Home"); err == nil { + zlog.Error("Conn Property Home = ", home) + } + + zlog.Debug("DoConneciotnLost is Called ... ") +} + +func main() { + //创建一个server句柄 + s := znet.NewServer() + + //注册链接hook回调函数 + s.SetOnConnStart(DoConnectionBegin) + s.SetOnConnStop(DoConnectionLost) + + //配置路由 + s.AddRouter(0, &zrouter.PingRouter{}) + s.AddRouter(1, &zrouter.HelloZinxRouter{}) + + //开启服务 + s.Serve() +} diff --git a/examples/zinx_server/zrouter/hello.go b/examples/zinx_server/zrouter/hello.go new file mode 100644 index 0000000..f403e15 --- /dev/null +++ b/examples/zinx_server/zrouter/hello.go @@ -0,0 +1,24 @@ +package zrouter + +import ( + "github.com/aceld/zinx/ziface" + "github.com/aceld/zinx/zlog" + "github.com/aceld/zinx/znet" +) + +type HelloZinxRouter struct { + znet.BaseRouter +} + +//HelloZinxRouter Handle +func (this *HelloZinxRouter) Handle(request ziface.IRequest) { + zlog.Debug("Call HelloZinxRouter Handle") + //先读取客户端的数据,再回写ping...ping...ping + zlog.Debug("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData())) + + err := request.GetConnection().SendBuffMsg(1, []byte("Hello Zinx Router V0.10")) + if err != nil { + zlog.Error(err) + } +} + diff --git a/examples/zinx_server/zrouter/ping.go b/examples/zinx_server/zrouter/ping.go new file mode 100644 index 0000000..465afb5 --- /dev/null +++ b/examples/zinx_server/zrouter/ping.go @@ -0,0 +1,25 @@ +package zrouter + +import ( + "github.com/aceld/zinx/ziface" + "github.com/aceld/zinx/zlog" + "github.com/aceld/zinx/znet" +) + +//ping test 自定义路由 +type PingRouter struct { + znet.BaseRouter +} + +//Ping Handle +func (this *PingRouter) Handle(request ziface.IRequest) { + + zlog.Debug("Call PingRouter Handle") + //先读取客户端的数据,再回写ping...ping...ping + zlog.Debug("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData())) + + err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping")) + if err != nil { + zlog.Error(err) + } +} diff --git a/makefile b/makefile index 4932f88..62ffc52 100644 --- a/makefile +++ b/makefile @@ -1,11 +1,28 @@ -# gofmt格式化 -# run in terminal: -# make fmt -# win系统中,在git bash中如果出现make包没有找到 -# 管理员运行git bash,运行以下命令 -# choco install make -GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) +.PHONY: build -fmt: - gofmt -w $(GOFMT_FILES) +SERVICE := zinx +CUR_PWD := $(shell pwd) + +SERVER_DEMO_PATH := $(CUR_PWD)/examples/zinx_server +CLIENT_DEMO_PATH := $(CUR_PWD)/examples/zinx_client +SERVER_DEMO_BIN := $(SERVER_DEMO_PATH)/server +CLIENT_DEMO_BIN := $(CLIENT_DEMO_PATH)/client + +AUTHOR := $(shell git log --pretty=format:"%an"|head -n 1) +VERSION := $(shell git rev-list HEAD | head -1) +BUILD_INFO := $(shell git log --pretty=format:"%s" | head -1) +BUILD_DATE := $(shell date +%Y-%m-%d\ %H:%M:%S) + +export GO111MODULE=on + +LD_FLAGS='-X "$(SERVICE)/version.TAG=$(TAG)" -X "$(SERVICE)/version.VERSION=$(VERSION)" -X "$(SERVICE)/version.AUTHOR=$(AUTHOR)" -X "$(SERVICE)/version.BUILD_INFO=$(BUILD_INFO)" -X "$(SERVICE)/version.BUILD_DATE=$(BUILD_DATE)"' + +default: build + +build: + go build -ldflags $(LD_FLAGS) -gcflags "-N" -i -o $(SERVER_DEMO_BIN) $(SERVER_DEMO_PATH)/main.go + go build -ldflags $(LD_FLAGS) -gcflags "-N" -i -o $(CLIENT_DEMO_BIN) $(CLIENT_DEMO_PATH)/main.go +clean: + rm $(SERVER_DEMO_BIN) + rm $(CLIENT_DEMO_BIN) diff --git a/utils/globalobj.go b/utils/globalobj.go index 10decb8..c64c640 100644 --- a/utils/globalobj.go +++ b/utils/globalobj.go @@ -92,6 +92,10 @@ func (g *GlobalObj) Reload() { 提供init方法,默认加载 */ func init() { + pwd, err := os.Getwd() + if err != nil { + pwd = "." + } //初始化GlobalObject变量,设置一些默认值 GlobalObject = &GlobalObj{ Name: "ZinxServerApp", @@ -100,11 +104,11 @@ func init() { Host: "0.0.0.0", MaxConn: 12000, MaxPacketSize: 4096, - ConfFilePath: "conf/zinx.json", + ConfFilePath: pwd+"/conf/zinx.json", WorkerPoolSize: 10, MaxWorkerTaskLen: 1024, MaxMsgChanLen: 1024, - LogDir: "./log", + LogDir: pwd+"/log", LogFile: "", LogDebugClose: false, } diff --git a/znet/server.go b/znet/server.go index 733427c..144d746 100644 --- a/znet/server.go +++ b/znet/server.go @@ -44,6 +44,7 @@ type Server struct { 创建一个服务器句柄 */ func NewServer() ziface.IServer { + printLogo() s := &Server{ Name: utils.GlobalObject.Name, @@ -168,14 +169,18 @@ func (s *Server) CallOnConnStop(conn ziface.IConnection) { } } -func init() { + +func printLogo() { fmt.Println(zinxLogo) fmt.Println(topLine) fmt.Println(fmt.Sprintf("%s [Github] https://github.com/aceld %s", borderLine, borderLine)) - fmt.Println(fmt.Sprintf("%s [tutorial] https://www.jianshu.com/p/23d07c0a28e5 %s", borderLine, borderLine)) + fmt.Println(fmt.Sprintf("%s [tutorial] https://www.kancloud.cn/aceld/zinx %s", borderLine, borderLine)) fmt.Println(bottomLine) fmt.Printf("[Zinx] Version: %s, MaxConn: %d, MaxPacketSize: %d\n", utils.GlobalObject.Version, utils.GlobalObject.MaxConn, utils.GlobalObject.MaxPacketSize) } + +func init() { +} diff --git a/ztimer/timerscheduler_test.go b/ztimer/timerscheduler_test.go index 714f270..16c8303 100644 --- a/ztimer/timerscheduler_test.go +++ b/ztimer/timerscheduler_test.go @@ -65,16 +65,15 @@ func TestNewAutoExecTimerScheduler(t *testing.T) { select {} } - //测试取消一个定时器 func TestCancelTimerScheduler(t *testing.T) { Scheduler := NewAutoExecTimerScheduler() - f1 := NewDelayFunc(foo, []interface{}{3, 3}) - f2 := NewDelayFunc(foo, []interface{}{5, 5}) - timerId1,_:=Scheduler.CreateTimerAfter(f1, time.Duration(3)*time.Second) - timerId2,_:=Scheduler.CreateTimerAfter(f2, time.Duration(5)*time.Second) - log.Printf("timerId1=%d ,timerId2=%d\n",timerId1,timerId2) - Scheduler.CancelTimer(timerId1) //删除timerId1 + f1 := NewDelayFunc(foo, []interface{}{3, 3}) + f2 := NewDelayFunc(foo, []interface{}{5, 5}) + timerId1, _ := Scheduler.CreateTimerAfter(f1, time.Duration(3)*time.Second) + timerId2, _ := Scheduler.CreateTimerAfter(f2, time.Duration(5)*time.Second) + log.Printf("timerId1=%d ,timerId2=%d\n", timerId1, timerId2) + Scheduler.CancelTimer(timerId1) //删除timerId1 //阻塞等待 select {}