Coder Social home page Coder Social logo

aceld / zinx Goto Github PK

View Code? Open in Web Editor NEW
6.8K 158.0 1.2K 36.78 MB

A lightweight concurrent server framework based on Golang.

Home Page: https://github.com/aceld/zinx/wiki

License: MIT License

Go 99.76% Shell 0.01% Makefile 0.24%
go zinx tcp-server game-server golang

zinx's Introduction

zinx's People

Contributors

aceld avatar adsian avatar carl549401 avatar chengkunxf avatar chenhaohu avatar clukboy avatar cyj19 avatar dawnzzz avatar dduo518 avatar fivezjd avatar graydovee avatar gufeijun avatar hcram41 avatar jibadboy avatar kstwoak avatar lbbniu avatar li-guojie avatar moonlight6666 avatar nichtsen avatar trash-boy avatar valiner avatar wahello avatar waylennn avatar wukongyong avatar wzy2687 avatar xm-tech avatar xuhaowang avatar xxl6097 avatar yanhedoki avatar yilinershi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zinx's Issues

字节序问题

pack:binary.Write(dataBuff, binary.LittleEndian, msg.GetMsgId())
unpack:binary.Read(dataBuff, binary.LittleEndian, &msg.DataLen)
这种方式的话,客户端是不是需要对应也要用小端字节序处理,go里没有本地字节序和网络字节序相互转换的方法吗?这个难道就这样处理吗?不太懂,求大佬解释一下...

关于消息序列化的问题

worker池处理业务时是否会存在消息序列化的问题,如果一个玩家的多个消息需要按顺序处理,worker池好像不能保证先来的消息先处理完

在connection.go中StartWriter为什么不用一个chan通知写线程关闭

看了好几篇代码,在写连接模块中,出错了会调用 c.stop。如果一种情况,客户端不传数据过来。而服务器主动发数据给客户端出错了。看到代码connection.go 70行中直接跳出staartwrite。为什么不在这里用chan通知写关闭。按我的理解,读出出错了,调用stop,那么写出错了,也应该 调用stop了

关于获取head len 问题

//获取包头长度方法 func(dp *DataPack) GetHeadLen() uint32 { //Id uint32(4字节) + DataLen uint32(4字节) return 8 }
uint32 在64位机器上是 64bits 8个字节了。

无法执行断线时的Hook函数。

无法执行断线时的Hook函数。
如链接号小于工作池,则输出了
wsasend: An established connection was aborted by the software in your host machine.
Conn Writer exit

例如 工作池大小为10,第十个用户会输出上述 从第11个开始 程序既不能检测到断线 也不能对传入数据进行处理。

任何一个链接断开都无法执行设定的 SetOnConnStop
该如何解决?

SendBuffMsg存在安全问题.

c.msgBuffChan <- msg
//在大并发高负载的情况下 经常因为这句崩溃,原因是向一个关闭的chan写入数据,
//可能是在“将data封包,并且发送”的时候chan被其他携程关闭了 所有在发送前最后再检查一下最好!

func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error {
if c.isClosed == true {
return errors.New("Connection closed when send buff msg")
}
//将data封包,并且发送
dp := NewDataPack()
msg, err := dp.Pack(NewMsgPackage(msgId, data))
if err != nil {
fmt.Println("Pack error msg id = ", msgId)
return errors.New("Pack error msg ")
}

//写回客户端
c.msgBuffChan <- msg  

return nil

}

错误日志:

---> CallOnConnStart....
[Writer Goroutine is running]
[Reader Goroutine is running]
read msg head error EOF
Conn Stop()...ConnID = 156
---> CallOnConnStop....
Send Buff Data error:, write tcp4 172.21.0.15:7777->39.189.45.248:14870: use of closed network connection Conn Writer exit
39.189.45.248:14870 [conn Writer exit!]
connection Remove ConnID= 156 successfully: conn num = 0
39.189.45.248:14870 [conn Reader exit!]
panic: send on closed channel

goroutine 36 [running]:
github.com/aceld/zinx/znet.(*Connection).SendBuffMsg(0xc0000d00e0, 0x1f4, 0xc0003f9400, 0x95, 0xa0, 0x0, 0x0)
/Users/jamie/go/pkg/mod/github.com/aceld/[email protected]/znet/connection.go:226 +0x1ec
YmServer/tools.Response(0xac40c0, 0xc000524a20, 0x1000001f4, 0x9fc41e, 0x2, 0xc000515470)
/Users/jamie/go/src/YmServer/tools/toos.go:35 +0x10d
YmServer/controller.(*GetMyGameProxyRouter).Handle(0xe84c88, 0xac40c0, 0xc000524a20)
/Users/jamie/go/src/YmServer/controller/my_game.go:72 +0x517
github.com/aceld/zinx/znet.(*MsgHandle).DoMsgHandler(0xc000217080, 0xac40c0, 0xc000524a20)
/Users/jamie/go/pkg/mod/github.com/aceld/[email protected]/znet/msghandler.go:47 +0xe4
github.com/aceld/zinx/znet.(*MsgHandle).StartOneWorker(0xc000217080, 0x6, 0xc000234720)
/Users/jamie/go/pkg/mod/github.com/aceld/[email protected]/znet/msghandler.go:70 +0x103
created by github.com/aceld/zinx/znet.(*MsgHandle).StartWorkerPool
/Users/jamie/go/pkg/mod/github.com/aceld/[email protected]/znet/msghandler.go:83 +0x50

非常建议往一个全面的连接层这个目标发展

zinx成为一个支持

  • tcp
  • udp
  • kcp-udp
  • http
  • websocket
    的服务器框架
    为上述链接模式提供统一的抽象逻辑
    这样zinx便能获得非常广泛的使用范围
    比如, 实现一个ECS State sync的kcp-udp游戏服务器😉

您好,我客户端使用proto发送数据,服务端收到错误信息

服务端收到数据 unpack error Too large msg data recieved
数据量很小,但是为什么会报出这样的提示。

pkgMsg := &bean.PkgMsg{
Type: 1,
TaskID: "taskID------111", //php客户端分配的渲染任务ID唯一标识
PkgUrl: "PkgUrl", //用户素材包的下载地址
PkgMd5: "PkgMd5", //用户素材包的Md5用作校验
}

	msg, err := proto.Marshal(pkgMsg)
	fmt.Println(msg)
	if err != nil {
		fmt.Println("marshal msg err: ", err)
		// return
	}

	indexN, err := conn.Write(msg)
	if err != nil {
		fmt.Println("write error err ", err)
		// return
	}

简书教程

简书教程只到第十章,大神有时间能更新课程吗?

建议把mmo_game独立出来,以加快clone的成功率

挺好的框架,非常好的教程,看得出来作者真的很用心,瞬间粉。但是从早9点到现在11:30,clone好多遍都失败(期间试过vpn,加大git curl缓存)。建议zinx只留server的内容,客户端dem独立一个git项目

mmo_game的client支持开源吗?

mmo_game的client目前是已经编译好的应用,无法看到客户端的实现,感觉这部分缺失了,导致mmo_game这一demo不完整。而且编译的应用也容易因为兼容性等问题导致无法运行

handle里的数据问题

每个handle里的数据都不一样,如果不是write一个简单的字符串,比如是另外的proto序列化的数据,怎么能做到统一处理而不用每个handle里都要把数据序列化一遍

关于消息队列的问题

既然每个cli的链接都会创建一个goroutine,干嘛非要再用消息队列来处理消息,直接在conn的goroutine里处理消息不就可以了。

timer包里,用函数值代替变参函数怎么样?

感觉delayfunc哪里用函数值实现可能更好
让调用者自己生成一个匿名函数,匿名函数里调用自己想调用的函数
用变参有点不灵活

type Callable func()

func docall(c Callable){
	c()
}

func Sayhello(s string) {
	fmt.Println(s)
}

func TestF(t *testing.T) {
	docall(func() {
		Sayhello("nihao")
	})
}

有个疑问,这种情况需要加锁吗?

func (m *Move) Handle (request ziface.IRequest){
//1 解析客户端传递过来的protobuf
protoMsg := &pb.Position{}
err := proto.Unmarshal(request.GetData(), protoMsg)
if err != nil {
fmt.Printf("move router proto unmarshal error:%v\n",err)
return
}
//2 得到当前发送位置的是哪个玩家
pid, err := request.GetConnection().GetProperty("pid")
if err != nil {
fmt.Printf("move router get pid error:%v\n",err)
return
}

//3 获取player对象
player := core.WorldMgrObj.GetPlayerByPid(pid.(int32))


//4 广播并更新当前玩家的位置
player.UpdatePos(protoMsg.X ,protoMsg.Y, protoMsg.Z, protoMsg.V)

}
您好 ,我有个疑问,想第3步获取了player对象,这个时候修改player对象需要加锁吗 ,我理解是需要加锁的,因为在此时可能有其他协程在读取player对象的数据,这个时候我们修改player,可能会造成数据竞争,请问是有这个问题吗?

connmanager的ClearConn会导致死锁问题

看到简书上一个人指出,但觉得修改方式不好,坐等作者大佬修复
Stop方法中会调用ConnManager的Remove方法,此时map锁还没有解锁

`//清除并停止所有连接
func (connMgr *ConnManager) ClearConn() {
//保护共享资源Map 加写锁
connMgr.connLock.Lock()
defer connMgr.connLock.Unlock()

//停止并删除全部的连接信息
for connID, conn := range connMgr.connections {
	//停止
	conn.Stop()    // Stop方法中会调用ConnManager的Remove方法,此时map锁还没有解锁
	//删除
	delete(connMgr.connections, connID)
}

fmt.Println("Clear All Connections successfully: conn num = ", connMgr.Len())

}`

细节

  1. globalobj.go 中init已经调用Reload了

image

  1. 这个地方是不是直接用g参数
    image

关于`MsgHandle`的一点疑问

例子中的PingRouterHelloZinxRouter应该是不同的业务逻辑处理。
如果在业务处理中有耗时或者阻塞的操作,根据这里的写法,MsgHandle, 这个业务处理流程岂不是接收不到后面的请求了?

希望得到大佬的回复。谢谢!

请问我按照readme里面运行服务报错

[root@server ]$ go run main.go server.go
go: finding github.com/aceld/zinx/ziface latest
go: finding github.com/aceld/zinx latest
go: finding github.com/aceld/zinx/znet latest
go: downloading github.com/aceld/zinx v0.0.0-20191210110905-0a663b2d6b15
go: extracting github.com/aceld/zinx v0.0.0-20191210110905-0a663b2d6b15
build command-line-arguments: cannot load zinx/utils: cannot find module providing package zinx/utils

package main

import (
"github.com/aceld/zinx/znet"
)

func main() {
//1 创建一个server句柄
s := znet.NewServer()

//2 配置路由
s.AddRouter(0, &PingRouter{})

//3 开启服务
s.Serve()

}

package main

import (
"fmt"

"github.com/aceld/zinx/ziface"
"github.com/aceld/zinx/znet"

)

type PingRouter struct {
znet.BaseRouter
}

func (this *PingRouter) Handle(request ziface.IRequest) {
fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))

err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))
if err != nil {
	fmt.Println(err)
}

}

优化/修复项建议

  1. znet/server.go

此处go了一个新协程是可以省略的,dealConn.Start() 中又go了读写两个协程就可以了,如果怕CallOnConnStart有慢操作影响accept速度,可以把CallOnConnStart移到读协程中去执行

go dealConn.Start()

MD 文档

“基于Zinx框架开发的服务器应用,主函数步骤比较精简,最多主需要3步即可。”
应该是“最多只需要3步”吧

mmo里的锁安全问题

mmo里getplayer是读锁。
image
里面取出player就直接修改player 的数据,难道不会有并发写的问题吗?

还有玩家交互的需求,比如交易,pk扣血,要同时改变多个player的数据,不能用只读锁吧?

还有AOIManager 里的grids map[int]*Grid的相关操作,也没有加锁

这些感觉都会出并发锁的问题啊,压力测试里,难道没有出现吗?希望作者能大概说说这里面的处理

go.mod 写法有误,导致工程引用失败

错误写法:
module zinx

项目引入zinx报错
module declares its path as: zinx
but was required as: github.com/aceld/zinx

==================================
正确写法应该是:
module github.com/aceld/zinx

v0.2 的demo有点疑问

zinx/znet/connection.go

/* 处理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中
        buf := make([]byte, 512)
        cnt, err := c.Conn.Read(buf)
        if err != nil {
            fmt.Println("recv buf err ", err)
            c.ExitBuffChan <- true
            continue
        }
        //调用当前链接业务(这里执行的是当前conn的绑定的handle方法)
        if err := c.handleAPI(c.Conn, buf, cnt); err !=nil {
            fmt.Println("connID ", c.ConnID, " handle is error")
            c.ExitBuffChan <- true
            return
        }
    }
}

这里的读取失败不是应该用 return吗。。用 continue 岂不是2个defer一直执行不了。

不应该合并#48的代码

#48 中做的修改是完全不需要的
go.mod 文件只编译的时候有用,从git仓库拉取包并不是按照go.mod中的名字进行的
go.mod文件中 module github.com/aceld/zinx这个名字使用zinx也是可以的
在zinx下面的包互相引用,不需要吧仓库地址也带上, import "zinx/ziface" 的引用方式是正确的

在新的项目中,如果用到zinx,包的导入应该这么写

import (
	"github.com/aceld/zinx/znet"
        "github.com/aceld/zinx/ziface"
)

zinx不是包,只是一个文件夹,go的包都必须在目录里包含.go文件,且有package 包名

另外:不需要跟踪go.sum文件
建议按照va.b.c的格式发布一个release,否则在go mod tidy拉取的时候,都是按最后一次提交拉取,这会产生变化,go.mod文件为的就是保证版本一致,没有release的情况下版本总是v0.0.0

可以添加TG群组吗?

golang开发者使用linux的还是蛮多的吧,linux用qq实在是不方便,可以在项目下面留tg群组吗?

调用server.Stop()时无法正常终止程序。

变动

server.Serve()中添加了如下代码:

//运行服务
func (s *Server) Serve() {
	s.Start()
	//TODO Server.Serve() 是否在启动服务的时候 还要处理其他的事情呢 可以在这里添加
	//阻塞,否则主Go退出, listenner的go将会退出
	exits := make(chan os.Signal, 1)
	signal.Notify(exits, os.Interrupt, os.Kill)
	select {
	case <-exits:
		s.Stop()
		return
	}
}

异常情景

clientserver socket正常通信,在终端中手动停止掉 go run server.go, client方日志显示正常的socket关闭,但是在server这里执行connection.Close()卡住。

终端的一些日志:

connection add to ConnManager successfully: conn num =  1
[Writer Goroutine is running]
[Reader Goroutine is running]
^C[STOP] Zinx server , name  ZinxServerApp
Conn Stop()...ConnID =  0
127.0.0.1:54887 [conn Writer exit!]
read msg head error  read tcp4 127.0.0.1:8999->127.0.0.1:54887: use of closed network connection
Conn Stop()...ConnID =  0
127.0.0.1:54887 [conn Reader exit!]
^C^C^C^C^C^C^C^C^Z
[1]  + 43852 suspended  go run server.go

感觉可以优化的地方

znet/connection.go 第151 和第164行,可以接口断言下用户是否写了hook函数,再决定是否要调用:例如可以把第164行的:c.TcpServer.CallOnConnStop(c) 替换成下面的代码:
ival := reflect.ValueOf(c.TcpServer).Interface()
if _,ok := ival.(*Server);ok{
if val.OnConnStop!=nil{//这一句是判断用户有没有写钩子函数
c.Server.CallOnConnStop(c)
}
}
同理151类似改法。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.