Wie öffne und verwende ich einen Socket in C? [closed]

Lesezeit: 7 Minuten

Benutzer-Avatar
Der.Anti.9

Ich würde gerne den einfachsten und effektivsten Weg zum Öffnen und Schreiben von Daten in einen Socket in der Programmiersprache C für die Netzwerkprogrammierung kennen.

Benutzer-Avatar
Bill die Eidechse

Sie haben Recht, die Verwendung von Sockets in C hat eine schwierige Syntax. Spätere Sprachen wie Java und Python machen es im Vergleich zu einem Kinderspiel. Das beste Tutorial, das ich für die Socket-Programmierung in C gefunden habe, ist Beejs Leitfaden zur Netzwerkprogrammierung. Ich empfehle Ihnen, am Anfang zu beginnen, um einen guten Überblick zu erhalten, aber wenn Sie nur etwas Code zum Laufen bringen müssen jetztkönnen Sie direkt zum Abschnitt mit dem Titel springen Client-Server-Hintergrund.

Viel Glück!

  • Das ist dumm. Steckdosen sind definiert als C-API, und “spätere Sprachen” müssen all diese C-Aufrufe auf einer bestimmten Ebene durchführen. Es gibt “spätere Bibliotheken” in C, die es auch leicht machen, bis hin zu beispielsweise einer HTTP-Anforderung, anstatt mit Sockets herumzuspielen. Sie können leicht eine Client-Funktion haben, die eine IP-Adresse in Host- oder Punktnotation als akzeptiert char * string, eine Portnummer als intund geben Sie a zurück FILE * Stream, der die angeschlossene Schaltung bezeichnet, oder ein Nullzeiger mit errno auf etwas Nützliches einstellen.

    – Kas

    18. Dezember 2013 um 23:54 Uhr

Sie erwähnen nicht, auf welcher Plattform Sie sich befinden, sondern eine Kopie davon Unix-Netzwerkprogrammierung von Stevens wäre eine gute Ergänzung für Ihr Bücherregal. Die meisten Betriebssysteme implementieren Berkley Sockets mit Socket, Bind, Connect usw.

  • Ich habe versucht, mich für meine eigene Antwort an den Namen dieses Buches zu erinnern, aber es ist derzeit eingelagert. Dies ist die Bibel für die Socket-Programmierung.

    – paxdiablo

    21. November 2008 um 3:44 Uhr

  • Ja, das ist ein tolles Buch. Ich habe es bei der Arbeit in Reichweite.

    – Bill die Eidechse

    21. November 2008 um 4:13 Uhr

  • Alle Bücher von Stevens sind großartig, aber UNP ist das Beste. Beide Bände.

    – qrdl

    21. November 2008 um 7:44 Uhr

Benutzer-Avatar
Ciro Santilli Путлер Капут 六四事

POSIX 7 Minimales lauffähiges Client-Server-TCP-Beispiel

Holen Sie sich zwei Computer in ein LAN, z. B. Ihr WiFi-Heimnetzwerk.

Führen Sie den Server auf einem Computer aus mit:

./server.out

Holen Sie sich die IP des Server-Rechners mit ifconfigz.B 192.168.0.10.

Führen Sie auf dem anderen Computer Folgendes aus:

./client.out 192.168.0.10

Geben Sie nun Zeilen auf dem Client ein, und der Server gibt sie um 1 erhöht zurück (ROT-1-Chiffre).

server.c

#define _XOPEN_SOURCE 700

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    int enable = 1;
    int i;
    int newline_found = 0;
    int server_sockfd, client_sockfd;
    socklen_t client_len;
    ssize_t nbytes_read;
    struct sockaddr_in client_address, server_address;
    unsigned short server_port = 12345u;

    if (argc > 1) {
        server_port = strtol(argv[1], NULL, 10);
    }

    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }

    server_sockfd = socket(
        AF_INET,
        SOCK_STREAM,
        protoent->p_proto
        /* 0 */
    );
    if (server_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");
        exit(EXIT_FAILURE);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(server_port);
    if (bind(
            server_sockfd,
            (struct sockaddr*)&server_address,
            sizeof(server_address)
        ) == -1
    ) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(server_sockfd, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    fprintf(stderr, "listening on port %d\n", server_port);

    while (1) {
        client_len = sizeof(client_address);
        client_sockfd = accept(
            server_sockfd,
            (struct sockaddr*)&client_address,
            &client_len
        );
        while ((nbytes_read = read(client_sockfd, buffer, BUFSIZ)) > 0) {
            printf("received:\n");
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n')
                newline_found;
            for (i = 0; i < nbytes_read - 1; i++)
                buffer[i]++;
            write(client_sockfd, buffer, nbytes_read);
            if (newline_found)
                break;
        }
        close(client_sockfd);
    }
    return EXIT_SUCCESS;
}

client.c

#define _XOPEN_SOURCE 700

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    char *server_hostname = "127.0.0.1";
    char *user_input = NULL;
    in_addr_t in_addr;
    in_addr_t server_addr;
    int sockfd;
    size_t getline_buffer = 0;
    ssize_t nbytes_read, i, user_input_len;
    struct hostent *hostent;
    /* This is the struct used by INet addresses. */
    struct sockaddr_in sockaddr_in;
    unsigned short server_port = 12345;

    if (argc > 1) {
        server_hostname = argv[1];
        if (argc > 2) {
            server_port = strtol(argv[2], NULL, 10);
        }
    }

    /* Get socket. */
    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Prepare sockaddr_in. */
    hostent = gethostbyname(server_hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
        exit(EXIT_FAILURE);
    }
    in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
    if (in_addr == (in_addr_t)-1) {
        fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
        exit(EXIT_FAILURE);
    }
    sockaddr_in.sin_addr.s_addr = in_addr;
    sockaddr_in.sin_family = AF_INET;
    sockaddr_in.sin_port = htons(server_port);

    /* Do the actual connection. */
    if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }
    while (1) {
        fprintf(stderr, "enter string (empty to quit):\n");
        user_input_len = getline(&user_input, &getline_buffer, stdin);
        if (user_input_len == -1) {
            perror("getline");
            exit(EXIT_FAILURE);
        }
        if (user_input_len == 1) {
            close(sockfd);
            break;
        }
        if (write(sockfd, user_input, user_input_len) == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }
        while ((nbytes_read = read(sockfd, buffer, BUFSIZ)) > 0) {
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n') {
                fflush(stdout);
                break;
            }
        }
    }
    free(user_input);

    exit(EXIT_SUCCESS);
}

Auf GitHub mit einem Makefile. Getestet auf Ubuntu 15.10.

Nachrichtenlänge

Das read Aufrufe auf Client und Server laufen innerhalb von While-Schleifen.

Wie beim Lesen von Dateien kann das Betriebssystem Nachrichten willkürlich aufteilen, um die Dinge schneller zu machen, z. B. kann ein Paket viel früher ankommen als das andere.

Das Protokoll muss also eine Konvention angeben, wo Nachrichten aufhören. Zu den gängigen Methoden gehören:

  • ein Header mit einer Längenangabe (z. B. HTTP Content-Length)
  • eine eindeutige Zeichenfolge, die Nachrichten beendet. Hier verwenden wir \n.
  • der Server schließt die Verbindung: HTTP erlaubt das https://stackoverflow.com/a/25586633/895245. Natürlich begrenzt, da die nächste Nachricht eine Neuverbindung erfordert.

Nächste Schritte

Dieses Beispiel ist begrenzt, weil:

  • Der Server kann jeweils nur eine Client-Verbindung verarbeiten
  • Die Kommunikation wird einfach synchronisiert. Beispiel: Bei einer P2P-Chat-App kann der Server (andere Person) jederzeit Nachrichten senden.

Das Lösen dieser Probleme erfordert Threading und möglicherweise andere Aufrufe wie poll.

Sofern Sie keinen Netzwerk-Daemon schreiben, können die meisten Netzwerke in C auf einer höheren Ebene ausgeführt werden, als die Sockets direkt zu verwenden, indem Sie geeignete Bibliotheken verwenden.

Wenn Sie beispielsweise nur eine Datei mit HTTP abrufen möchten, verwenden Sie Neon oder libcurl. Es wird einfacher sein, es wird auf einem höheren Niveau sein und Sie werden kostenloses SSL, IPv6 usw. haben.

Das Lesen und Schreiben von einfachen Sockets ist nicht schwieriger als das Lesen und Schreiben normaler Dateien (verwenden Sie einfach recv anstelle von read und send stattdessen if write). Etwas knifflig wird es, wenn Sie eine Steckdose öffnen müssen. Der Grund dafür ist, dass es viele verschiedene Möglichkeiten gibt, über Sockets zu kommunizieren (TCP, UDP usw.).

Benutzer-Avatar
Wüstennaut

Ich schreibe im Allgemeinen in C++, aber Sie können etwas Nutzen in einem Whitepaper finden, das ich geschrieben habe: „How to Avoid the Top Ten Sockets Programming Errors“ – ignorieren Sie den Rat, das ACE-Toolkit zu verwenden (da es C++ erfordert), aber beachten Sie den Socket Fehler im Papier – sie sind leicht zu machen und schwer zu finden, besonders für Anfänger.
http://www.riverace.com/sockets10.htm

1175800cookie-checkWie öffne und verwende ich einen Socket in C? [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy