로컬 네트워크에서 UDP 소켓이 얼마나 빠른지 벤치마킹하려고 합니다.
로컬 IP 주소를 ping하면 ~80마이크로초의 왕복 시간을 얻을 수 있습니다.
대기 시간을 벤치마킹하기 위해 최소한의 UDP 서버와 클라이언트를 다음과 같이 작성했습니다. 클라이언트와 서버는 동일한 네트워크 인터페이스를 사용하여 동일한 호스트에서 실행됩니다. 클라이언트는 데이터그램 소켓을 통해 서버에 timeval을 보내고, 수신 시 서버는 즉시 현재 timeval과 수신된 timeval 사이의 차이를 가져옵니다. 이렇게 하면 편도 이동에 대해 ~110마이크로초, 총 약 ~220마이크로초 RTT를 얻습니다.
ping이 내 프로그램보다 훨씬 빠른 이유는 무엇입니까?
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;
}
답변1
추측:
ping
프로토콜 을 사용하는 도구입니다 icmp
. icmp
OSI 모델의 네트워크 계층인 계층 3에 위치합니다.
udpclient.c
프로토콜 을 사용하는 도구입니다 udp
. udp
OSI 모델의 전송 계층인 계층 4에 위치합니다.
각 레이어마다 추가 데이터가 원시 데이터에 추가됩니다. 기본 예를 들면, icmp
패킷에는 소스 IP 주소와 대상 IP 주소가 포함되어 있습니다. 데이터 udp
그램에는 UDP 포트 정보와 같은 모든 정보가 포함됩니다. 이 포트 정보는 네트워크 스택의 더 높은 수준에서 해석되어야 합니다.
따라서 UDP 패킷은 네트워크 스택에서 한 수준 더 위로 크롤링해야 합니다. 3층이 아닌 4층으로 올라가는 계단을 상상해볼 수도 있다. 시간은 더 걸리지만 30μs보다 훨씬 더 오래 걸립니다.
나는 그 30μs가 다음으로 구성되어 있다고 믿습니다.
- 데이터 크기
icmp
대udp
- 캡슐화 및 캡슐화 해제를 위한 NIC/드라이버/CPU 시간
udpclient.c
사용하는 CPU 시간