最近得写个udpAgent,由于要做udpServer端的容灾,所以要感知udpAgent发出的UDP包是否被某个udpServer进程接收。但是UDP协议本身是无连接和无状态的,也就是说默认情况下,udpAgent进程是无法感知其发出的UDP包是否被成功接收。

但是,从TCP/IP协议栈来说,如果一个UDP包没有到达目的地址,发送端会收到一个的ICMP不可达报文。所以现在的问题是如何让进程捕获到这个ICMP不可达错误,结论就是调用connect( )函数,即使用有连接的UDP。

// ================================================================================================

这里做个简单实验:

1、无连接的UDP:Socket( )=>SendTo( )

2、有连接的UDP:Socket( )=>Connect( )=>Send( )

下面是基础的无连接UDP客户端代码(SendUdp_NoCon.cpp):发送2个UDP包到一个不存在的目标地址(127.0.0.1:8888)

  1. #include <iostream>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>
  7. #include <netdb.h>
  8. using namespace std;
  9. int main()
  10. {
  11. int i, iSvrSock, iRet;
  12. string strData(”Hello World!”);
  13. struct sockaddr_in sSvrAddr;
  14. memset(&sSvrAddr, 0, sizeof(sSvrAddr));
  15. sSvrAddr.sin_family = AF_INET;
  16. sSvrAddr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
  17. sSvrAddr.sin_port = htons(8888);
  18. // 1.Socket()
  19. iSvrSock = socket(AF_INET, SOCK_DGRAM, 0);
  20. for (i = 0; i < 2; i ++)
  21. {
  22. // 2.Sendto()
  23. iRet = sendto(iSvrSock, strData.data(), strData.size(), 0, (struct sockaddr*)&sSvrAddr, sizeof(sSvrAddr));
  24. if (iRet < 0) {
  25. cout << “SendUdp Err, Return:” << iRet << ” Info:” << strerror(errno) << endl;
  26. } else {
  27. cout << “SendUdp Suc” << endl;
  28. }
  29. sleep(1);
  30. }
  31. close(iSvrSock);
  32. return 0;
  33. }

编译并执行,进程并没有感知UDP包是否被接收:

  1. [andy@localhost 201309]$ g++ -o NoCon SendUdp_NoCon.cpp </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">[andy@localhost <span class="hljs-number">201309</span>]$ ./NoCon
  2. SendUdp Suc
  3. SendUdp Suc 

通过tcpdump抓包可以发现TCP/IP协议栈有收到ICMP不可达报文:

  1. [root@localhost ~]# tcpdump -i lo "udp or icmp" -n -s1500
  2. tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
  3. listening on lo, link-type EN10MB (Ethernet), capture size 1500 bytes
  4. 17:32:38.963669 IP 127.0.0.1.60835 > 127.0.0.1.ddi-udp-1: UDP, length 12
  5. 17:32:38.963698 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 udp port ddi-udp-1 unreachable, length 48
  6. 17:32:39.963939 IP 127.0.0.1.60835 > 127.0.0.1.ddi-udp-1: UDP, length 12
  7. 17:32:39.963966 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 udp port ddi-udp-1 unreachable, length 48

下面是有连接UDP客户端代码(SendUdp_Con.cpp):也是发送2个UDP包到一个不存在的目标地址(127.0.0.1:8888)

  1. #include <iostream>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>
  7. #include <netdb.h>
  8. using namespace std;
  9. int main()
  10. {
  11. int i, iSvrSock, iRet;
  12. string strData("Hello World!");
  13. struct sockaddr_in sSvrAddr;
  14. memset(&sSvrAddr, 0, sizeof(sSvrAddr));
  15. sSvrAddr.sin_family = AF_INET;
  16. sSvrAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  17. sSvrAddr.sin_port = htons(8888);
  18. // 1.Socket()
  19. iSvrSock = socket(AF_INET, SOCK_DGRAM, 0);
  20. // 2.Connect()
  21. connect(iSvrSock, (struct sockaddr*)&sSvrAddr, sizeof(sSvrAddr));
  22. for (i = 0; i < 2; i ++)
  23. {
  24. // 3.Send()
  25. iRet = send(iSvrSock, strData.data(), strData.size(), 0);
  26. if (iRet < 0) {
  27. cout << "SendUdp Err, Return:" << iRet << " Info:" << strerror(errno) << endl;
  28. } else {
  29. cout << "SendUdp Suc" << endl;
  30. }
  31. sleep(1);
  32. }
  33. close(iSvrSock);
  34. return 0;
  35. }

编译并执行,这次进程感知到了发送的UDP错误 Connection refused:

  1. [andy@localhost 201309]$ g++ -o Con SendUdp_Con.cpp </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">[andy@localhost <span class="hljs-number">201309</span>]$ ./Con
  2. SendUdp Suc
  3. SendUdp Err, Return:-1 Info:Connection refused

由上面的输出可知,是在Socket第2次sent( )的时候才捕获到这个Connection refused错误。这里并不是说第1次send( )的UDP包被成功接收了,而是因为UDP的send( )/sendto( )只是将UDP包的数据拷贝到发送缓冲区就立即返回了,等到第2次send( )的时候错误才被捕获,这种现象为异步错误。

// ================================================================================================

为什么无连接UDP没有把ICMP错误通知给进程?

我的理解是,由于无连接UDP你可以连续调用多个sendto( )函数,将UDP包发送给多个目的地址。由于ICMP不可达错误对于进程来说为异步错误,所以如果就算进程捕获了这个错误,也不知道是哪个目的地址不可达。如下面的例子,连续调用3次sendto将UDP包发给3个目的地址:

  1. iRet1 = sendto(iSvrSock, strData.data(), strData.size(), 0, (struct sockaddr*)&sSvrAddr1, sizeof(sSvrAddr1));
  2. iRet2 = sendto(iSvrSock, strData.data(), strData.size(), 0, (struct sockaddr*)&sSvrAddr2, sizeof(sSvrAddr2));
  3. iRet3 = sendto(iSvrSock, strData.data(), strData.size(), 0, (struct sockaddr*)&sSvrAddr3, sizeof(sSvrAddr3));

如果iRet3返回了Connection refused错误,也无法判断是sSvrAddr1还是sSvrAddr2不可达。

udpSocket调用connect( )并不会进行“3次握手”连接

tcpSocket调用connect( )的时候,实际上是TCP/IP协议栈“3次握手”建立连接的过程。但是对于udpSocket,调用connect( )只是在进程绑定目的地址而已,不会向目的地址发送任何数据。实际上,udpocket调用connect( )之后,其只是在TCP/IP协议栈内绑定一个(协议/源IP/源端口/目的IP/目的端口)的五元组,一直维护到连接结束。

       无连接UDP调用sendto( )和有连接UDP调用send( )的联系和区别

实际上,无连接UDP调用1次sendto( )发送UDP包,系统要做3件事:连接=>发送=>断开连接。而有连接UDP的send( )由于已经连接好了,只需完成”发送”这一步,故有连接UDP在性能上要由于无连接UDP。

源链接:https://blog.csdn.net/yyyiran/article/details/12197365

udpSocket捕获ICMP不可达错误相关推荐

  1. [vue] 怎么捕获组件vue的错误信息?

    [vue] 怎么捕获组件vue的错误信息? errorHandler 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

  2. reactjs错误边界:用来捕获后代组件的错误,渲染出备用页面

    8. 错误边界 理解: 错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面 特点: 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件.定时 ...

  3. promise 中的异常捕获_promise 中的错误处理

    js 中的错误处理. 分为三块吧. 第一:普通的js中 错误处理. 第二: promise 中的错误处理 第三: async 中的错误处理 第一: 普通的js 的错误处理 说普通是因为比较简单,算一个 ...

  4. android 获取堆栈地址,关于java native interface:如何捕获SIGSEGV(分段错误)并在Android下的JNI下获取堆栈跟踪?...

    我正在将一个项目转移到新的Android本机开发工具包(即JNI)中,我想捕获sigsegv,如果它发生(也可能是sigill.sigabrt.sigfpe),以便呈现一个很好的崩溃报告对话框,而不是 ...

  5. 处理手机点击淘宝优惠链接提示“目标地址不可达”错误

    注意: 该案例针对性较强, 如果你的网络环境中没有AdGuard之类的广告过滤器在工作, 可能对你并不适用 最近发现手机上从优惠App访问淘宝的优惠商品链接 频繁会提示"服务竟然出错了 目标 ...

  6. linux的网络不可达问题,我的服务器日志中的linux – (网络不可达)错误

    我的Centos消息日志文件中有很多网络无法访问的行.他们似乎无法解决某些地址,我没有任何想法,为什么我的服务器必须首先解决它们.谁能让我知道这种错误的根源?我受到了攻击吗? Oct 23 11:39 ...

  7. pip显示网络不可达错误解决

    解决安装python包网络不可达问题 如图, 升级pip3报网络不可达的错,安装某些python包也失败,原始的pip3是从国外拿资源,速度慢,资源少,这里我们可以用华科大的镜像,操作如下: sudo ...

  8. linux 网络不可达 网卡,我的服务器日志中的linux – (网络不可达)错误

    我的Centos消息日志文件中有很多网络无法访问的行.他们似乎无法解决某些地址,我没有任何想法,为什么我的服务器必须首先解决它们.谁能让我知道这种错误的根源?我受到了攻击吗? Oct 23 11:39 ...

  9. java android 堆栈_如何在Android上的JNI下捕获SIGSEGV(分段错误)并获得堆栈跟踪?...

    ITMISS 从Jelly Bean开始,您无法获取堆栈跟踪,因为READ_LOGS走了.:-(实际上,我的信号处理程序在工作时没有做任何多余的事情,并且已经发布了使用它的代码,您可以在github上 ...

  10. linux主机网络异常日志,我的服务器日志中的linux – (网络不可达)错误

    我的Centos消息日志文件中有很多网络无法访问的行.他们似乎无法解决某些地址,我没有任何想法,为什么我的服务器必须首先解决它们.谁能让我知道这种错误的根源?我受到了攻击吗? Oct 23 11:39 ...

最新文章

  1. python3.7.2教程-centos7系统下python2与python3共存
  2. java wed登录面 代码_Java Web用户登录实例代码
  3. python多值参数函数介绍,数字累加例子
  4. php导出excel失败原因,PHPExcel导出Excel文件报找不到该文件错误
  5. 计算机学业水平考试及格,信息技术学业水平考试表格部分试题(带答案)
  6. GuessedAtParserWarning: No parser was explicitly specified,
  7. [APIO2009]抢掠计划 ($Tarjan$,最长路)
  8. python机器学习——文本情感分析(英文文本情感分析)
  9. 视频图像格式YUV详解
  10. AD15 PCB规则检查,unplated pad
  11. java 四级联动,省市区镇四级联动附代码和数据库
  12. 74ls390设计任意进制计数器_《设计任意进制计数器》的实验报告
  13. chromium浏览器定制 | 高匿名爬虫随机指纹
  14. 备案服务器查询网站,网站服务器备案查询
  15. writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)
  16. 500Illegal PORT command 错误 无法取得目录列表 8uftp
  17. xcode 配置wechat_Xcode 真机调试微信支付 提示 mainfest.json配置APPID和订单的appid 不一致...
  18. 2017暑假训练第一场的一些题目
  19. 什么是继承python_面向对象继承
  20. SSH struts2漏洞升级2.5.30

热门文章

  1. Java实现图书管理系统
  2. java图书管理系统毕业设计_java图书管理系统毕业论文
  3. 欧姆龙CP/CJ系列PLC包含哪些通讯方式呢?
  4. Python语言程序设计基础_期末作品设计——程序员计算器(2020-2 A)_答案_通识教育必修课程_上海师范大学
  5. matlab做拉普拉斯反演,拉普拉斯变换反演
  6. 炫酷大屏demo_周末大放送 16几个炫酷的HTML5动画锦集
  7. android记账本流程图,记账的基本流程
  8. 小米3g刷高格固件_高恪可刷机型清单
  9. c++课设作业之课程信息管理系统
  10. 报错:[Microsoft][ODBC 驱动程序管理器] 无效的字符串或缓冲区长度