Socket buffer vergrößern mit setsockopt(2)

Herakles

Profifragensteller
Moin!

Ich möchte gern ein wenig mit der Performance von Netzwerken herumspielen und habe dafür ein kleines Testprogramm geschrieben, das ich unten anhänge. Nun beobachte ich einen interessanten Unterschied zwischen 100Mbit/s-Ethernetkarten und WLAN-Karten im a-Band im Gegensatz zu WLAN-Karten im g-Band.

Dieser Test schießt auf einem UDP-Multicast Socket jede Millisekunde 384 Bytes an Daten heraus. Schlägt das fehl (EAGAIN), werden es alle zwei Millisekunden 768 Bytes und so fort.

Das Programm unten läuft wunderbar im 100Mbit/s-Ethernet und im 802.11a-Band, keine Mucken, kein nichts. Wenn ich allerdings hergehe und das Programm im 802.11g-Band laufen lasse, dann bekomme ich nach wiederholbar genauer Anzahl von versendeten Paketen einen "EAGAIN"-Error "Ressource temporarily unavailable". Die Anzahl der Pakete kann ich recht exakt variieren, indem ich den Socker Puffer verändere, mein bisheriger Maximalwert dafür war 262142, mehr ließ das System nicht zu.

Meine Schlussfolgerung ist also, dass die Größe des Socketpuffers das Auftreten des besagten Fehlers beeinflusst.

Ich gehe nun davon aus, dass im 802.11g-Band wegen des höher belasteten Mediums als im a-Band oder im Ethernet durch CSMA (Carrier Sense) sogenannte backoff-Times eingebaut werden (das sind pausierte Zufallszeiten der WLAN-Karte nach einem versucht versandten Paket, was dann aber durch ein erkannt blockiertes Medium nicht versandt wird). Dadurch benötige ich natürlich Platz im Socket Puffer - denn das Paket soll ja später bei wieder freiem Medium noch versandt werden.

1. Hat jemand Erfahrung mit Socket Puffern und wie man erfolgreich mit Ihnen herumspielt, vergrößert, verkleinert? Es gibt einen entsprechenden call für setsockopt(2), allerdings bekomme ich so den Buffer nicht größer als 262142, was offenbar nicht groß genug für meine anliegende Datenlast( ~2,9 Mbit/s) ist.
2. Hat jemand eine andere Idee, wo der Fehler liegen könnte - bin ich also vielleicht auf dem vollkommen "falschen Dampfer"?

Eins noch: Ich weiß, dass dies ein BSD-Forum ist; Dennoch ist das Programm unter Linux geschrieben worden. Ich würde nur gern hier und nicht auf einer Linuxseite fragen, weil ich einfach weiß, dass hier so einige Netzwerkexperten reinschauen und aktiv sind :) Außerdem fühl ich mich hier wohl...

Viele Grüße
Herakles

Hier der Code:
Code:
#include <sys/types.h>       
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>     
#include <errno.h>
#include <linux/ip.h>
#include <string.h>
#include <stdlib.h>
#define MULTICAST_IP			"224.0.0.1"
#define PORT				12345
    
struct sockaddr_in server_address;
int fd_socket;

int init_connection_client( void ) {
	int sock, opt;
socklen_t len;

	errno = 0;
	//create a socket
	sock = socket( PF_INET, SOCK_DGRAM, 0 );
	if( sock == -1 ) {
		printf("%s:%u socket() error (%s), error-code: %d\n", __FILE__, __LINE__, strerror(errno), sock);
		exit(EXIT_FAILURE);
	}
	opt=524284;
	if( setsockopt( sock, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt) ) == -1 ) {
		printf("%s:%u setsockopt(SND) error (%s), error-code: %d\n", __FILE__, __LINE__, strerror(errno), errno);
		exit(EXIT_FAILURE);
	}
	opt=0;
	len=sizeof(opt);
	if( getsockopt( sock, SOL_SOCKET, SO_SNDBUF, &opt, &len ) == -1 ) {
		printf("%s:%u getsockopt(SND) error (%s), error-code: %d\n", __FILE__, __LINE__, strerror(errno), errno);
		exit(EXIT_FAILURE);
	}
printf("SO_SNDBUF: %d\n",opt);
	server_address.sin_family = PF_INET;
	server_address.sin_addr.s_addr = inet_addr( MULTICAST_IP );
	server_address.sin_port = htons( PORT );

	if( bind( sock, (struct sockaddr*)&server_address, sizeof(struct sockaddr)) == -1 ) {
		printf("%s:%u bind error (%s), error-code: %d\n", __FILE__, __LINE__, strerror(errno), errno);
		exit(EXIT_FAILURE);
	}

	
	return sock;
}
int send_data( unsigned char *network_data, size_t len ) {
	if( sendto( fd_socket, network_data, len, MSG_DONTWAIT, \
		(struct sockaddr*)&server_address, sizeof(struct sockaddr_in) ) == -1 ) {
			printf("%s:%u sendto() error (%s), error-code: %d\n", __FILE__, __LINE__, strerror(errno), errno);
			return errno;
	}

	return 0;
}

#define BYTE_PRO_MS 384
int main(int argc, char **argv) {
  unsigned char *data_for_transmission;
  int sleeper=atoi(argv[1]);
  int run=0;
  int datasize=0;
  
  printf("%d\n",sleeper);
  data_for_transmission = malloc( 30000 );
  fd_socket=init_connection_client();
  datasize=sleeper / 1000 * BYTE_PRO_MS;
  while(1) {
	if( send_data( data_for_transmission, datasize ) != 0 ) {
	  printf("sleep for 3\n");
		sleep(3);
		sleeper+=1000;
		datasize+=BYTE_PRO_MS;
	}
	run++;
	printf("run: %d sleep: %d, datasize: %d\n",run, sleeper, datasize);
	usleep(sleeper);
  }
 return 0;
}
 
Es gibt zwei Lösungen für das Problem:

1. Man nutzt die setsockopt(2)-Option SO_SNDBUFFORCE. Laut socket(7) findet bewirkt das folgendes:

SO_SNDBUFFORCE (since Linux 2.6.14)
Using this socket option, a privileged (CAP_NET_ADMIN) process
can perform the same task as SO_SNDBUF, but the wmem_max limit
can be overridden.

Jetzt steht da natürlich "since Linux...". Eine Suche im Internet, ob es diese Option auch für *BSD gibt, hat erstmal kein Ergebnis gebracht. Demzufolge ist das also nicht unbedingt die Option der Wahl, zumal man hier ja offensichtlich auch eine gewollte Einschränkung des Systems umgeht. Deshalb ist vielleicht die zwei Option besser:

2. Man benutzt nach wie vor SO_SNDBUF, erhöht aber den möglichen Maximalwert für den Socketpuffer - systemweit. In Linux (jaja... :) ) geht das folgendermaßen:

Code:
sysctl -w net.core.wmem_max=8388608

...oder welchen Wert man da auch immer möchte. So kann man nun mit wesentlich größeren Socket Puffern arbeiten und mein beschriebenes Problem ist gelöst.

Abschließend fragt sich nur noch, welchen Einfluss dieser Eingriff auf mein System hat. Ist das ein tiefer Eingriff in den Netzwerkstack, der mir letztendlich ein instabileres System liefert oder gar Nebeneffekte hat, die ich noch gar nicht abschätzen kann?

Ich spiel mal noch ein wenig weiter... :)

Herakles
 
Back
Top