Ich versuche, einen Benchmark durchzuführen, wie schnell UDP-Sockets in meinem lokalen Netzwerk sind.
Durch Pingen meiner lokalen IP-Adresse erhalte ich eine Roundtrip-Zeit von ~80 Mikrosekunden.
Ich habe einen minimalen UDP-Server und -Client geschrieben, um die Latenz wie folgt zu testen. Client und Server laufen auf demselben Host und verwenden dieselbe Netzwerkschnittstelle. Der Client sendet über Datagramm-Sockets einen Zeitwert an den Server, und nach dem Empfang berechnet der Server sofort die Differenz zwischen dem aktuellen Zeitwert und dem empfangenen Zeitwert. Auf diese Weise erhalte ich ~110 Mikrosekunden für eine einfache Fahrt, also insgesamt etwa ~220 Mikrosekunden RTT.
Warum ist Ping so viel schneller als mein Programm?
cc udpserver.c -o udpserver
cc udpclient.c -o udpclient
./udpserver 4321
./udpclient 127.0.0.1 4321
udpserver.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
struct timeval tval_recv, tval_now;
struct sockaddr_in serv_addr;
int n;
/* Check arguments */
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
/* Set up socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
/* Setup socket address for server */
memset((char *) &serv_addr, '\0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(atoi(argv[1]));
/* Bind socket to socket address */
if(bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
while(1) {
n = recvfrom(sockfd, &tval_recv, sizeof(tval_recv), 0, NULL, NULL);
if(n < 0)
error("ERROR in recvfrom");
gettimeofday(&tval_now, NULL);
printf("%ld.%06ld\n", tval_now.tv_sec - tval_recv.tv_sec,
(long int)(tval_now.tv_usec - tval_recv.tv_usec));
}
return 0;
}
udpclient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(char *msg) {
perror(msg);
exit(0);
}
int main(int argc, char **argv) {
in_addr_t s_addr;
int sockfd, portno, n;
struct sockaddr_in serveraddr;
struct timeval tval;
// struct timespec ts_current;
/* Check arguments */
if (argc != 3) {
fprintf(stderr,"Usage: %s <hostname> <port>\n", argv[0]);
exit(0);
}
/* Create the socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
error("Error opening socket");
/* Set the socket address */
s_addr = inet_addr(argv[1]);
portno = atoi(argv[2]);
memset((char *) &serveraddr, '\0', sizeof(serveraddr));
memcpy(&serveraddr.sin_addr.s_addr, &s_addr, sizeof(s_addr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
/* Send packets */
while(1) {
gettimeofday(&tval, NULL);
// clock_gettime(CLOCK_MONOTONIC, &ts_current);
n = sendto(sockfd, &tval, sizeof(tval), 0, (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
if(n < 0)
error("ERROR in sendto");
sleep(1);
}
return 0;
}
Antwort1
Wilde Vermutung:
ping
ist ein Tool, das das icmp
Protokoll verwendet. icmp
Befindet sich auf Schicht 3, der Netzwerkschicht, des OSI-Modells.
udpclient.c
ist ein Tool, das das udp
Protokoll verwendet. udp
Befindet sich auf Schicht 4, der Transportschicht des OSI-Modells.
Mit jeder Schicht werden den Rohdaten zusätzliche Daten hinzugefügt. Ein einfaches Beispiel: Ein icmp
Paket enthält eine Quell-IP-Adresse und eine Ziel-IP-Adresse. Ein udp
Datagramm enthält all das plus z. B. die UDP-Port-Informationen. Diese Port-Informationen müssen von einer höheren Ebene im Netzwerkstapel interpretiert werden.
UDP-Pakete müssen also eine Ebene weiter nach oben im Netzwerkstapel kriechen. Man könnte sich vorstellen, die Treppe zum 4. Stock statt zum 3. Stock zu nehmen. Das dauert länger, allerdings deutlich länger als 30µs.
Ich glaube, diese 30µs bestehen aus
- Datengröße
icmp
vs.udp
- NIC-/Treiber-/CPU-Zeit für die Kapselung und Entkapselung
- CPU-Zeit, die
udpclient.c
verwendet