[LINUX] Netzwerkprogrammierung (grundlegend)

Hinweis: Es ist ein persönliches Memo. Wir übernehmen keine Garantie für die Richtigkeit des Inhalts. Wenn Sie keine Kenntnisse über das Verwendungssystem oder die C-Sprache haben, ahmen Sie diese bitte nicht nach. es ist gefährlich. (Während der Bearbeitung)

Grundlagen

Netzwerkprogrammierung

Die Kommunikation zwischen Prozessen kann auf demselben Computer wie einer Pipe (Pipe (2)) oder einem FIFO (Named Pipe) durchgeführt werden. Grundsätzlich erfolgt diese prozessübergreifende Kommunikation auf einem anderen Computer in einem Netzwerk, zu dem Sie nicht gehören. Es bedeutet, was gemäß der Kommunikationsvereinbarung getan wird.

Aufmerksamkeit für Architektur

Strukturausrichtung, Bytereihenfolge und wie der Adressbus auf eine Adresse zeigt Die Busbreite usw. unterscheiden sich je nach verwendeter Architektur. C-Sprache beauftragt C-Sprachbenutzer, den Unterschied zu erkennen. Verlassen Sie sich bei der Kommunikation mit verschiedenen Computern im Netzwerk nicht auf den Computer. Beispielsweise kann bei einem RISC-Prozessor der Zugriff auf eine ungerade Adresse zu einem Busfehler führen.

Systemaufruf

Die Netzwerkprogrammierung verwendet Systemaufrufe (uapi), um Anwendungen zu erstellen. In Benutzeranwendungen arbeitet der Kernel im Benutzermodus und verwendet virtuellen Speicher. Das Betriebssystem verwaltet die Netzwerkmodule direkt. Dies erfolgt im Kernel-Modus (privilegierter Modus).

Netzwerkanwendungen verwenden eine Schnittstelle namens Sockets. Sockets können als spezielle Dateien behandelt werden. Setzen Sie sokect (2) ab, um einen Socket zu erstellen.

Sockets sind noch Dateien. Daher können auch binäre Ein- / Ausgabefunktionen wie Lesen (2) und Schreiben (2) verwendet werden.

Der erstellte Socket kann mit bind (2) adressiert werden. Da Sockets ein Dateityp sind, werden sie auch von der Inode-Nummer verwaltet.

Das Netzwerkmodul, das den Socket enthält, verfügt über einen Puffer zum Speichern der Daten Stellt das Paket in die Warteschlange. So fordern Sie das Betriebssystem auf, Daten zu senden Geben Sie send (2) in der sendenden Anwendung aus. (Es ist eine Anfrage, keine Garantie) Wenn im Puffer freier Speicherplatz vorhanden ist, wird dieser in der Warteschlange gespeichert.

Daten können durch Ausgabe von recv (2) in der empfangenden Anwendung erhalten werden. Datenabruf aus dem Puffer anfordern. Das Programm wird blockiert, wenn die empfangenen Daten nicht in der Warteschlange vorhanden sind. Beachten Sie, dass recv (2) Daten aus dem Puffer abruft, auch wenn die Anwendung nicht ausgeführt wird. Beachten Sie, dass Daten auf unbestimmte Zeit abgerufen werden, wenn sie nicht normal enden.

Unterbrechungen werden von der Netzwerkkarte zum Senden und Empfangen von Daten verwendet. Dies wird vom Kernel erledigt, In der Sprache C wird ein abstrahiertes Signal für den Benutzer vorbereitet.

Redundanz

Wenn Anforderungen von mehreren Clients an eine Netzwerkanwendung vorliegen Die Anwendung kann die Verarbeitungsleistung überschreiten. In diesem Fall werden Multi-Process, Multi-Thread, Select (2) usw. verwendet.

Umgang mit Fehlern

Mit connect (2) wird der Kommunikationspartner auf Anwendungsebene identifiziert. Wenn der Datenübertragungs- / Empfangsprozess während des Verbindungsaufbaus nicht normal abgeschlossen ist, unabhängig davon, ob es sich um TCP oder UDP handelt. Wenn sie nicht berührt werden, kann die Verarbeitung für immer blockiert werden. Wenn es schlecht ist, wird auch die Tasteneingabe nicht akzeptiert.

Daher tritt eine Zeitüberschreitung in alerm (2), setitimer (2) usw. auf. Angemessene Signalverarbeitung mit Sigaktion (2) usw.

Protokollheader

Viele Protokolle werden durch C-Sprachstrukturen dargestellt. Viele dieser Details finden Sie in den folgenden Verzeichnissen:

/usr/include/net/* /usr/include/netinet/*

TCP/IP

Kommunikation vom Typ DGRAM und Kommunikation vom Typ STREAM

UNIX-Domäne und INET-Domäne

Daten

In TCP / IP erfolgt die Kommunikation durch Paketaustausch.

Ein Paket besteht aus einem Header und einer Nutzlast.

Header

Der Header enthält Informationen zur Zieladresse, Quelladresse, Nutzlast usw.

Nutzlast

Die Nutzlast sind die Daten selbst, mit denen Sie kommunizieren möchten

Botschaft

Auf dem Betriebssystem wird eine Kommunikationsanwendung erstellt, und die zwischen diesen Anwendungen ausgetauschten Daten werden als Nachricht bezeichnet.

Datentyp

Die Datentypen in den unten aufgeführten Headern garantieren die Größe in Bit. Dies ist auch für POSIX-kompatible Betriebssysteme vorgesehen.

/usr/include/stdint.h

<stdint.h> <inttypes.h> (C99)

Beispiel:


uint32_t /*32-Bit-Int-Typ ohne Vorzeichen*/

Wie es definiert ist, hängt vom System ab. Weitere Informationen finden Sie direkt in der Kopfzeile.

Netzwerkbyte-Reihenfolge

TCP / IP-Protokoll Protokoll-Header im Netzwerk Vereinheitlichen Sie Anwendungsnachrichten mit Big Endians. Die folgenden Funktionen können für die Endian-Konvertierung verwendet werden.


#include <arpa/inet.h> /*Je nach System<net/inet.h> */

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);


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

int main(void)
{
    uint32_t s = 0xff;

    /* host to network endian */
    uint32_t r = htonl(s);

    printf("%#0.8x\n", r);
    printf("%#0.8x\n", s);
    return 0;
}

Ergebnisbeispiel


0xff000000
0x000000ff

Namensauflösung von IP-Adresse und Domainname

1 . Verwenden Sie die Datei / etc / hosts


#include <netdb.h>
struct hostent *gethostbyname(const char *name);

#include <sys/socket.h>
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);

2 . Verwenden Sie DNS


#include <sys/socket.h>
void sethostent(int stayopen);
void endhostent(void);

Wenn Sie den Namen beim DNS-Server auflösen, registrieren Sie die Adresse vorab beim DNS-Server in /etc/resolv.conf.

Protokolleintrag

/etc/protocols

#include <netdb.h>

struct protoent *getprotoent(void);
struct protoent *getprotobyname(const char *name);
struct protoent *getprotobynumber(int proto);

Serviceeintrag

/etc/services


#include <netdb.h>

struct servent *getservent(void);
struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);

Recommended Posts

Netzwerkprogrammierung (grundlegend)
CentOS 7 Grundeinstellungen nach Netzwerkeinstellungen
Netzwerk
Netzwerkprogrammierung mit Python Scapy
Numpy [Basic]
Grundlegendes Schreiben verschiedener Programmiersprachen (Self-Memo)
Kot Gorua Programmierung
Grundlegende Befehle
Leistungsbewertung der Programmiersprache auf Basisebene
Grafikprogrammierung
Relationales Netzwerk
Programmieranfänger Python3 Ingenieur Zertifizierung Grundprüfungsprotokoll