TCP Eggo Bediener: Bou jou eerste netwerk bediener van nuuts af in C

Inleiding

In hierdie plasing gaan ons stap-vir-stap deur die bou van ‘n eenvoudige TCP eggo bediener in C. Hierdie bediener illustreer die basies beginsels van netwerk programmering. In die voorbeeld ontvang ons data van ‘n kliënt en stuur dit presies soos ontvang terug. Die oorspronklike artikel is te vinde hier.

Wat is Berkeley Sockets?

Berkeley Sockets (ook genoem POSIX Sockets) is die standaard koppelvlak vir netwerk kommunikasie op Unix-gebaseerde stelsels. Dit bied ‘n stel funksies soos socket(), bind(), listen(), en accept(), wat jou in staat stel om TCP/IP verbindings te skep en te bestuur.

TCP (Transmission Control Protocol) verseker betroubare data versending. Wanneer ‘n kliënt aan ons bediener koppel, skep TCP ‘n volledige dupleks kanaal vir data-uitruiling.

Nodige .h lêers om in te sluit

Vir ons C program (echo_server.c), moet ons ‘n paar lêers insluit:

/* echo_server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>      /* close() */
#include <sys/socket.h>  /* socket(), bind(), listen(), accept() */
#include <netinet/in.h>  /* sockaddr_in */
#include <arpa/inet.h>   /* inet_ntoa() */

Volgende in die program definieer ons twee konstantes:

#define PORT     8080
#define BUFSIZE 1024

PORT sal gebruik word vir die poort waarop die TCP bediener gaan loop, en BUFSIZE vir die grootte van ‘n karrakter buffer waarin ons data in gaan lees van die TCP konneksie af.

Skep ‘n Konneksie

Die eerste stap is om ‘n nuwe konneksie te skep. Ons gebruik AF_INET vir IPv4 en SOCK_STREAM om ‘n verbindingsgeoriënteerde TCP konneksie te spesifiseer:

int main(void) {
    int server_fd, client_fd;
    struct sockaddr_in adress;
    socklen_t adress_len = sizeof(adress);

    /* Skep die socket */
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket() failed");
        exit(EXIT_FAILURE);
    }

Die derde argument (0) laat die stelsel outomaties die korrekte protokol kies, in hierdie geval TCP.

Adres Hegging

Nou heg ons die konneksie aan ‘n plaaslike IP-adres en poort. INADDR_ANY beteken die bediener luister op alle beskikbare netwerk-koppelvlakke (0.0.0.0):

    /* Stel adres inligting op */
    memset(&adress, 0, sizeof(adress));
    adress.sin_family      = AF_INET;
    adress.sin_addr.s_addr = INADDR_ANY;
    adress.sin_port        = htons(PORT);

    if (bind(server_fd,
             (struct sockaddr *)&adress,
             sizeof(adress)) < 0) {
        perror("bind() failed");
        exit(EXIT_FAILURE);
    }

Die htons() funksie skakel die poortnommer om van gasheer-greep-volgorde na netwerk-greep-volgorde (groot-einde). Hier het ons te make met greep volgorde. Ons gebruik die htons() funksie om te verseker dat die greep order dieselfde werk afgesien van watter soort rekenaar ons die program op loop.

Luister vir Nuwe Verbindings

Met listen() verstel ons die konneksie oor na ‘n blokkende modus waar dit wag vir nuwe versoeke:

    if (listen(server_fd, 5) < 0) {
        perror("listen() failed");
        exit(EXIT_FAILURE);
    }
    printf("Server listening on port %d...\n", PORT);

Nadat ‘n nuwe konneksie ontvang sal die listen() funksie aan beweeg na die volgende instruksies.

Die eerste argument van die listen() funksie is die verwysing na die bediener. Die tweede argument van die funksie stel die wag-ry-grootte in. Omdat een konneksie in hierdie enkelverwerking toepassing gebruik word, word net een konneksie op ‘n slag hanteer. Daar mag wel tydens die prosesseering van die tanse versoek ander inkomende versoeke wees, die hierdie ander versoeke sal dan in ‘n waglys geplaas word. In hierdie geval is die waglys grootte 5.

Aanvaar van Verbindings en Stuur van Data

Die kern van ons bediener is ‘n oneindige lus wat verbindings aanvaar, data ontvang, en dit terug stuur:

    char buf[BUFSIZE];

    while (1) {
        /* Blokkeer totdat 'n kliënt koppel */
        client_fd = accept(server_fd,
                           (struct sockaddr *)&adress,
                           &adress_len);
        if (client_fd < 0) {
            perror("accept() failed");
            continue;
        }

        printf("New client: %s\n",
               inet_ntoa(adress.sin_addr));

        ssize_t n;
        while ((n = recv(client_fd, buf,
                          BUFSIZE, 0)) > 0) {
            /* Stuur presies dieselfde data terug */
            send(client_fd, buf, (size_t)n, 0);
        }

        close(client_fd);
        printf("Client disconnected.\n");
    }

    close(server_fd);
    return 0;
}

Hier is die recv() funksie van belang. Dit word gebruik om data in te lees. Dit neem ‘n verwysing na die kliënt konneksie, ‘n buffer, die buffer grootte. Die laaste parameter is vlae wat nie nou van belang is nie.

Die send() funksie is ook van belang, Dit word gebruik om data terug te stuur na die kliënt en werk baie soortgelyk aan die recv() funksie.

Bou

Bou die program met GCC soos volg:

$ gcc -Wall -Wextra -o echo_server echo_server.c

Neem kennis dat daar glad nie gekoppel hoef te word met die Berkeley Socket biblioteek nie, net die standaard gcc vlae word gebruik.

Toets

Die bediener kan getoets word met nc (netcat). Die eerste stap is om die bediener in een terminal te loop:

$ ./echo_server

Daarna, in ‘n ander terminal kan netcat gebruik word om te verbind met die bediener

$ nc 127.0.0.1 8080
Hello world!
Hello world!   <-- eggo terug

Elke boodskap wat gestuur word na die bediener sal terug gestuur word na netcat toe.

Opsomming

Ons het ‘n werkende TCP eggo bediener gebou wat die Berkeley Socket biblioteek gebruik. Die stappe is eenvoudig: socket() → bind() → listen() → accept() → recv()/send() → close(). Met hierdie basis kan jy uitbrei om spesifieke protokolle soos HTTP te implementeer asook parallelle verwerking implementeer.

In ‘n volgende plasing sal ons kyk na ‘n basiese implementeering van die HTTP protokol.


(Afrikaanse artikel)

Comments

Popular Posts