Client Socket

Teil 1 von 7 der Serie Sockets

Sockets

Client Socket

Server Socket

TLS Socket, SSL Socket

Selenium WebDriver ließt WebSocket

Multicast

LIPC, Unix Domain Sockets

UPD, Datagram Sockets

Es gibt Code, den ich mir nicht merken kann, egal wie oft ich ihn brauche. In C++ eine Socket-Verbindung zu einem Server zu erstellen ist ein Beispiel dafür. Deshalb halte ich hier mein persönliches Template für diesen Zweck fest.

#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    const char* ip = "127.0.0.1";
    int port = 14001;
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in serv_addr;
    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    inet_pton(AF_INET, ip, &serv_addr.sin_addr);

    if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("Kann keine Verbindung zu %s:%d aufbauen\n", ip, port);
        return 0;
    }

    char buffer[32] = {0};
    int gelesen = read( sock , buffer, sizeof(buffer));
    printf("gelesen: '%s' (%d Zeichen)\n", buffer, gelesen);
    return 0;
}

Das zweite Beispiel ist etwas ergiebiger. Erstens zeigt es wie die String-Darstellung eines Hostnamens wie www.tuncer.net mit getaddrinfo in einen Typ sockaddr_in umgewandelt werden kann. Es gäbe auch die Möglichkeit den Hostnamen über gethostbyname() aufzulösen, doch die liefert eine host_ent-Struktur mit einem anderen Fokus (mehrere Namen auf einem Interface, mehrere Interfaces auf einem Namen). Doch mit vhosts oder mehreren Netwerken (d.h. IP-Adressen) auf einem Host gibt es Komplikationen, die ich hier in diesem Beispiel nicht brauche.

Dann zeigt das Beispiel den Ablauf einer HTTP-Sitzung. Das HTTP-Protokoll für einen HTTP-POST-Request inclusive einem Query-String wird durchgespielt.

Zudem wird gezeigt, wie der Request an einen virtual Host geschickt wird. Die Schwierigkeit ist ja, das alle vhosts auf einer Maschine die selbe IP-Adresse haben. Klingt kompliziert, doch letztlich ist es nur die Host-Zeile im Protokoll-Header.

Schliesslich wird die Anwort vom Server in Chunks gelesen und auf die Konsole ausgegeben.

Das Beispiel wäre eigentlich erst dann vollständig, wenn ich den Header vom Server auch noch lesen (vom Anfang bis \r\n\r\n) und interpretieren würde. Der schickt nämlich soetwas wie

HTTP/1.1 200 OK\r\n
Date: Sun, 05 Apr 2020 07:18:35 GMT\r\n
Server: Apache/2.4.29 (Ubuntu)\r\n
Last-Modified: Thu, 19 Dec 2019 09:59:55 GMT\r\n
ETag: "10c8-59a0b9f86fac7"\r\n
Accept-Ranges: bytes\r\n
Content-Length: 4296\r\n
Vary: Accept-Encoding\r\n
Connection: close\r\n
Content-Type: text/html\r\n\r\nCode-Sprache: HTTP (http)

Doch in diesem Artikel geht es primär um den Socket und nicht um die übertragenen Daten.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <string>

int main()
{
    int remote = socket(AF_INET, SOCK_STREAM, 0);
    if(remote < 0)
    {
        perror("socket");
        return 1;
    }

    char hostname[] = "localhost";
    short port=80;

    struct addrinfo hints;
    memset(&hints,0,sizeof(struct addrinfo));
    hints.ai_family = AF_INET;

    struct addrinfo* addrinfo; // Ergebnisse

    if( getaddrinfo(hostname, NULL, &hints, &addrinfo) )
    {
        fprintf(stderr, "Fehler: Kann Host '%s' nicht auflösen\n", hostname);
        return 1;
    }
    struct sockaddr_in* remote_addr = (struct sockaddr_in *) addrinfo->ai_addr;

    remote_addr->sin_port = htons(port);

    if(connect(remote, (struct sockaddr *) remote_addr, sizeof(*remote_addr)) < 0)
    {
        fprintf(stderr, "Fehler: Verbindung zu '%s:%d' kann nicht hergestellt werden\n", hostname, port);
        return 1;
    }

    char query_string[] = "param1=1&param2=2";

    char anfrage[1024] =
        "POST /app/ HTTP/1.1\r\n"
        "Host: gamma\r\n"
        "Content-Length: ";
    strcat(anfrage, std::to_string(strlen(query_string)).c_str());
    strcat(anfrage, "\r\n"
        "Connection: close\r\n\r\n"
    );

    if(write(remote, anfrage, strlen(anfrage)) < 0)
    {
        perror("send");
        return 1;
    }

    if(write(remote, query_string, strlen(query_string)) < 0)
    {
        perror("send");
        return 1;
    }

    char antwort[1024];
    int gelesen;
    while( (gelesen = read(remote, antwort, sizeof(antwort))) > 0)
    {
        if( write(1, antwort, gelesen) < 0 )
        {
            perror("write");
            return 0;
        };
    }

    close(remote);
    return 0;
}

Sockets

Server Socket