2017-06-15 4 views
0

Ich schreibe einen Spieleserver und da dies das erste Mal ist, frage ich mich, wie ich Pakete an den Client senden kann, ohne dem Server nachzuhängen.
Auch wenn der Client zurückgeblieben ist, sollten Pakete an sie gesendet werden. (Nicht sicher, ob dies das Richtige ist, aber wenn ich keine Pakete an sie sende, ist der Client nicht synchron) Also hier ist meine Idee zuerst:Wie kann ich Daten über TCP an einen Client senden, ohne den Server zu blockieren?

Jeder Spieler bekommt 2 goroutines wenn sie verbinden: eine zum Senden und andere zum Empfangen.

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

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

// in server player's sending loop (in their own goroutine) 
for { 
    packet := <- p.sendChan: 
    sendPacket(packet) // this will block 
} 

Also hier die mainloop des Servers kann, ohne blockieren, höchstens 100 Pakete an den Player-Kanal senden, während die Sendpacket blockiert (wegen vielleicht hinken).
Aber das Problem ist, wenn der Spieler nach 100 Paketen blockiert, wird der Server stoppen. Das ist offensichtlich schlecht. Und Go hat keine Möglichkeit, unbegrenzte Kanäle anzugeben.

Dann dachte ich über eine neue gorouting für jedes sendPacket, aber das scheint, als würde es zu viel Systemressourcen verschwenden und die ganze Sache langsam machen.

Die Frage ist also: Was ist der beste Weg? Ich denke nicht, dass der Server Ressourcen für einen verzögerten Client verschwenden sollte, aber gleichzeitig sollten sie alle Pakete gesendet werden. Gibt es einen besseren Weg, dies zu tun? (Ich bin nicht sicher, wie es in der realen Welt getan hat, so andere Lösungen zu fein sein würde)

+0

Dies hängt von den Anforderungen Ihres Spiels bezüglich Paketbestellung, Paketverlust usw. ab. Eine Lösung besteht darin, den Server aggressiv zu schützen, indem die langsamen Benutzer getrennt werden. Wenn Sie keine Verbindungen trennen und eine beliebige Paketreihenfolge verarbeiten möchten, können Sie darauf zurückgreifen, die Anforderung an ein Segment anzuhängen (geschützt durch einen Mutex), wobei eine sekundäre Goroutine diese konsumiert und versucht, sie zu senden. – ANisus

+0

Verwenden Sie einen gepufferten Writer. – Adrian

+0

Im [Gorolla Chat-Beispiel] (https://github.com/gorilla/websocket/tree/master/examples/chat#chat-example) finden Sie eine Möglichkeit, das Problem zu lösen. Das Beispiel schreibt in eine Websocket-Verbindung. Rippen Sie die Websockets aus und ersetzen Sie sie durch direkte Schreibvorgänge in TCP-Verbindungen. –

Antwort

1

versuchen, diesen Ansatz auf der Grundlage der Gorilla Chat Example:

Im Server-Hauptschleife:

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

     // Remove the player from server's collection of players 
     // to avoid sending to closed channel. 
     ... 

     // Let the player's sending loop close the connection 
     // and do other cleanup. 
} 

Dies ist sendChan:

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

in Server-Spieler Sendeschleife (in ihrer eigenen 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's collection, close the connection and 
// do any necessary cleanup for the player. 
... 
Verwandte Themen