设想这样一个应用场景:一个网游登录服务器的实现里,每个玩家的连接用一个goroutine来处理,有一个主动对象AccountServer代表帐号服务器,AccountServer会接收每个玩家的请求发送给帐号服务器验证合法性,然后把返回的结果分发给各个玩家。同时每个玩家goroutine在等待帐号验证的过程中需要阻塞等待。
利用缓冲信道可以比较容易地实现这个特性。以下就是大致的代码结构,其中SendAndReceive函数被玩家goroutine调用并阻塞等待结果。该函数中利用缓冲信道来获取一个用于获得结果的信道,使用之后再回收。
type Msg struct { data []byte ch chan []byte } type Connection interface { Write([]byte) Read() []byte } type AccountServer struct { conn Connection // 与帐号数据库服务器的网络链接 tokens chan chan []byte msg chan Msg } func NewAccountServer(conn Connection, maxclientcount int) *AccountServer { p := &AccountServer{} p.conn = conn p.tokens = make(chan chan []byte, maxclientcount) p.msg = make(chan Msg, maxclientcount) for i := 0; i < maxclientcount; i++ { p.tokens <- make(chan []byte) } go p.run() return p } func (p *AccountServer) run() { rch := make(chan []byte) sch := make(chan []byte) go func() { for {p.conn.Write(<-sch)} }() go func() { for {rch <- p.conn.Read()} }() p.manage(sch, rch) } func (p *AccountServer) manage(sch chan<- []byte, rch <-chan []byte) { var id uint32 players := make(map[uint32]chan []byte) for { select { case msg := <-p.msg: id++ // 在数据包前面附上一个uint32,用于标识发送数据的玩家 buff := make([]byte, 4+len(msg.data)) buff[0] = byte(id & 0xff) buff[1] = byte((id >> 8) & 0xff) buff[2] = byte((id >> 16) & 0xff) buff[3] = byte((id >> 24) & 0xff) copy(buff[4:], msg.data) sch <- buff players[id] = msg.ch case data := <-rch: if len(data) <= 4 { break } // 从帐号数据库服务器返回的数据前四个字节会附带同样的uint32,,用于标识玩家 var key uint32 key = uint32(data[0]) key |= uint32(data[1]) << 8 key |= uint32(data[2]) << 16 key |= uint32(data[3]) << 24 ch, ok := players[key] if ok { ch <- data[4:] } } } } // 玩家对应的goroutine调用此函数向帐号服务器发送数据并等待返回 func (p *AccountServer) SendAndReceive(data []byte) []byte { // 获取一个用于获取返回数据的信道 ch := <-p.tokens // 回收信道 defer func() { p.tokens <- ch }() p.msg <- Msg{data, ch} return <-ch }
本文分享自微信公众号 - Golang语言社区(Golangweb),作者:stevewang
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2016-08-18
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句