Por que o ping é muito mais rápido que os soquetes UDP?

Por que o ping é muito mais rápido que os soquetes UDP?

Estou tentando avaliar a velocidade dos soquetes UDP na minha rede local.

Ao executar ping em meu endereço IP local, obtenho um tempo de ida e volta de aproximadamente 80 microssegundos.

Escrevi um servidor e cliente UDP mínimo para avaliar a latência da seguinte maneira. O cliente e o servidor estão sendo executados no mesmo host, usando a mesma interface de rede. O cliente envia um intervalo de tempo para o servidor por meio de soquetes de datagrama e, após o recebimento, o servidor imediatamente calcula a diferença entre o intervalo de tempo atual e o intervalo de tempo recebido. Fazendo isso, obtenho aproximadamente 110 microssegundos para uma viagem só de ida, para um total de cerca de aproximadamente 220 microssegundos RTT.

Por que o ping é muito mais rápido que o meu 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;
}

Responder1

Palpite ousado:

pingé uma ferramenta que usa o icmpprotocolo. icmpfica na camada 3, a camada de rede, do modelo OSI.

udpclient.cé uma ferramenta que usa o udpprotocolo. udpfica na camada 4, a camada de transporte, do modelo OSI.

Com cada camada, dados adicionais serão adicionados aos dados brutos. Como exemplo básico: um icmppacote contém um endereço IP de origem e um endereço IP de destino. Um udpdatagrama contém tudo isso, além, por exemplo, das informações da porta UDP. Essas informações de porta precisam ser interpretadas por um nível superior na pilha da rede.

Portanto, os pacotes UDP precisam subir mais um nível na pilha da rede. Poderíamos imaginar subir as escadas para o 4º andar em vez do 3º andar. Levará mais tempo, porém muito mais que 30 µs.

Acredito que esses 30µs consistem em

  • tamanho dos dados icmpversusudp
  • Tempo de NIC/driver/CPU para encapsular e desencapsular
  • Tempo de CPU que udpclient.cusa

informação relacionada