标签:
本文将讲解为什么服务器回复端口不可达,以及客户端socket 如何获取 端口不可达 信号。
首先,做为服务器,当一个报文经过查路由,目的ip是上送本机的时候,经过netfilter 判决后,
调用ip_local_deliver_finish,它根据ip头中的协议类型(TCP/UDP/ICMP/......),调用不同的4层接口函数进行处理。
对于udp而言,handler 是udp_rcv,它直接调用了__udp4_lib_rcv,查找相应的sock,
如果sk不存在if(sk != NULL),就回复icmp destination unreachable,函数非常简单
所以作为服务器,收到一个目的端口并未监听的报文,直接回复端口不可达。
那么作为客户端,如何处理服务器回复的 端口不可达 报文呢?
起始当初想法很简单,我认为,不同的协议之间是不会干涉的,即TCP和UDP直接是不会干涉的。
何况这种不伦不类的icmp?后来想错了。
作为客户端,端口不可达报文进入ip_local_deliver_finish,它调用icmp_rcv函数,进行处理。(其实这也是当初
我认为客户端udp不会对端口不可达数据进行相应的原因,因为udp处理流程是udp_rcv)。
icmp_rcv函数最重要的是 它调用了:icmp_pointers[icmph->type].handler(skb);
handler = icmp_unreach
icmp_unreach函数最终的一步,就是它最后一步:
是不是很像ip_local_deliver_finish?
是很像,只是ip_local_deliver_finish中,调用了ipprot->handler,而这里调用了ipprot->err_handler
对于udp,err_handler = udp_err = __udp4_lib_err
在该函数中,只有进入如下的流程,应用程序才会反应:
先决条件是inet->recverr为非0,或者inet->recverr为0但是udp处于TCP_ESTABLISHED状态。
否则应用程序休想收到该端口不可达的数据,应用程序就等着read超时吧。所以说,为了获取udp端口不可达的情况
有2种方法:
法1:
对udp进行connect操作,并且将sendto改成send
法2:
int val = 1;
setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));
udp获知端口不可达的源程序
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
unsigned char revc_buf[1024];
int main()
{
int fd,ret,recv_len,size=1024;
struct sockaddr_in server_addr,addr;
int val = 1;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("192.168.2.254");
server_addr.sin_port = htons(77);
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd < 0)
{
perror("socket fail ");
return -1;
}
printf("socket sucess\n");
//方法2
#if 1
setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));
if(sendto(fd, "nihao", strlen("nihao"), 0, (const struct sockaddr *)&(server_addr), sizeof(struct sockaddr_in))<0)
{
perror("sendto fail ");
return -1;
}
printf("sendto sucess\n");
recv_len = recvfrom(fd, revc_buf, sizeof(revc_buf), 0, (struct sockaddr *)&addr, (int *)&size);
printf("recv_len:%d sucess\n");
<span style="font-size:18px;"></span><pre name="code" class="cpp"> //方法1
#elif 0
ret = connect(fd, (const struct sockaddr *) &(server_addr), sizeof (struct sockaddr_in));
if(ret < 0)
{
printf("connect fail\n");
return -1;
}
ret = send(fd, "ni hao", strlen("nihao"),0);
if(ret < 0)
{
printf("write fail\n");
return -1;
}
ret = recvfrom(fd, revc_buf, sizeof(revc_buf), 0, (struct sockaddr *)&addr, (int *)&size);
if(ret < 0)
{
printf("read fail\n");
return -1;
}
#endif
close(fd);
return 0;
}
关于UDP接收icmp端口不可达(port unreachable)
标签:
原文地址:http://blog.csdn.net/mrpre/article/details/43451775