recvfrom未返回信号后为-1
我正在使用原始套接字编写ping程序,但recvfrom不会返回-1和EINTR,即使正在处理SIGALRM。此SIGALRM由我的警报(1)生成。我希望 recvfrom返回,这样我就可以确定数据包确实丢失了recvfrom未返回信号后为-1,c,sockets,icmp,raw-sockets,C,Sockets,Icmp,Raw Sockets,我正在使用原始套接字编写ping程序,但recvfrom不会返回-1和EINTR,即使正在处理SIGALRM。此SIGALRM由我的警报(1)生成。我希望 recvfrom返回,这样我就可以确定数据包确实丢失了 #include "libsock" #include <netinet/ip.h> #include <netinet/ip_icmp.h> double total=0, max=0, min=10000000; int totalpackets=0, pa
#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”
#包括
#包括
双倍总计=0,最大值=0,最小值=10000000;
int totalpackets=0,packetslost=0;
接收的int=0;
无效句柄2()
{
printf(“无法访问主机\n”);
}
无符号短
csum(无符号短*buf,整数nwords)
{
无符号长和;
对于(总和=0;nwords>0;nwords--)
总和+=*buf++;
总和=(总和>>16)+(总和&0xffff);
总和+=(总和>>16);
返回~求和;
}
无效的
handler()
{
printf(“\n”);
printf(“------------ping统计-----------------\n”);
printf(“平均值:%f最大值:%f最小值:%f总数据包:%d丢失的数据包:%d成功百分比:%f\n\n”,总计/(totalpackets packetslost),最大值,最小值,totalpackets,packetslost,((双精度)(totalpackets packetslost)/(双精度)TotalPacketsLost)*100);
出口(0);
}
int
main(int argc,char*argv[])
{
如果(argc!=2)
{
printf(“tracert的需要目的地\n”);
出口(0);
}
int sfd=插座(AF INET、SOCK RAW、IPPROTO ICMP);
char buf[4096]={0};
int-one=1;
常量int*val=&one;
如果(设置锁定选项(sfd、IPPROTO_IP、IP_HDRINCL、val、尺寸(一个))<0)
printf(“无法设置HDRINCL!\n”);
地址中的结构sockaddr\u;
结构ip*ip_hdr=(结构ip*)buf;
地址sin_端口=htons(7);
addr.sin_family=AF_INET;
地址(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));
网络(自动网络,argv[1],&(ip_hdr->ip_dst));
ip_hdr->ip_sum=csum((无符号短*)buf,4);
结构icmphdr*icmphd=(结构icmphdr*)(buf+20);
ICMPPH->type=ICMP\u ECHO;
ICMPPH->code=0;
ICMPPH->校验和=0;
icmphd->un.echo.id=0;
icmphd->un.echo.sequence=1;
记忆集(buf+28,'a',64);
ICMPPH->校验和=csum((无符号短*)(buf+20),36);
信号(SIGINT,handler);
结构时间值tv1,tv2;
printf(“用64字节的数据ping%s。\n\n”,argv[1]);
而(1)
{
接收=0;
totalpackets++;
发送至(sfd、buf、20+8+64、0、SA&addr、sizeof addr);
char buff[4096]={0};
addr2中的结构sockaddr_;
socklen\u t len=sizeof(结构sockaddr\u in);
信号机(信号机、手柄2);
gettimeofday(&tv1,NULL);
警报(1);
int x=recvfrom(sfd、buff、20+8+64、0、SA和addr2以及len);
gettimeofday(&tv2,NULL);
双d=(双)(tv2.tv_-sec-tv1.tv_-sec)*1000+((双)(tv2.tv-usec-tv1.tv_-usec))/1000;
如果(x>0){
结构icmphdr*icmphd2=(结构icmphdr*)(buff+20);
printf(“到达目标:%s\t运行时间:%f ms\n\n”,
(地址2.sin_addr),d);
总数+=d;
如果(d>最大值)
max=d;
如果(d使用“普通”signal
的行为在很大程度上取决于操作系统。假设POSIX一致性,您应该使用sigaction
。有关更多信息,请参阅。您不应该使用sigaction
,因为它不可移植。相反,请使用sigaction
使用选项SA_RESTART
,捕捉到信号后,阻塞呼叫将以静默方式恢复。
没有SA_RESTART
,阻塞调用停止,返回-1并将errno
设置为EINTR
这是一个函数mysignal
,您可以使用它将信号
调用替换为选项
设置为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;
}
int-mysignal(int-sig,void(*func)(int),int-options){
INTR;
结构动作法;
act.sa_handler=func;
act.sa_标志=选项;
sigemptyset(和act.sa_面具);
r=sigaction(sig,&act,NULL);
if(r<0)perror(_func__);
返回r;
}
信号的确切行为取决于系统。来源:
BSD语义相当于调用sigaction(2)
,如下所示
旗帜:
sa.sa_flags = SA_RESTART;
Linux上的情况如下:
默认情况下,在glibc 2及更高版本中,signal()
包装函数不起作用
调用内核系统调用。相反,它使用
提供BSD语义的标志。
(强调矿山)
因此,默认情况下,在Linux和BSD系统上,signal
将设置sau RESTART
,这将自动重新启动系统调用,以便它永远不会报告EINTR
您需要使用sigaction
来精确控制标志:
sa.sa_flags = SA_RESTART;
struct sigaction sact = {
.sa_handler = handle_sigalrm,
.sa_flags = 0,
};
sigaction(SIGALRM, &sact, NULL);
你考虑过只设置套接字超时吗?@NNONNEO谢谢你,我以前不知道套接字超时,我的问题已经解决了,但我仍然想知道为什么recvfrom不能像我认为的那样工作。1)信号处理程序应该有签名void handler(int signum)
2)不要使用printf()在信号处理程序中,它是不可重入的。当printf在malloc内时,信号可能到达,以(重新)分配其缓冲区。或者只是更新缓冲区内的指针。@EdouardThiel:很难让它失败:你必须在“外部”发生时触发信号<代码> Prtff <代码>处于某个敏感操作的中间。然后,在与任何其他基于线程或中断驱动的多处理系统相同的重新进入情况下,“代码> Prtff<代码>内部工作可以尝试最小化窗口的腐败(我在我的工作中)。但有些事情似乎不值得去防范。