Bis zu 7-fache Verzögerung auf WLAN-Schnittstelle...!?

Herakles

Profifragensteller
Moin!

Ich fahre hier gerade ein paar Netzwerktests und bin dabei auf ein Kuriosum gestoßen.

In einem kleinen C-Programm schicke ich per UDP-Paket einfach Pakete ohne nennenswerten Inhalt auf die WLAN-Schnittstelle. Die geschieht in einer Endlosscheife gemeinsam mit einem usleep(3000), also 3 ms.

Schneide ich nun mit einem nicht involviertem Netzwerkinterface den Datenverkehr mit (WLAN-Karte im Monitormodus), so bemerke ich, dass die meisten Pakete 3 ms nach dem letzten verschickt werden, einige jedoch schneller, und - viel schlimmer - einige wesentlich langsamer. So habe ich bei einem Lauf von 10 Sekunden mindestens ein Paket, das 20ms benötigt, also fast die 7-fache Zeit.

Woran mag das liegen? Der Kernel, bzw. der Treiber scheint der Flaschenhals zu sein, denn wenn ich auf der Karte mitschneide, mit der ich sende, dann erscheint jedes Paket in exakt den vorgegebenen 3 ms Abständen (Abweichung nur im Mikrosekundenbereich). Mein kleines Test-Programm möchte ich deshalb als Fehlerquelle ausschließen.

Der Vollständigkeit halber habe ich den Code unten angehängt. Und ja, es handelt sich um ein Linux-Testsystem. Da ich aber selbst gern in diesem Forum unterwegs bin und hier viele Netzwerkexperten weiß, möchte ich die Frage dennoch hier stellen. Man möge es mir verzeihen! Ich verwende Atheros-Karten. Allerdings ist dasselbe Verhalten mit Intel-ipw2200 Karten zu beobachten.

Danke im Voraus,
Herakles

Code:
#define PORT 12345
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
	int sock;
	struct hostent *host;
	char network_data;
	struct sockaddr_in client_address;
	struct sockaddr_in server_address;

	host = gethostbyname( argv[1] );
	server_address.sin_family = host->h_addrtype;
	memcpy( (char *) &server_address.sin_addr.s_addr, host->h_addr_list[0], host->h_length );
	server_address.sin_port = htons( PORT );
	errno = 0;

	sock = socket( PF_INET, SOCK_DGRAM, 0 );
	if( sock == -1 ) {
		printf( "socket() error: %s\n",strerror(errno));
		exit( -1 );
	}
	client_address.sin_family = PF_INET;
	client_address.sin_addr.s_addr = htonl (INADDR_ANY);
	client_address.sin_port = htons( 0 );
	if( bind( sock, (struct sockaddr*)&client_address, sizeof( struct sockaddr_in ) ) < 0 ) {
		printf("Cannot bind socket: %s\n",strerror(errno));
		exit( -1 );
	}
	while(1) {
		usleep(3000);
		if( sendto( sock, &network_data, 1, MSG_DONTWAIT, \
			(struct sockaddr*)&server_address, sizeof(struct sockaddr_in) ) == -1 ) {
			printf("sendto() error: %s\n",strerror(errno));
		}
	}
}
 
Wenn es zu einer Paketkollision kommt, wird eine Zufallszeit gewartet, bis der nächste Versuch gestartet wird.

Vermutlich misst du lokal nur wann die Pakete an die Hardware weitergereicht werden, nicht wenn sie ankommen.

Da kann man nicht viel Machen. WLAN ist nicht auf Echtzeitanforderungen ausgelegt.

Über ein Kabelnetz kannst du Quasi-Echtzeit erreichen indem du ein Netz aufbaust, das nur gering belastet ist (e.G. GBit Netz für 10MBit Echtzeit-Daten, wahrscheinlich würde 100MBit auch noch gehen).

Es gibt jedoch auch richtige Realtime Ethernet Standards. Dort gibt es unterschiedliche Pakettypen. Echtzeitpakete, die immer Vorrang haben und normale Ethernet-Pakete.
 
Hi!

Mir gefällt Dein erster Ansatz sehr gut, ist er doch der Einzige, der meinen bisherigen Lösungsweg nicht zunichte macht :)

Also, es kommt zu Paketkollisionen und deswegen wird das Paket verzögert. Da habe ich allerdings ein Verständnisproblem - eine WLAN-Karte, die mit UDP Paketen arbeitet verschickt doch ohnehin nur nach dem Prinzip "fire an forget". Da wird nicht vorher nachgelauscht, ob das WLAN gerade frei ist, da wird geschossen. Löschen sich zwei Pakete gegenseitig aus, weil zwei Geräte gleichzeitig senden, dann gibt es das Prinzip der "Acknowledges". Wird innerhalb einer sehr engen Zeitfensters kein ACK nach einem verschickten Paket empfangen, wird das Paket einfach erneut verschickt. Dies ist in allen von mir bisher beobachteten Treiberimplementationen dann maximal 11 mal der Fall, danach gilt das Paket als "verloren" und wird nicht mehr verschickt.

Kommt es also zu Kollisionen, also Auslöschungen, so bemerkt der Sender das NACH dem versenden und würde dann erneut verschicken.

Ein solches Verhalten hätte ich im wireshark gesehen - ich hätte eben ein zum Paket gehörendes, aber fehlendes ACK bemerkt oder eine mehrfache Aussendung des Paketes. Beides war aber nicht der Fall.

Generell scheint das aber Sinn zu ergeben, dass ein Zufallswert gewartet wird und erneut verschickt wird, beobachte ich doch einige Paket mit 4, einige mit 5,6,7,9,13 oder eben 19-20 ms Verzögerung. Das würde das erklären. Wie aber kann eine solche "Kollisionserkennung" technisch stattfinden? Das verstehe ich nicht...

Herakles
 
UDP ist auf Layer 4.

Da bei WLAN das Verlustrisiko sehr hoch ist, gibt es auf Layer 2 entsprechende Maßnahmen. Um dort einzugreifen müsstest du die Firmware der WLAN-Chips anpassen. Zumindest der Transport bis zum Access-Point ist also unabhängig vom gewählten Protokoll (TCP/UDP/ICMP) garantiert.

Kollisionen können alle Möglichen Störungen sein. Der Äther ist ein elektromagnetisches Chaos durch das Daten durchgeschleust werden. Hier kann wirklich eine Menge schiefgehen, das ist gar nicht anders zu erwarten.
 
Zurück
Oben