Home: https://yuque.com/aceld
Discord : https://discord.gg/xQ8Xxfyfcz
A lightweight concurrent server framework based on Golang.
Home Page: https://github.com/aceld/zinx/wiki
License: MIT License
Discord : https://discord.gg/xQ8Xxfyfcz
在 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
}
}
client
与server
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
感觉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")
})
}
worker池处理业务时是否会存在消息序列化的问题,如果一个玩家的多个消息需要按顺序处理,worker池好像不能保证先来的消息先处理完
#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
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,可能会造成数据竞争,请问是有这个问题吗?
[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)
}
}
如题,感觉网络库这些东西,经历过实战,大家才敢用~~嘿嘿
每个handle里的数据都不一样,如果不是write一个简单的字符串,比如是另外的proto序列化的数据,怎么能做到统一处理而不用每个handle里都要把数据序列化一遍
服务端收到数据 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
}
不知道什么原因client.exe在win64位运行不起来,不知道是不是调了分辨率的问题,只全屏运行成功过一次,后来每次都是闪退
我在两台win7 64上都运行不起来,不知道有没有log可以查看?
此处go了一个新协程是可以省略的,dealConn.Start() 中又go了读写两个协程就可以了,如果怕CallOnConnStart有慢操作影响accept速度,可以把CallOnConnStart移到读协程中去执行
go dealConn.Start()
pack:binary.Write(dataBuff, binary.LittleEndian, msg.GetMsgId())
unpack:binary.Read(dataBuff, binary.LittleEndian, &msg.DataLen)
这种方式的话,客户端是不是需要对应也要用小端字节序处理,go里没有本地字节序和网络字节序相互转换的方法吗?这个难道就这样处理吗?不太懂,求大佬解释一下...
你好老师,
请问 服务端能否感知到客户端意外断开连接?
当客户端意外退出时,服务端需要断开接连释放资源,请问能否感知到客户端意外断开连接呢?
看到简书上一个人指出,但觉得修改方式不好,坐等作者大佬修复
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())
}`
简书教程只到第十章,大神有时间能更新课程吗?
Zinx可直接使用TCP二进制传输吗。
有Demo例子吗
分布式 啥时有?
无法执行断线时的Hook函数。
如链接号小于工作池,则输出了
wsasend: An established connection was aborted by the software in your host machine.
Conn Writer exit
例如 工作池大小为10,第十个用户会输出上述 从第11个开始 程序既不能检测到断线 也不能对传入数据进行处理。
且任何一个链接断开都无法执行设定的 SetOnConnStop
该如何解决?
看了好几篇代码,在写连接模块中,出错了会调用 c.stop。如果一种情况,客户端不传数据过来。而服务器主动发数据给客户端出错了。看到代码connection.go 70行中直接跳出staartwrite。为什么不在这里用chan通知写关闭。按我的理解,读出出错了,调用stop,那么写出错了,也应该 调用stop了
到处找,但是没找到~
Line 131 in 966f1f8
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
//获取包头长度方法 func(dp *DataPack) GetHeadLen() uint32 { //Id uint32(4字节) + DataLen uint32(4字节) return 8 }
uint32 在64位机器上是 64bits 8个字节了。
比如说服务端挂了,客户端会一直等到客户端上线,然后重连。
golang开发者使用linux的还是蛮多的吧,linux用qq实在是不方便,可以在项目下面留tg群组吗?
zinx成为一个支持
“基于Zinx框架开发的服务器应用,主函数步骤比较精简,最多主需要3步即可。”
应该是“最多只需要3步”吧
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类似改法。
mmo_game的client目前是已经编译好的应用,无法看到客户端的实现,感觉这部分缺失了,导致mmo_game这一demo不完整。而且编译的应用也容易因为兼容性等问题导致无法运行
游戏框架性能是有所要求的。go的 GC一直被诟病,是否可以采用sync.Pool进行优化
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一直执行不了。
请问下几时可以 弄一个websocket版本?
ettings -> Go Modules -> click (Enable Go Modules (vgo) integration, Vendoring mode)
if you are in mainland China, the proxy is: https://goproxy.cn,direct
例子中的PingRouter
和HelloZinxRouter
应该是不同的业务逻辑处理。
如果在业务处理中有耗时或者阻塞的操作,根据这里的写法,MsgHandle, 这个业务处理流程岂不是接收不到后面的请求了?
希望得到大佬的回复。谢谢!
当客户端是用其他框架甚至语言时,Server端Message的格式无法兼容,建议将Message的格式做成支持用户自定义的格式。
挺好的框架,非常好的教程,看得出来作者真的很用心,瞬间粉。但是从早9点到现在11:30,clone好多遍都失败(期间试过vpn,加大git curl缓存)。建议zinx只留server的内容,客户端dem独立一个git项目
错误写法:
module zinx
项目引入zinx报错
module declares its path as: zinx
but was required as: github.com/aceld/zinx
==================================
正确写法应该是:
module github.com/aceld/zinx
既然每个cli的链接都会创建一个goroutine,干嘛非要再用消息队列来处理消息,直接在conn的goroutine里处理消息不就可以了。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.