Home » Tutorials » Netzwerk und Internet » Sockets mit WinAPI

Sockets mit WinAPI

TCP-Client

Im Gegensatz zu Datagramm-Sockets (wie für UDP) basieren Stream-Sockets (für TCP) auf festen Verbindungen zwischen zwei Sockets. Hier muss man, im Gegensatz zu den vorangegangen Kapiteln (UDP) zwischen Client und Server unterscheiden. Beim Aufbau der Verbindung übernimmt der Client den aktiven Part. Er verbindet sich aktiv zu einem Server. Der Server ist dabei eher passiv. Er unterhält einen Socket der nur auf Verbindungsanfragen wartet.
Ein TCP-Client kann nicht wie ein UDP-Socket kreuz und quer durchs Netz Daten verschicken, sondern muss sich zuvor eben immer erst mit einem anderen Stream-Socket verbinden. Deswegen gibt es auch kein SendTo (wie bei UDP), sondern nur ein Send. Diesem Befehl fehlt einfach die Zieladresse. Und diese wiederum ist dem Socket bereits bekannt, da es sich ja mit einem anderen Socket verbunden hat und nur an dieses Socket kann gesendet werden.
Und wie erfolgt die Verbindung? Dazu gibt es den Befehl Connect, er stellt eine Verbindungsanfrage an einen Server (also vielmehr an eine Adresse und hofft, das dahinter ein Server ist). Das waren dann auch schon die zwei neuen Befehle. Alles andere ist wie beim UDP-Socket. Man kann vor dem Connect, das Socket (also den Client) noch an eine Adresse binden, muss man aber nicht. Wenn das nicht gemacht wird, bekommt man eben während des Connect einen Port zugewiesen (wie bei SendTo für UDP).
Zum Empfangen von Daten können hier auch Messages eingesetzt werden. Wobei wir dann anstatt recvfrom besser recv aufrufen. Eine zusätzliche Message ist für uns noch interessant: FD_CLOSE. Diese meldet uns, wenn der Server die Verbindung getrennt hat (Vorraussetzung ist eine aktive Trennung des Servers und nicht dessen Absturz oder ein Durchschneiden der Kabel).
Zum Trennen der Verbindung kann entweder das Socket geschlossen werden (CloseSocket) oder shutdown aufgerufen werden.
Da die Funktionen von ihren Parametern nicht viel Neues bringen, gebe ich auch gleich ein Beispiel. Ähnlich wie in Kapitel 6 baut es auf ein VCL-Formular auf um eben die Messages zu verarbeiten.

uses WinSock,Messages, Forms, ...;

const WM_mySocket = WM_APP+1;

type TForm1 = class(TForm)
    ...
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FSocket: TSocket;
    procedure SocketMessage(var msg: TMessage); message WM_mysocket;
    //zum Empfangen der Messages
    ...
end;

...
 
procedure TForm1.FormCreate(Sender: TObject);
var SockAddr: TSockAddrIn;
    AddrLen: Integer;
begin
  FSocket := socket (AF_Inet, SOCK_STREAM, IPPROTO_TCP);
  if FSocket = invalid_socket then HandleError;

  AddrLen := SizeOf(SockAddr);
  SockAddr.sin_family := AF_Inet;
  SockAddr.sin_port := htons(6000);
  SockAddr.sin_addr.S_addr := inet_addr('127.0.0.1');

  //Verbinden zum eigenen Rechner an Port 6000
  if Connect(FSocket, SockAddr, AddrLen) = Socket_Error then
    HandleError;

  //Eine Absenderadresse haben wir jetzt automatisch bekommen.
  //Wir hätten sie auch mit bind vorgeben können.

  if WSAAsyncSelect(FSocket, self.Handle, WM_mySocket,
    FD_READ or FD_CLOSE or FD_WRITE)= SOCKET_ERROR then
    HandleError;
end;

procedure TForm1.SocketMessage(var msg: TMessage);
var buf: array[0..127] of char;
    FirstMsg: String;
begin
  case msg.LParamLo of
    FD_READ: begin
        Memo1.Lines.Add('FD_READ');
        FillChar(buf, length(buf), 0);
        if recv(msg.wparam,  // = Socket-Handle
          Buf,
          Length(buf)) = SOCKET_ERROR then
          HandleError;

        //Absenderadresse gibts bei recv nicht
        Memo1.Lines.Add(Buf);
      end;

    FD_WRITE: begin
        FirstMsg := 'Hallo Server!';
        Send(FSocket,      //TSocket
             FirstMsg[1],  //Buf
             Length(FirstMsg),  //length(buf)
             0);                //flags

        //Die Zieladresse fällt, wie gesagt, weg
        //Die Flags setzen wir wieder auf 0
      end;

    FD_CLOSE: Self.Close    //Programm schließen wenn Verbindung
                            //getrennt wird

  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  CloseSocket(FSocket);
end;

Dieses Programm verbindet sich zum Server und sendet sogleich eine Nachricht (Send kann auch an anderen Stellen im Programm aufgerufen werden, z.B. über ein Buttonklick-Ereignis). Ab dann empfängt es Nachrichten vom Server und beendet sich, sobald der Server die Verbindung beendet.
Der vollständige TCP-Client hängt wieder als Beispiel an.

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.