如何在不阻塞服务器的情况下向客户端发送数据通过TCP?

huangapple go评论75阅读模式
英文:

How to send data to a client over tcp without blocking the server?

问题

我正在编写一个游戏服务器,由于这是我第一次,我一直在思考如何在不使服务器延迟的情况下向客户端发送数据包。
即使客户端延迟,也应该向其发送数据包。(不确定这是否正确,但如果不向其发送数据包,客户端将无法同步)
所以这是我最初的想法:

每个玩家在连接时都会获得两个goroutine:一个用于发送,另一个用于接收。

// 在服务器的主循环中
select {
case player.sendChan <- somepacket:
default:
}

// 这是sendChan:
p.sendChan := make(chan Packet, 100)

// 在服务器玩家的发送循环中(在它们自己的goroutine中)
for {
packet := <- p.sendChan:
sendPacket(packet) // 这将阻塞
}

所以在这里,服务器的主循环最多可以向玩家通道发送100个数据包而不会阻塞,而sendPacket是阻塞的(可能是由于延迟)。
但问题是,如果玩家在发送了100个数据包后阻塞,服务器将停止。这显然是不好的。而且Go语言没有指定无界通道的方法。

然后我考虑为每个sendPacket启动一个新的goroutine,但这似乎会浪费太多系统资源并使整个过程变慢。

所以问题是:什么是最好的方法?我认为服务器不应该为延迟的客户端浪费资源,但同时,它们应该接收到所有数据包。
是否有更好的方法来做到这一点?(我不确定在现实世界中是如何做的,所以任何其他解决方案也可以)

英文:

I'm writing a game server and as this is my first time, I've been wondering how to send packets to the client without lagging the server.
Even if the client is lagging, packets should be sent to them. (Not sure if this is the right thing to do, but if I don't send packets to them, the client won't be in sync)
So here's my idea at first:

Each player gets 2 goroutines when they connect: one for sending and other for receiving.

// in the server main loop
select {
	case player.sendChan &lt;- somepacket:
	default:
}

// this is sendChan:
p.sendChan := make(chan Packet, 100)

// in server player&#39;s sending loop (in their own goroutine)
for {
	packet := &lt;- p.sendChan:
	sendPacket(packet) // this will block
}

So here the server's mainloop can send at most 100 packets to the player channel without blocking, while the sendPacket is blocking (due to lag maybe).
But the problem is if the player is blocking after 100 packets, the server will stop. That is obviously bad. And Go has no way to specify unbounded channels.

Then I thought about launching a new gorouting for each sendPacket but that seems like it would waste too much system resources and make the whole thing slow.

So the question is: What is the best way? I don't think the server should waste resources for a laggy client, but at the same time, they should be sent all packets.
Is there a better way to do this? (I'm not sure how it's done in the real world so any other solutions would be fine too)

答案1

得分: 1

尝试使用基于Gorilla Chat Example的方法:

在服务器的主循环中:

select {
    case player.sendChan <- somepacket:
    default:
        // 玩家无法接收数据包。关闭通道以通知玩家退出。
        close(player.sendChan)

        // 从服务器的玩家集合中移除该玩家,以避免向关闭的通道发送数据。
        ...

        // 让玩家的发送循环关闭连接并进行其他清理工作。
}

这是sendChan:

p.sendChan := make(chan Packet, 100)

在服务器玩家的发送循环中(在它们自己的goroutine中):

// 当p.sendChan关闭时,循环将退出。
for packet := range p.sendChan {
    // 始终在截止时间内写入。
    p.conn.SetWriteDeadline(time.Now().Add(writeWait))
    err := sendPacket(packet)
    // 在任何错误时跳出写入循环。
    if err != nil {
        break
    }
}
// 在发送数据包出错或p.sendChan关闭时,我们到达此处。
// 从服务器的集合中移除该玩家,关闭连接并进行任何必要的清理工作。
...
英文:

Try this approach based on the Gorilla Chat Example:

In the server main loop:

select {
	case player.sendChan &lt;- somepacket:
	default:
       // The player cannot receive the packet. Close channel to
       // signal the player to exit. 
       close(player.sendChan)

       // Remove the player from server&#39;s collection of players
       // to avoid sending to closed channel.
       ...

       // Let the player&#39;s sending loop close the connection 
       // and do other cleanup.
}

This is sendChan:

p.sendChan := make(chan Packet, 100)

In server player's sending loop (in their own goroutine):

// Loop will exit when p.sendChan is closed.
for packet := range p.sendChan {
    // Always write with a deadline.
    p.conn.SetWriteDeadline(time.Now().Add(writeWait))
	err := sendPacket(packet)
    // Break out of write loop on any error.
    if err != nil {
       break
    }
}
// We reach this point on error sending packet or close on p.sendChan.
// Remove the player from the server&#39;s collection, close the connection and
// do any necessary cleanup for the player.
...

huangapple
  • 本文由 发表于 2017年6月15日 20:21:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/44567386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定