Home » Tutorials » Netzwerk und Internet » Sockets mit WinAPI

Sockets mit WinAPI

TCP-Server

Ein TCP-Server ist etwas umfangreicher als ein TCP-Client. Abgesehen von zwei neuen Befehlen (mit alt bekannten Parametern) muss ich an einer anderen Stelle noch etwas weiter ausholen.
Bisher haben wir je Programm mit nur einem Socket gearbeitet. Es ist natürlich gut möglich in einem Programm mehrere Sockets zu haben. Bei einem Server ist dies allerdings erforderlich. Für einen Server haben wir generell ein Socket, was nur auf eingehende Verbindungen (von Clients) wartet. Kommt eine Verbindung herein, wird ein neues Socket aufgesetzt und der Datenaustausch erfolgt dann nur über den zweiten Socket. Genauso verhält es sich, wenn ein weiterer Client eine Verbindung aufbaut. Dadurch hat ein Server immer ein Listener-Socket (für eingehende Verbindungsanfragen) und für jeden Client noch ein Socket (Connect-Socket). Letztere sind dann auch genauso zu bedienen, wie TCP-Clients.

Erstellen eines Listener-Sockets

Ein Listener Socket wird in folgenden Schritten erstellt:

  1. Stream-Socket erstellen
  2. Socket an eine Adresse binden
  3. Socket in Listening-Modus schalten

Und das war’s auch schon wieder. Das Socket erstellen hatten wir bereits. Ein Socket an eine Adresse binden ist auch nach wie vor der gleiche Befehl (bind). Neu ist das umschalten in den Listening-Mode:

Listen(FSocket: TSocket;
       Backlog: Integer);

Der Befehl heißt (wie er auch schon da steht) Listen. Er benötigt das Socket-Handle und einen Integerwert, welcher beschreibt, wie viele Client-Verbindungen zugleich auf eine Verbindung warten können. (Standard unter Windows ist 5 = SOMAXCONN). Was bedeutet es auf eine Verbindung zu warten? Es bedeutet, das das Socket wartet, bis eine Verbindungsanfrage durch den Befehl Accept angenommen wird.

Eine Client-Verbindungsanfrage annehmen

Mit Accept kann eine Verbindungsanfrage angenommen werden. Und wann ruft man Accept auf? Zum einen braucht man natürlich einen Listener-Socket. Zum anderen braucht man einen Client der eine Anfrage stellt. Dies können wir im Programm zwar nicht so ohne weiteres wissen, aber genau dafür gibt es wieder die Messages.
Accept verhält sich ansonsten (ohne das Nutzen der Messages) genauso wie recv. Es kann den ganzen Thread blockieren oder eben einen WSAEWOULDBLOCK Fehler liefern, wenn man den Socket im NonBlocking-Mode hat und kein Client eine Anfrage gestellt hat. Wir werden, wie bisher, uns einfach Messages vom Socket schicken lassen wenn eine Verbindungsnafrage vorliegt.
Unser Event nennt sich auch passender Weise FD_ACCEPT und sieht dann im Kontext so aus:

 case msg.LParamLo of
    FD_ACCEPT: begin
        AddrLen := SizeOf(SockAddr);
        FConnectSocket := Accept(msg.wparam, //Listener-Socket
                                 @SocketAddr, //Client-Adr.
                                 @AddrLen);
        if FConnectSocket = INVALID_SOCKET then
          HandleError;
      end;
        
      ...
  end;

Über FConnectSocket können wir jetzt mit recv und send uns mit dem jeweiligen Client unterhalten. Wo der Client liegt (also dessen Adresse) haben wir durch Accept in SocketAddr erfahren. Da jeder Server mehrere Clientverbindungen halten kann, sollten wir FConnectSocket eher als Array ausführen oder eine zweite Verbindung nicht zulassen.
FConnectSocket hat jetzt in unserem Fall auch gleich die Messagebehandlung vom Listener-Socket „geerbt“. Sodass ohne weiteren Aufruf von WSAAsyncSelect, dieselben Messages wie zu dem Listener auch an unsere Messagebehandlungsroutine geleitet werden.
Außerdem hat FConnectSocket die gleiche Socketadresse, wie der Listener Socket. Ebenso haben alle weiteren Verbindungen zu anderen Clients von diesem Server die gleiche Socketadresse. Ich hatte ganz am Anfang mal erwähnt, dass dies eigentlich nicht möglich ist, eine Adresse samt Port mehrmals zu verwenden. Dies ist allerdings nur eine Blockierung der Socketimplementierung und hat keine Beeinträchtigung im Netzwerkverkehr. Wichtig für den Netzwerkverkehr ist nur, dass dann wenigstens die Remoteadresse verschieden ist, wenn schon die lokale Adresse übereinstimmt. Diese Blockierung wird über den Listener-Socket teilweise aufgehoben. Wir können dies auch händig für andere Sockets tun (aber nicht jetzt), wobei es dann allerdings zu Problemen kommen kann (wenn eben dann auch die Remoteadresse übereinstimmt).
Ein vollständiges Beispiel befindet sich wieder hier.

5 Gedanken zu „Sockets mit WinAPI“

  1. Wo findet sich denn der Link, um die Programmbeispiele herunterzuladen?
    Ich suche jetzt schon eine Weile, finde aber nichts.

  2. Eine weitere – aus meiner Sicht sehr wichtige – Antwort auf die Frage, warum man die Sockets selbst über die Windows API programmieren sollte, wäre noch:

    Ich hatte vor ca. 10 Jahren einen „Chat“ über die Komponenten von Delphi programmiert. Wir haben hier in der Firma einen Linux-Server zu stehen. Da die Leute das lustig fanden, ist der Chat heute fester Bestandteil. Linux bedeutet aber insoweit Probleme, weil ich das Programm emulieren lassen muss. Dafür würde sich theoretisch wine anbieten.
    Der Haken ist aber, dass die Komponenten alle fest in der VCL verankert sind und man mit wine nicht weit kommt. Man braucht also eine virtuelle Maschine mit Windows, um den Server laufen zu lassen. Insoweit erhoffe ich mir, dieses Problem eventuell lösen zu können. Wenn es mir gelingt, mit dieser Anleitung eine eigene Klasse ganz ohne jede VCL erstellen zu können, dann kann ich den Server als Konsolen-Programm erstellen und (endlich) ohne den ganzen Overhead unter wine laufen lassen.

    Falls den Seitenadmin dieser Kommentar stört, kann er ihn gern wieder gelöscht werden. Aber auf der Suche nach Sockets ohne VCL, wäre dies als Fund bei Suchmaschinen schon vor langer Zeit sehr hilfreich für mich gewesen. Suchmaschinen finden aber immer nur den Text, der auf den Seiten tatsächlich irgendwo steht und wenn man nach „nonvcl“ im Zusammenhang mit Sockets unter Delphi sucht, sieht es dünn aus mit den Suchergebnissen.

  3. Hallo, ich kann leider auch deine Programmbeispiele als Datei nicht finden. Sind die nicht mehr verlinkt?

    Ansonsten gutes Tutorial 🙂

Kommentare sind geschlossen.