2013-03-17 3 views
4

Я пишу программу ping с использованием сырых сокетов, но recvfrom не возвращает -1 с EINTR, хотя обрабатывается SIGALRM. Этот SIGALRM создается по моей тревоге (1). Я хочу recvfrom, чтобы вернуться, чтобы я мог решить, что пакет действительно потерян.recvfrom не возвращается -1 после сигнала

#include "libsock" 
#include <netinet/ip.h> 
#include <netinet/ip_icmp.h> 

double total=0, max=0, min=10000000; 
int totalpackets=0, packetslost=0; 
int recieved=0; 

void handler2() 
{ 
    printf("host unreachable\n"); 
} 

unsigned short 
csum (unsigned short *buf, int nwords) 
{ 
    unsigned long sum; 
    for (sum = 0; nwords > 0; nwords--) 
    sum += *buf++; 
    sum = (sum >> 16) + (sum & 0xffff); 
    sum += (sum >> 16); 
    return ~sum; 
} 

void 
handler() 
{ 
    printf("\n"); 
    printf("--------------PINGING STATISTICS----------------\n"); 
    printf("AVG:%f MAX:%f MIN:%f TOTAL PACKETS:%d PACKETS LOST:%d SUCCESS PERCENTAGE:%f\n\n",total/(totalpackets-packetslost),max,min,totalpackets,packetslost,((double)(totalpackets-packetslost)/(double)totalpackets)*100); 
    exit(0); 
} 

int 
main (int argc, char *argv[]) 
{ 
    if (argc != 2) 
    { 
    printf ("need destination for tracert\n"); 
    exit (0); 
    } 
    int sfd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 
    char buf[4096] = { 0 }; 

    int one = 1; 
    const int *val = &one; 
    if (setsockopt (sfd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) 
    printf ("Cannot set HDRINCL!\n"); 

    struct sockaddr_in addr; 
    struct ip* ip_hdr=(struct ip*)buf; 
    addr.sin_port = htons (7); 
    addr.sin_family = AF_INET; 
    inet_pton (AF_INET, argv[1], &(addr.sin_addr)); 

    ip_hdr->ip_hl = 5; 
    ip_hdr->ip_v = 4; 
    ip_hdr->ip_tos = 0; 
    ip_hdr->ip_len = 20 + 8 + 64; 
    ip_hdr->ip_id =0; 
    ip_hdr->ip_off = 64; 
    ip_hdr->ip_ttl = 64; 
    ip_hdr->ip_p = IPPROTO_ICMP; 
    inet_pton (AF_INET, "172.30.104.59", &(ip_hdr->ip_src)); 
    inet_pton (AF_INET, argv[1], &(ip_hdr->ip_dst)); 
    ip_hdr->ip_sum = csum ((unsigned short *) buf, 4); 

    struct icmphdr *icmphd = (struct icmphdr *) (buf+20); 
    icmphd->type = ICMP_ECHO; 
    icmphd->code = 0; 
    icmphd->checksum = 0; 
    icmphd->un.echo.id = 0; 
    icmphd->un.echo.sequence =1; 

    memset(buf+28,'a',64); 
    icmphd->checksum = csum ((unsigned short *) (buf+20), 36); 
    signal(SIGINT,handler); 
    struct timeval tv1,tv2; 

    printf("Pinging %s with 64 bytes of data.\n\n",argv[1]); 

    while(1) 
    { 
    recieved=0;  
    totalpackets++; 
    sendto (sfd, buf,20+ 8+64, 0, SA & addr, sizeof addr); 
    char buff[4096] = { 0 }; 
    struct sockaddr_in addr2; 
    socklen_t len = sizeof (struct sockaddr_in); 
    signal(SIGALRM,handler2); 
    gettimeofday(&tv1,NULL); 
    alarm(1); 
    int x=recvfrom (sfd, buff, 20+8+64, 0, SA & addr2, &len); 
    gettimeofday(&tv2,NULL); 
    double d=(double)(tv2.tv_sec-tv1.tv_sec)*1000+((double)(tv2.tv_usec-tv1.tv_usec))/1000; 

    if(x>0) { 
     struct icmphdr *icmphd2 = (struct icmphdr *) (buff + 20); 
     printf ("Reached destination:%s\tTime elapsed:%f ms\n\n", 
      inet_ntoa (addr2.sin_addr),d); 
     total+=d; 
     if(d>max) 
     max=d; 
     if(d<min) 
     min=d; 
    } else { 
     printf("Packet lost\n"); 
     packetslost++; 
    } 

    sleep(1); 
    } 
    return 0; 
} 

libsock содержит заголовки и SA = (STRUCT SOCKADDR *)

Is SIGALRM отличается от других сигналов, я не установил SA_RESTART ..

Спасибо.

+0

Рассматривали ли вы просто установив тайм-аут сокета? – nneonneo

+0

@nneonneo спасибо за это, не знал о тайм-ауте sock раньше, моя проблема решена, но я все еще удивляюсь, почему recvfrom не работает, как я думаю, это должно. – Sr1n4th

+3

1) обработчик сигнала должен иметь подпись 'void handler (int signum)' 2). Не используйте printf() в обработчике сигнала, он не является реентерабельным. – wildplasser

ответ

3

signal «s точное поведение зависит от системы. От man 2 signal:

семантика BSD эквивалентны вызову sigaction(2) со следующими флагами:

sa.sa_flags = SA_RESTART; 

Ситуация на Linux выглядит следующим образом:

...

По по умолчанию, в glibc 2 и более поздних версиях, оберточная функция signal() не вызывает системный вызов ядра. Вместо этого он вызывает sigaction (2) с использованием флагов , которые поставляют семантику BSD.

(курсив мой)

Таким образом, по умолчанию в системах Linux и BSD, signal установит SA_RESTART, который будет автоматически перезагрузить системный вызов, так что он никогда не сообщает EINTR.

Вы должны использовать sigaction, чтобы получить точный контроль над флагами:

struct sigaction sact = { 
    .sa_handler = handle_sigalrm, 
    .sa_flags = 0, 
}; 
sigaction(SIGALRM, &sact, NULL); 
2

Поведение использования «plain» signal в значительной степени зависит от ОС. Предполагая соответствие POSIX, вместо этого вы должны использовать sigaction. См. What is the difference between sigaction and signal? для получения дополнительной информации.

1

Вы не должны использовать signal, потому что он не переносится. Вместо этого используйте sigaction.

С опцией SA_RESTART блокирующий вызов будет молча возобновлен после того, как сигнал будет обнаружен. Без SA_RESTART блокирующий вызов останавливается, возвращает -1 и устанавливает errno на EINTR.

Вот функция mysignal, которую вы можете использовать для замены signal вызова с options набора 0:

int mysignal (int sig, void (*func)(int), int options) { 
    int r; 
    struct sigaction act; 

    act.sa_handler = func; 
    act.sa_flags = options; 
    sigemptyset (&act.sa_mask); 

    r = sigaction (sig, &act, NULL); 
    if (r < 0) perror (__func__); 
    return r; 
} 
Смежные вопросы