ping 테스트에서 타이머구현?
글쓴이: nayana / 작성시간: 월, 2005/02/14 - 6:11오후
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <unistd.h> #include <sys/time.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/in.h> #define BUFSIZE 4096 int seqnum; // ping 메시지 일련번호 char recvbuf[ BUFSIZE ]; // 수신버퍼 char sendbuf[ BUFSIZE ]; // 송신버퍼 int rawsock; // Raw 소켓 번호 int notrecv = 0; // ping 응답을 받지 못한 회수 struct timeval atime; int diff1; int diff2; struct sockaddr_in sendaddr, recvaddr; int send_ping(); // ping request int prn_rcvping( char *ipdata, int recvsize ); // ping 응답에 출력 void prn_icmp( struct icmphdr *icmp, int icmpsize ); // ICMP 헤더 출력 unsigned short in_cksum( unsigned short *addr, int len ); // ICMP Check sum void errquit(char *msg) { perror(msg); exit(0); } int main( int argc, char **argv ) { int recvsize, addrlen = sizeof( struct sockaddr ); fd_set readset; struct timeval tv; int ret; struct timeval ltime; if( argc != 2 ) { printf( "Usage : %s ip_address \n", argv[ 0 ] ); exit( 1 ); } addrlen = sizeof( struct sockaddr ); bzero( &recvaddr, sizeof( struct sockaddr ) ); bzero( &sendaddr, sizeof( struct sockaddr ) ); sendaddr.sin_family = AF_INET; inet_pton( AF_INET, argv[ 1 ], &sendaddr.sin_addr.s_addr ); sendaddr.sin_port = htons( 0 ); // raw 소켓 생성 rawsock = socket( AF_INET, SOCK_RAW, IPPROTO_ICMP ); if( rawsock < 0 ) errquit( "socket fail" ); // 커널에 상대의 주소를 기억해둠 if( connect( rawsock, ( struct sockaddr* )&sendaddr, sizeof( struct sockaddr) ) != 0 ) errquit("connect fail "); // 첫번째 ping 보내기 while( 1 ) { FD_ZERO( &readset ); FD_SET( rawsock, &readset ); tv.tv_sec = 1; // 1초 타이머 tv.tv_usec = 0; send_ping(); // ping을 보냄 ret = select( rawsock + 1, &readset, NULL,NULL,&tv ); // 타이머 가동 if( ret == 0 ) { // 타임아웃 if( ++notrecv == 3 ) { notrecv = 0; puts( "Request Timeout ... " ); } continue; } else if( ret < 0 ) // select()의 에러 errquit("select fail"); // select()의 정상리턴, ping 응답을 읽음 recvsize = recvfrom( rawsock, recvbuf, sizeof( recvbuf ),0, (struct sockaddr*)&recvaddr, &addrlen ); if( recvsize < 0 ) errquit("recvfrom fail "); notrecv = 0; // 수신된 응답에 대한 처리 gettimeofday( &atime, NULL ); diff2 = ( ( atime.tv_sec % 100 ) * 10000 ) + ( atime.tv_usec / 100 ); int subtime = diff2 - diff1; ltime.tv_sec = subtime / 1000; ltime.tv_usec = subtime / 10; printf( " %ld sec, %ld msec\n", ltime.tv_sec, ltime.tv_usec ); prn_rcvping( recvbuf, recvsize ); sleep( 1 );// 1초 간격으로 ping을 보냄 } exit( 0 ); } void prn_icmp( struct icmphdr *icmp, int icmpsize ) { printf( "[icmp](id:%d ", icmp->un.echo.id ); printf( "seq:%d " , icmp->un.echo.sequence ); printf( "code:%d " , icmp->code ); printf( "type:%d )\n" , icmp->type ); } // 수신된 메시지를 출력 int prn_rcvping( char *ipdata, int recvsize ) { int ip_headlen, icmp_len; struct icmphdr* icmp; struct iphdr* ip; char buf[ 512 ]; ip = (struct iphdr*)ipdata; ip_headlen = ip->ihl * 4; icmp_len = recvsize - ip_headlen; icmp = (struct icmphdr *)( ipdata + ip_headlen ); if ( icmp->type != ICMP_ECHOREPLY ) return -1; inet_ntop( AF_INET,(void*)&ip->saddr, buf, sizeof( buf ) ); printf( "%d bytes recv from (%s) ", icmp_len, buf ); prn_icmp( icmp, icmp_len ); return 0; } // ping Request 보내기 int send_ping() { gettimeofday( &atime, NULL ); diff1 = ( ( atime.tv_sec % 100 ) * 10000 ) + ( atime.tv_usec / 100 ); struct icmphdr* icmp; int len, sendsize; icmp = (struct icmphdr *)sendbuf; bzero( (char *)icmp, sizeof(struct icmp) ); icmp->code = 0 ; icmp->type = ICMP_ECHO; // ICMP_ECHO = 8 icmp->un.echo.sequence = seqnum++; // Ping 메시지 일련번호 icmp->un.echo.id = getpid(); // pid 를 ID로 설정 icmp->checksum = 0; // checksum 계산전 반드시 zero icmp->checksum = in_cksum( ( unsigned short *)icmp, sizeof( struct icmp ) ); len = sizeof(struct icmphdr); // 8 byte sendsize = sendto( rawsock, sendbuf, len, MSG_DONTWAIT, (struct sockaddr*)&sendaddr, sizeof( struct sockaddr ) ); prn_icmp( icmp, sendsize ); // ICMP 헤더 출력 return sendsize; } // checksum 구하기 unsigned short in_cksum( unsigned short *addr, int len ) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while( nleft > 1 ) { sum += *w++; nleft -= 2; } if( nleft == -1 ) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = ( sum >> 16 ) + (sum & 0xffff); sum += ( sum >> 16 ); answer = ~sum; return (answer); }
간단하게 ping 프로그램입니다. ping 테스트할때 시간이 얼마나 걸리는지 시간을 체크하였는데...0 sec 0 msec 가 나와버립니다.
운영체제 안에 있는 ping 프로그램을 사용하면 어느 정도 시간이 걸리는데... 위의 코드로 테스트 하면 무조건 0 sec 0 msec 나와버립니다. 시간을 체크하는 루틴이 잘못된것인가요?
Forums:
milisecond 단위는 잘 측정이 안되던데요..
저도 예전에 시간 측정을 할일이 있어서 해봤는데,,
대부분의 함수에서 0초가 나오더군요.. 구현은 nayana이 사용하신 gettimeofday()을 사용한게 아니라 time()을 사용했지만 아마도 비슷한 이유 같습니다.
그때 기억에 의하면, 1초에 33번의 timer interrupt가 걸리는데, 이 인터럽트가 걸리기 전에 대부분의 함수의 수행이 끝나서 그렇다고 합니다.
해결방법은 다음처럼 동일한 함수를 적정한 횟수로 반복한 시간을 측정한 다음 반복횟수만큼 나눠주는 것입니다.
clock_gettime() 을 사용하시면,[code:1]struct
clock_gettime() 을 사용하시면,
으로 시간 측정이 가능합니다.마이크로 세컨드 단위인 gettimeofday()보다 좀 더 짧은 시간도 측정할 수 있겠지요.
빌드할때 -lrt 옵션 필요하구요.
===============
Vas Rel Por
[quote]$ ./a.out 202.43.214.190[icmp](
잘 되는데요? ^^; 혹시 127.0.0.1로 하신거 아닌지 -.-;
/bin/ping 에서 localhost 를 불러도 0보다 약간 큰값이
/bin/ping 에서 localhost 를 불러도 0보다 약간 큰값이 나오는데,
그것은 어떤 다른방법을 사용했을까요?
삽질의 대마왕...
일단 로컬상에서만 테스트한것이 아니고 여러곳을 테스트 해보았는데 마찬가지
일단 로컬상에서만 테스트한것이 아니고 여러곳을 테스트 해보았는데 마찬가지 입니다.
그리고 clock_gettime함수를 쓰니까..레퍼런스가 없다는 메세지가 나옵니다. 물론 -lrt 컴파일 옵션을 주었습니다.
제경우에는 (이라고 해봐야 Solaris와 리눅스 2.4만..)제가
제경우에는 (이라고 해봐야 Solaris와 리눅스 2.4만..)
제가 썼던 유닉스에서 저 함수가 없었던 적은 없습니다만..
지금 쓰는 시스템도 확인해보면 다음 파일 존재합니다.
/usr/lib/librt.so
혹시나 해서 한번 여쭤 보는 것은..
컴파일 옵션에 -lrt를 붙이셨다고 하는데..
아닐 거라고 생각합니다만..
Makefile 내부에서 CFLAGS에 추가하신 것은 아니신지요..
===============
Vas Rel Por
댓글 달기