Implementierung von Socket-Optionen in der Linux-Netzwerkprogrammierung

Implementierung von Socket-Optionen in der Linux-Netzwerkprogrammierung

Socket-Optionsfunktion

Funktion: Methoden zum Lesen und Setzen von Socket-Dateideskriptorattributen

#include <sys/scoket.h>
int getsockopt (int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
int setsockopt (int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);

Die Socket-Optionstabelle sieht wie folgt aus:

Die Funktionen getsockopt und setsockopt geben bei Erfolg 0 und bei einem Fehler -1 zurück und setzen errno.

Für den Server sind einige Socket-Optionen nur gültig, wenn sie vor dem Aufruf des Listen-Systemaufrufs für den Listening-Socket festgelegt wurden. Dies liegt daran, dass der Verbindungs-Socket nur durch den Accept-Aufruf zurückgegeben werden kann und die durch Accept aus der Abhörwarteschlange akzeptierte Verbindung mindestens die ersten beiden Schritte des TCP-Drei-Wege-Handshakes abgeschlossen hat (da die Verbindung in der Abhörwarteschlange mindestens den Status SYN_RCVD erreicht hat). Dies bedeutet, dass der Server ein TCP-Synchronisierungssegment an die empfangene Verbindung gesendet hat. Allerdings sollten einige Socket-Optionen im TCP-Synchronisierungssegment festgelegt werden, z. B. die TCP-Maximalsegmentoption. Für diese Situation bietet Linux Entwicklern die folgende Lösung: Legen Sie diese Socket-Optionen für den Listening-Socket fest. Anschließend erbt der von Accept zurückgegebene Verbindungs-Socket diese Optionen automatisch. Diese Optionen sind: SO_DEBUG, SO_DONTROUTE, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVLOWAT, SO_SNDBUF, SO_SNDLOWAT, TCP_MAXSEG und TCP_NODELAY.

Für den Client sollten diese Socket-Optionen vor dem Aufruf der Verbindungsfunktion festgelegt werden, da nach der erfolgreichen Rückgabe des Verbindungsaufrufs der TCP-Drei-Wege-Handshake abgeschlossen ist.

SO_REUSEADDR-Option

Wir haben den TIME_WAIT-Status von TCP-Verbindungen bereits besprochen und erwähnt, dass das Serverprogramm durch Festlegen der Socket-Option SO_REUSEADDR die Verwendung der von der Verbindung im TIME_WAIT-Status belegten Socket-Adresse erzwingen kann.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
 
int main( int argc, char* argv[] )
{
  wenn(argc <= 2)
  {
    printf( "Verwendung: %s IP-Adresse Portnummer\n", Basisname( argv[0] ) );
    Rückgabe 1;
  }
  const char* ip = argv[1];
  Port wird als Portnummer bezeichnet.
 
  int sock = socket( PF_INET, SOCK_STREAM, 0 );
  behaupten( Socke >= 0 );
  int Wiederverwendung = 1;
  setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &wiederverwenden, sizeof( wiederverwenden ) );
 
  Struktur sockaddr_in Adresse;
  bzero( &Adresse, Größe von(Adresse));
  Adresse.sin_family = AF_INET;
  inet_pton( AF_INET, ip, &Adresse.sin_addr );
  Adresse.sin_port = htons(Port);
  int ret = bind(sock, (Struktur sockaddr*)&Adresse, sizeof(Adresse));
  behaupten( ret != -1 );
 
  ret = listen( Socke, 5 );
  behaupten( ret != -1 );
 
  Struktur sockaddr_in Client;
  socklen_t client_addrlength = Größe von (Client);
  int connfd = akzeptieren( sock, ( Struktur sockaddr* )&client, &client_addrlength );
  wenn ( connfd < 0 )
  {
    printf( "fehlernummer ist: %d\n", errno );
  }
  anders
  {
    Zeichen Remote [INET_ADDRSTRLEN];
    printf( "verbunden mit IP: %s und Port: %d\n", 
      inet_ntop( AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN ), ntohs( client.sin_port ) );
    schließen( connfd );
  }
 
  schließen (Socke);
  gebe 0 zurück;
}

Nachdem setsocketopt festgelegt wurde, kann die daran gebundene Socket-Adresse sofort wiederverwendet werden, auch wenn sich der Sock im Status TIME_WAIT befindet. Darüber hinaus können wir geschlossene Sockets auch schnell wiederverwenden, indem wir den Kernelparameter /proc/sys/net/ipv4/tcp_tw_recycle ändern, sodass die TCP-Verbindung überhaupt nicht in den Zustand TIME_WAIT wechselt und die Anwendung die lokale Socket-Adresse sofort wiederverwenden kann.

Optionen SO_RCVBUF und SO_SNDBUF

Die Optionen SO_RCVBUF und SO_SNDBUF stellen die Größen des TCP-Empfangspuffers bzw. des Sendepuffers dar. Wenn wir jedoch setsockopt verwenden, um die Größe des TCP-Empfangspuffers und des Sendepuffers festzulegen, verdoppelt das System seinen Wert und er darf nicht kleiner als sein Mindestwert sein. Der Mindestwert für den TCP-Empfangspuffer beträgt 256 Byte und der Mindestwert für den Sendepuffer beträgt 2048 Byte (verschiedene Systeme können jedoch unterschiedliche Standardmindestwerte haben). Darüber hinaus können wir die Kernelparameter /proc/sys/net/ipv4/tcp_rmem und /proc/sys/net/ipv4/tcp_wmem direkt ändern, um zu erzwingen, dass der TCP-Empfangspuffer und der Sendepuffer keine Mindestgrößenbeschränkung haben.

Ändern Sie das Client-Programm des TCP-Sendepuffers:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
 
#define BUFFER_SIZE 512
 
int main( int argc, char* argv[] )
{
  wenn(argc <= 3)
  {
    printf( "Verwendung: %s IP-Adresse Portnummer Sendepuffergröße\n", Basisname( argv[0] ) );
    Rückgabe 1;
  }
  const char* ip = argv[1];
  Port wird als Portnummer bezeichnet.
 
  Struktur sockaddr_in Serveradresse;
  bzero( &Serveradresse, Größe von( Serveradresse ) );
  server_address.sin_family = AF_INET;
  inet_pton( AF_INET, ip, &Serveradresse.sin_addr );
  server_address.sin_port = htons(Port);
 
  int sock = socket( PF_INET, SOCK_STREAM, 0 );
  behaupten( Socke >= 0 );
 
  int sendbuf = atoi( argv[3] );
  int len ​​= sizeof( sendbuf );
  setsockopt( sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof( sendbuf ) );
  getsockopt( sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, ( socklen_t* )&len );
  printf( "die Größe des TCP-Sendepuffers nach der Einstellung ist %d\n", sendbuf );
 
  wenn (Verbinden(Sock, (Struktur Sockaddr*)&Serveradresse, Größe von(Serveradresse)) != -1)
  {
    Zeichenpuffer [PUFFERGRÖSSE];
    memset(Puffer, „a“, PUFFERGRÖSSE);
    sende(sock, puffer, PUFFERGRÖSSE, 0);
  }
 
  schließen (Socke);
  gebe 0 zurück;
}

Ändern Sie das Serverprogramm des TCP-Empfangspuffers:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
 
#define BUFFER_SIZE 1024
 
int main( int argc, char* argv[] )
{
  wenn(argc <= 3)
  {
    printf( "Verwendung: %s IP-Adresse Portnummer Empfangspuffergröße\n", Basisname( argv[0] ) );
    Rückgabe 1;
  }
  const char* ip = argv[1];
  Port wird als Portnummer bezeichnet.
 
  Struktur sockaddr_in Adresse;
  bzero( &Adresse, Größe von(Adresse));
  Adresse.sin_family = AF_INET;
  inet_pton( AF_INET, ip, &Adresse.sin_addr );
  Adresse.sin_port = htons(Port);
 
  int sock = socket( PF_INET, SOCK_STREAM, 0 );
  behaupten( Socke >= 0 );
  : In diesem Fall wird die Anweisung recvbuf = atoi( argv[3] );
  int len ​​= sizeof(recvbuf);
  setsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof( recvbuf ) );
  getsockopt( sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, ( socklen_t* )&len );
  printf( "die Größe des Empfangspuffers nach dem Einstellen ist %d\n", recvbuf );
 
  int ret = bind(sock, (Struktur sockaddr*)&Adresse, sizeof(Adresse));
  behaupten( ret != -1 );
 
  ret = listen( Socke, 5 );
  behaupten( ret != -1 );
 
  Struktur sockaddr_in Client;
  socklen_t client_addrlength = Größe von (Client);
  int connfd = akzeptieren( sock, ( Struktur sockaddr* )&client, &client_addrlength );
  wenn ( connfd < 0 )
  {
    printf( "fehlernummer ist: %d\n", errno );
  }
  anders
  {
    Zeichenpuffer [PUFFERGRÖSSE];
    memset(Puffer, '\0', PUFFERGRÖSSE);
    während (recv (connfd, Puffer, Puffergröße-1, 0) > 0) {}
    schließen(connfd);
  }
 
  schließen (Socke);
  gebe 0 zurück;
}

Laufergebnisse:

root@iZbp1anc6yju2dks3nw5j0Z:~/test/socket# ./client 127.0.0.1 12345 2000
Die Größe des TCP-Sendepuffers beträgt nach der Einstellung 4608

root@iZbp1anc6yju2dks3nw5j0Z:~/test/socket# ./server 127.0.0.1 12345 50
Die Empfangspuffergröße nach der Einstellung beträgt 2304

Wie oben erklärt: Wenn wir setsockopt verwenden, um die Größe des TCP-Empfangspuffers und des Sendepuffers festzulegen, verdoppelt das System seinen Wert und er darf nicht kleiner als sein Mindestwert sein.

Optionen SO_RCVLOWAT und SO_SNDLOWAT

  • Die Optionen SO_RCVLOWAT und SO_SNDLOWAT stellen die Niedrigwassermarke für den TCP-Empfangspuffer bzw. den Sendepuffer dar. Sie werden im Allgemeinen vom E/A-Multiplexsystem aufgerufen, um zu bestimmen, ob der Socket lesbar oder beschreibbar ist. Wenn die Gesamtmenge der lesbaren Daten im TCP-Empfangspuffer größer als seine Untergrenze ist, benachrichtigt der Systemaufruf des E/A-Multiplexings die Anwendung, dass sie Daten aus dem entsprechenden Socket lesen kann. Wenn der freie Speicherplatz im TCP-Sendepuffer (Speicherplatz, in den Daten geschrieben werden können) größer als seine Untergrenze ist, benachrichtigt der Systemaufruf des E/A-Multiplexings die Anwendung, dass sie Daten in den entsprechenden Socket schreiben kann.
  • Standardmäßig sind sowohl die Niedrigwassermarke des TCP-Empfangspuffers als auch die Niedrigwassermarke des TCP-Sendepuffers 1 Byte groß.

SO_LINGER-Option

Die Option SO_LINGER wird verwendet, um das Verhalten des Systemaufrufs „Close“ beim Schließen einer TCP-Verbindung zu steuern. Wenn wir den Systemaufruf „close“ zum Schließen eines Sockets verwenden, wird „close“ standardmäßig sofort zurückgegeben, und das TCP-Modul ist dafür verantwortlich, die verbleibenden Daten im dem Socket entsprechenden TCP-Sendepuffer an die andere Partei zu senden.

Wenn wir den Wert der Option SO_LINGER festlegen, müssen wir dem Systemaufruf setsockopt (getsockopt) eine Struktur vom Typ Linger übergeben, die wie folgt definiert ist:

#include <sys/socket.h>
Struktur verweilen
{
  int l_onoff; //Diese Option ein- (ungleich 0) oder ausschalten (0) int l_linger; //Verweildauer};
  • Abhängig von den Werten der beiden Mitgliedsvariablen in der Linger-Struktur kann der Systemaufruf „close“ eines der folgenden drei Verhaltensweisen erzeugen:
  • l_onoff ist gleich 0. Zu diesem Zeitpunkt hat die Option SO_LINGER keine Wirkung und „close“ schließt den Socket mit dem Standardverhalten.
  • l_onoff ist ungleich 0, l_linger ist gleich 0. Zu diesem Zeitpunkt wird der Systemaufruf „close“ sofort zurückgegeben, das TCP-Modul verwirft die verbleibenden Daten im TCP-Sendepuffer, die dem geschlossenen Socket entsprechen, und sendet ein Reset-Segment an die andere Partei. Diese Situation bietet dem Server daher die Möglichkeit, eine Verbindung abnormal zu beenden. l_onoff ist ungleich 0, l_linger ist größer als 0. Das Verhalten beim Schließen zu diesem Zeitpunkt hängt von zwei Bedingungen ab: (1) ob im TCP-Sendepuffer Restdaten vorhanden sind, die dem geschlossenen Socket entsprechen; (2) ob der Socket blockiert oder nicht blockiert. Bei einem blockierten Socket wartet close für die Dauer von l_linger, bis das TCP-Modul alle verbleibenden Daten gesendet und eine Bestätigung von der anderen Partei erhalten hat. Wenn das TCP-Modul innerhalb dieses Zeitraums nicht alle verbleibenden Daten sendet und keine Bestätigung von der anderen Partei erhält, gibt der Systemaufruf „close“ -1 zurück und setzt errno auf EWOULDBLOCK. Wenn der Socket nicht blockiert, wird „close“ sofort zurückgegeben. Zu diesem Zeitpunkt müssen wir anhand des Rückgabewerts und der Fehlernummer feststellen, ob die verbleibenden Daten gesendet wurden.

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, er wird für jedermanns Studium hilfreich sein. Ich hoffe auch, dass jeder 123WORDPRESS.COM unterstützen wird.

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung und Anwendungsbeispiele der in der Linux-Netzwerkprogrammierung verwendeten Netzwerkfunktionen
  • Beispiel für ein UDP-Socket-Programm für die Linux-Netzwerkprogrammierung
  • Beispiel für die Socket-Dateiübertragung bei der Linux-Netzwerkprogrammierung
  • Eine kurze Analyse der Netzwerkprogrammierfunktionen von Linux

<<:  Vue verwendet dynamische Komponenten, um einen TAB-Umschalteffekt zu erzielen

>>:  Centos7-Installation und Konfiguration von Mysql5.7

Artikel empfehlen

Eine kurze Diskussion über Flex-Layout und Skalierungsberechnung

1. Einführung in Flex Layout Flex ist die Abkürzu...

Eine Frage zur Einstellung des Randradius-Werts

Problemdatensatz Heute wollte ich ein kleines Bau...

Die CSS-Priorität der Webseite wird für Sie ausführlich erklärt

Bevor wir über die CSS-Priorität sprechen, müssen...

Linux entfernt node.js vollständig und installiert es über den Befehl yum neu

erster Schritt Einmaliges Löschen mit der integri...

So kapseln Sie Paging-Komponenten manuell in Vue3.0

In diesem Artikel wird der spezifische Code der m...

Detaillierte Erklärung einiger Einstellungen für Tabellenanpassung und Überlauf

1. Zwei Eigenschaften des Tabellen-Resets: ①borde...

MySQL-Prozesssteuerung: IF()-, IFNULL()-, NULLIF()-, ISNULL()-Funktionen

In MySQL können Sie die Funktionen IF(), IFNULL()...