Okay, zunächst einmal, wenn Sie etwas haben und es funktioniert, ist es normalerweise eine gute Idee, es so zu lassen. Warum reparieren, was nicht kaputt ist?
Aber wenn Sie Probleme haben und Ihren Netzwerkcode wirklich neu schreiben möchten, haben Sie meiner Meinung nach vier Hauptoptionen:
- Multithread-Blockierungscode (was Sie gerade tun)
- Nicht blockierende Sockets mit Level-Trigger-Benachrichtigung
- Nicht blockierende Steckdosen mit Benachrichtigung über Bereitschaftsänderungen
- Asynchrone Sockets
Nachdem ich viele Multiplayer-Clients und -Server (von Peer-to-Peer bis hin zu Massively Multiplayer) geschrieben habe, denke ich, dass Option 2 zu der geringsten Komplexität mit ziemlich guter Leistung sowohl für den Server- als auch für den Client-Teil des Spiels führt. Als knappe Sekunde würde ich Option 4 wählen, aber dafür müssen Sie normalerweise Ihr gesamtes Programm überdenken, und ich finde es meistens nützlich für Server und nicht für Clients.
Insbesondere möchte ich davon abraten, Sockets in einer Multithread-Umgebung zu blockieren, da dies normalerweise zu Sperr- und anderen Synchronisierungsfunktionen führt, die nicht nur die Komplexität des Codes erheblich erhöhen, sondern auch dessen Leistung beeinträchtigen können, da einige Threads darauf warten Andere.
Vor allem aber sind die meisten Socket-Implementierungen (und die meisten E / A-Implementierungen dazu) auf der niedrigsten Ebene nicht blockierend. Blockierungsvorgänge werden einfach bereitgestellt, um die Entwicklung trivialer Programme zu vereinfachen. Wenn ein Socket blockiert, ist die CPU in diesem Thread vollständig inaktiv. Warum also eine nicht blockierende Abstraktion über die blockierende Abstraktion über eine bereits nicht blockierende Aufgabe erstellen?
Die nicht blockierende Socket-Programmierung ist etwas entmutigend, wenn Sie sie nicht ausprobiert haben, aber es stellt sich heraus, dass sie recht einfach ist. Wenn Sie bereits Eingabeabfragen durchführen, haben Sie bereits die Einstellung, nicht blockierende Sockets durchzuführen.
Das erste, was Sie tun möchten, ist, den Socket auf nicht blockierend zu setzen. Das machst du mit fcntl()
.
Danach, bevor Sie zu tun send()
, recv()
, sendto()
, recvfrom()
, accept()
( connect()
ist ein bisschen anders) oder andere Anrufe, die den Thread blockieren könnten, rufen Sie select()
an der Steckdose. select()
Hier erfahren Sie, ob eine nachfolgende Lese- oder Schreiboperation für den Socket ausgeführt werden kann, ohne dass dieser blockiert. In diesem Fall können Sie den gewünschten Vorgang sicher ausführen, und der Socket wird nicht blockiert.
Dies in ein Spiel aufzunehmen ist ganz einfach. Wenn Sie bereits eine Spielschleife haben, zum Beispiel wie folgt:
while game_is_running do
poll_input()
update_world()
do_sounds()
draw_world()
end
Sie können es so ändern, dass es so aussieht:
while game_is_running do
poll_input()
read_network()
update_world()
do_sounds()
write_network()
draw_world()
end
wo
function read_network()
while select(socket, READ) do
game.net_input.enqueue(recv(socket))
end
end
und
function write_network()
while not game.net_output.empty and select(socket, WRITE) do
send(socket, game.net_output.dequeue())
end
end
In Bezug auf die Ressourcen ist das einzige Buch, von dem ich denke, dass jeder es in seinen Bücherregalen haben muss, auch wenn es das einzige Buch ist, das er hat, " Unix Network Programming, Vol 1. " des verstorbenen Richard Stevens. Es spielt keine Rolle, ob Sie Windows oder ein anderes Betriebssystem oder eine Sprach-Socket-Programmierung durchführen. Denken Sie nicht, dass Sie Steckdosen verstehen, bis Sie dieses Buch gelesen haben.
Eine weitere Ressource, in der Sie einen allgemeinen Überblick über die verfügbaren Lösungen in Bezug auf die Mehrfach-Socket-Programmierung finden (hauptsächlich relevant für die Serverprogrammierung), ist diese Seite .