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 .
Sie haben nicht geschrieben, welche Programmiersprache Sie verwenden, aber die meisten Sprachen bieten gute asynchrone Socket-Frameworks, die häufig Funktionen des zugrunde liegenden Betriebssystems verwenden.
Wenn Sie Ihre eigene Thread-Implementierung basierend auf dem Blockieren von Sockets schreiben (denken Sie daran: Wenn Sie mehrere Clients haben, benötigen Sie einen Thread für jeden einzelnen Socket), werden Sie nur neu erfinden, was das asynchrone Socket-Framework bereits bietet.
Daher würde ich Ihnen empfehlen, asynchrone Sockets zu verwenden.
quelle
N
Clients bedient? Wenn Sie blockieren und sicherstellen möchten, dass jeder Socket seine Daten unabhängig vom Status der übrigen Sockets verarbeitet, müssen Sie einen Thread pro Socket haben. Das, oder verwenden Sie keine Sperrbuchsen.