ping 테스트에서 타이머구현?

nayana의 이미지

#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 나와버립니다. 시간을 체크하는 루틴이 잘못된것인가요?

seoleda의 이미지

저도 예전에 시간 측정을 할일이 있어서 해봤는데,,

대부분의 함수에서 0초가 나오더군요.. 구현은 nayana이 사용하신 gettimeofday()을 사용한게 아니라 time()을 사용했지만 아마도 비슷한 이유 같습니다.

그때 기억에 의하면, 1초에 33번의 timer interrupt가 걸리는데, 이 인터럽트가 걸리기 전에 대부분의 함수의 수행이 끝나서 그렇다고 합니다.

해결방법은 다음처럼 동일한 함수를 적정한 횟수로 반복한 시간을 측정한 다음 반복횟수만큼 나눠주는 것입니다.

starttime;
for (int i=0; i=reps; i++){
    time_test_function();
}
endtime
time = (endtime-starttime)/reps;
sharefeel의 이미지

clock_gettime() 을 사용하시면,

struct timespec {
   time_t tv_sec;
   long    tv_nsec;
}
으로 시간 측정이 가능합니다.

마이크로 세컨드 단위인 gettimeofday()보다 좀 더 짧은 시간도 측정할 수 있겠지요.
빌드할때 -lrt 옵션 필요하구요.

===============
Vas Rel Por

버려진의 이미지

Quote:
$ ./a.out 202.43.214.190
[icmp](id:4093 seq:0 code:0 type:8 )
0 sec, 16 msec
8 bytes recv from (202.43.214.190) [icmp](id:4093 seq:0 code:0 type:0 )
[icmp](id:4093 seq:1 code:0 type:8 )
0 sec, 22 msec
8 bytes recv from (202.43.214.190) [icmp](id:4093 seq:1 code:0 type:0 )
[icmp](id:4093 seq:2 code:0 type:8 )
0 sec, 17 msec ..

잘 되는데요? ^^; 혹시 127.0.0.1로 하신거 아닌지 -.-;

litdream의 이미지

/bin/ping 에서 localhost 를 불러도 0보다 약간 큰값이 나오는데,
그것은 어떤 다른방법을 사용했을까요?

PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.071 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.073 ms

--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.071/0.075/0.081/0.004 ms

삽질의 대마왕...

nayana의 이미지

일단 로컬상에서만 테스트한것이 아니고 여러곳을 테스트 해보았는데 마찬가지 입니다.
그리고 clock_gettime함수를 쓰니까..레퍼런스가 없다는 메세지가 나옵니다. 물론 -lrt 컴파일 옵션을 주었습니다.

sharefeel의 이미지

제경우에는 (이라고 해봐야 Solaris와 리눅스 2.4만..)
제가 썼던 유닉스에서 저 함수가 없었던 적은 없습니다만..

지금 쓰는 시스템도 확인해보면 다음 파일 존재합니다.
/usr/lib/librt.so

혹시나 해서 한번 여쭤 보는 것은..

Quote:
그리고 clock_gettime함수를 쓰니까..레퍼런스가 없다는 메세지가 나옵니다. 물론 -lrt 컴파일 옵션을 주었습니다.

컴파일 옵션에 -lrt를 붙이셨다고 하는데..
아닐 거라고 생각합니다만..
Makefile 내부에서 CFLAGS에 추가하신 것은 아니신지요..

===============
Vas Rel Por

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.