¿Por qué el ping es mucho más rápido que los sockets UDP?

¿Por qué el ping es mucho más rápido que los sockets UDP?

Estoy intentando comparar la velocidad de los sockets UDP en mi red local.

Al hacer ping a mi dirección IP local, obtengo un tiempo de ida y vuelta de ~80 microsegundos.

Escribí un servidor y un cliente UDP mínimos para comparar la latencia de la siguiente manera. El cliente y el servidor se ejecutan en el mismo host y utilizan la misma interfaz de red. El cliente envía un valor de tiempo al servidor a través de sockets de datagramas y, al recibirlo, el servidor toma inmediatamente la diferencia entre el valor de tiempo actual y el valor de tiempo recibido. Al hacer esto, obtengo ~110 microsegundos para un viaje de ida, para un total de alrededor de ~220 microsegundos RTT.

¿Por qué el ping es mucho más rápido que mi programa?

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;
}

Respuesta1

Suposición salvaje:

pinges una herramienta que utiliza el icmpprotocolo. icmpse encuentra en la capa 3, la capa de red, del modelo OSI.

udpclient.ces una herramienta que utiliza el udpprotocolo. udpse encuentra en la capa 4, la capa de transporte, del modelo OSI.

Con cada capa, se agregarán datos adicionales a los datos sin procesar. Como ejemplo básico: un icmppaquete contiene una dirección IP de origen y una dirección IP de destino. Un udpdatagrama contiene todo eso más, por ejemplo, la información del puerto UDP. Esta información de puerto debe ser interpretada por un nivel superior en la pila de red.

Por lo tanto, los paquetes UDP tienen que subir un nivel más en la pila de red. Uno podría imaginarse subiendo las escaleras hasta el cuarto piso en lugar del tercer piso. Tardará más, aunque mucho más que 30 µs.

Creo que esos 30 µs consisten en

  • tamaño de datos icmpvs.udp
  • Tiempo de NIC/controlador/CPU para encapsular y decapsular
  • Tiempo de CPU que udpclient.cutiliza

información relacionada