多网口UDP发包无法收到回包排查与解决

news2024/10/7 7:25:23

最近几周几乎都是单休,加班很多,也遇到了很多未知的问题,杂事也多时间比较紧张,也没有多少空余来进行一些总结积累。这点让我很是怀念起几年前的日子,任务安排周期长,做技术纯粹又专心。

前几天遇到了一个UDP发包无法收到回包的问题,趁着今天不加班来记录回顾一下。

网络环境

两台主机,每台主机上分别有两个网口。

主机1的Ip地址:

  • 管理口:192.168.1.2
  • 网口一:2006:470:8192:beef:6114:e3d6:a597:aea6
  • 网口二:2006:470:8192:beef:d45e:a0fa:9c9c:2e4b

主机2的Ip地址:

  • 管理口:192.168.1.3
  • 网口一:2005:470:8192:beef:9020:c015:a801:1301
  • 网口二:2005:470:8192:beef:68af:d2e6:a0e3:6fb1

如下图:
tp

udpServer

func udpServerStart() {
	srcAddr := &net.UDPAddr{
		IP:   net.IPv6zero,
		Port: 12345,
	}
	//监听不绑定IP
	conn, _ := net.ListenUDP("udp6", srcAddr)
	fmt.Printf("udp server bind on:[%s]\n", conn.LocalAddr())
	for {
		buffer := make([]byte, 1024)
		//将数据读到buffer中
		n, remoteAddr, _ := conn.ReadFromUDP(buffer)
		fmt.Printf("remote addr:[%s] \n data:[%v] str:[%s]\n", remoteAddr.String(), buffer[:n], string(buffer[:n]))
		//发送回包
		_, _ = conn.WriteToUDP([]byte("hello client"), remoteAddr)
	}
}

udpClient

再写个httpServer,在"udpTest"接口中实现个udpClient,用来发udp数据包

func httpServerStart() {
	udpTestHandler := func(writer http.ResponseWriter, request *http.Request) {
		ipStr := request.FormValue("ip")
		fmt.Printf("req param,ip:[%s]\n", net.ParseIP(ipStr))
		socket, err := net.DialUDP("udp6", nil,
			&net.UDPAddr{
				IP:   net.ParseIP(ipStr),
				Port: 12345,
			},
		)
		if err != nil {
			fmt.Println("连接UDP服务器失败,err: ", err)
			return
		}
		defer socket.Close()
		sendData := []byte("Hello Server")
		_, err = socket.Write(sendData) // 发送数据
		if err != nil {
			fmt.Println("发送数据失败,err: ", err)
			return
		}
		data := make([]byte, 4096)
		n, remoteAddr, err := socket.ReadFromUDP(data) // 接收数据
		if err != nil {
			fmt.Println("接收数据失败, err: ", err)
			return
		}
		fmt.Printf("recv:%v addr:%v count:%v\n", string(data[:n]), remoteAddr, n)
	}

	handler := http.NewServeMux()
	handler.Handle("/udpTest", http.TimeoutHandler(http.HandlerFunc(udpTestHandler), time.Second*3, "timeout"))

	server := &http.Server{Addr: ":8096", Handler: handler}
	err := server.ListenAndServe()
	if nil != err {
		log.Fatal(err) //显示错误日志
	}
}

发包测试

在主机1上向主机2的两个端口发包

http://192.168.1.2:8096/udpTest?ip=2005:470:8192:beef:68af:d2e6:a0e3:6fb1
http://192.168.1.3:8096/udpTest?ip=2005:470:8192:beef:9020:c015:a801:1301

主机1没有日志打印,且http接口都显示超时了。

观察主机2的日志:

udp server bind on:[[::]:12345]

remote addr:[[2003:470:8192:beef:ca43:6a6a:9c4c:b1a0]:48406] 
 data:[[72 101 108 108 111 32 83 101 114 118 101 114]] str:[Hello Server]
reply addr:[[2003:470:8192:beef:ca43:6a6a:9c4c:b1a0]:48406] length:12 

remote addr:[[2003:470:8192:beef:ca43:6a6a:9c4c:b1a0]:35365] 
 data:[[72 101 108 108 111 32 83 101 114 118 101 114]] str:[Hello Server]
reply addr:[[2003:470:8192:beef:ca43:6a6a:9c4c:b1a0]:35365] length:12 

主机2收到了udp包,也发送了回包。
但主机1没有并没有收到,包走丢了?

主机2上用tcpdump抓个包看一下
host2pcap

看到主机2只收到了主机1的udp包,但没有回包发出去,那么回包发到哪去了?

问题定位

因为之前有试过主机1和主机2的tcp和http请求都是通的,但UDP却不通确实感到疑惑。
我们知道http是基于TCP的,UDP和TCP相比也是更加简单的,也无需握手。

查阅了一些资料后发现扯淡的文档居多,直到遇到了这篇好文《Linux路由应用-使用策略路由实现访问控制》,重点片段为:

UDP无连接,不可靠,只负责将数据尽力而为传到目的主机,它对源和目的IP地址的管理很松散,UDP数据流(更确切的并不能称为数据流)是单包的。在两端都没有显式bind到具体的IP地址的情况下,最终的数据包可以使用任意的本机地址,关键看路由的结果。数据到达对端之后,如果对端也没有显示bind到具体的IP地址,那么回复包的源地址也可能不再是初始包的目的地址。

解决办法

其解决办法在《Linux路由应用-使用策略路由实现访问控制》文章中也描述了可以使用配置系统策略路由的方式来解决,这里再总结一下。

方法一:配置策略路由

这部分参考《Linux路由应用-使用策略路由实现访问控制》文章中添加策略路由表部分即可。

方法二:配置系统路由

如果通信时很明确知道数据包是发往某个口的,则可以添加一条系统默认路由来解决。如:

ip -6 route add ::/0 dev ppp0 metric 60

这样当主机二再进行回包时,就会指定使用ppp0口进行回包了。

也可以通过udp进行socket通信时获取到包中的目的地址,再从UDP包中获取出源地址灵活添加系统路由配置。
常见的方式为,可以通过开启IPPROTO_IP进行获取。
如为go语言实现,可通过如下代码开启:

    syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1)

再调用ReadMsgUDP就可以获取到的具体的目的IP了。
详细代码可参考:
udp_ip_pktinfo.go

方法三:udp server绑定具体的IP

当然还有最后一种简单有用的方法,udp server进行监听时对每个网口的ip地址都进行一次绑定,简单方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705968.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:如何将Python导入PyCharm

如何将Python导入PyCharm 介绍 PyCharm是一个非常流行的Python开发工具,它拥有许多强大的功能和插件,使开发人员能够更高效地编写Python代码。在本篇文章中,我们将介绍如何将Python导入PyCharm。 步骤 1. 安装PyCharm 首先,您…

jupyter-notebook:从记录点回复数据

使用jupyter进行记录数据分析思路时,有时候会莫名出现一些问题。比如这次遇到的保存并关闭之后,隔了一个晚上再次打开文件就成了空文件了,昨天写的分析都没有了,很头疼。解决方法:如果确定是保存了后,每一个…

32 linux 中物理页的 cow

前言 熟悉 linux 进程机制的人都知道 linux 中新建进程是以 fork exec 的形式创建的进程 fork 的时候复制了父进程的相关数据结构, 然后更新了待执行的 binary, 去执行 然后 父子进程之间 内存管理是 基于 copy on write 的 对于某块物理页, fork 之后内存设置为 只读…

JAVA3

文章目录 注释核心机制JVM的功能 优缺点优点缺点 注释 例子: 核心机制 JVM的功能 优缺点 优点 缺点

FreeRTOS入门(二)

目录 什么是RTOS? 嵌入式有哪些常见的RTOS? ✓ VxWorks(开源收费) ✓ UCOSII&III(开源免费) ✓ FreeRTOS(开源免费) ✓ RT_Thread(开源免费) ✓ AliOS(开源收费) ✓ LiteOS FreeR…

微信小程序canvas层级太高,与其他非原生组件层级冲突

官网已经提出新版本以支持同层渲染,但是实际项目中层级还是冲突的。 最后在文档中找到这样一段话,用真机打开,层级就正常了 。所以建议大家,多使用真机调试去测试!!!!

redis中常用的命令

1.关于对key操作的命令 keys *: 查看redis中所有的key exists key: 判断指定的key是否存在。存在返回1 否则返回0 del key: 删除指定的key expire key seconds: 为指定的key设置过期时间 2.关于库的命令 默认redis中存在16个库 select n: 选中库 n0~15 flushdb: 清空…

C++中的exec()函数

exec()函数在C中是一个进程控制函数,用于创建新进程执行其他程序或命令行指令。exec()函数可以替换当前进程的代码和数据,创建新的进程运行其他程序。exec()函数有多个版本,例如execl、execv、execle、execve等,根据不同的参数类型…

SAP 区分工单BOM物料是手工删除 还是 Teco后自动关闭需求

SAP 区分工单BOM物料是手工删除 还是 Teco后自动关闭需求 首先 resb表删除标识XLOEK 都为 ‘X’,无法通过其它字段直接区分 1先从前台界面区分 手工删除的,组件界面颜色正常,状态为-REL 删除 Teco自动关闭需求的,颜色不一样&am…

python中调用java函数

python中调用java函数 1. 将java项目打包成jar(IDEA)2. 在python中调用jar 1. 将java项目打包成jar(IDEA) 【CtrlShiftAltS】或者“File --> Project Structure --> Project Settings” 选择Artifacts选项卡,点…

[Android Studio]1.2计数器

所有要改的代码如下: MainActivity代码: package com.example.code02;import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; imp…

FPGA第一个程序入门

1、使用正点原子的达芬奇开发板进行第一个FPGA程序设计。 2、启动vivado 2019.2。 3、 新建工程。 File--Project--New,选择RTL Project(寄存器传输级)。 4、选择器件。 add sources点击next, add constraints点击next&#x…

npm install安装依赖总结

node下载地址:https://nodejs.org/en/download/releases 。可以看到node版本、npm版本、node_module版本 1.npm的全局安装路径 查看默认值: npm get prefix 默认是C:\Users\你的用户名\AppData\Roaming\npm 、 可以通过 npm config prefix 更改全局…

mysql-数据迁移 及报错解决(ERROR 1290 (HY000)

文章目录 1. 物理迁移1. 迁移前,配置mysql的输出目录1. 查看mysql的输出目录2. 修改mysql的输出目录 2. 文件迁移 1. 物理迁移 1. 迁移前,配置mysql的输出目录 1. 查看mysql的输出目录 在安装MySQL的会限制了导入与导出的目录权限。只允许在规定的目录…

量子 能源,节能减排还是另有“端倪”?

光子盒研究院 前言:如今,量子技术早已走出实验室、广泛赋能电力、化学、医学等各个领域;创新赛道上,加速奔跑的量子产业,将带来无限可能。现在,光子盒特开启「量子」专栏,解读量子技术将为下游应…

基于matlab从3D医学图像中对脑肿瘤进行语义分割(附源码)

一、前言 此示例演示如何从 3D 医学图像中对脑肿瘤进行语义分割。 语义分割涉及用类标记 3-D 体积的图像或体素中的每个像素。此示例说明了如何使用 3-D U-Net 深度学习网络在磁共振成像 (MRI) 扫描中对脑肿瘤进行二进制语义分割。U-Net是一个快速&…

【MySQL学习笔记】(五) 表的约束

表的约束 1 什么是约束?2 空属性3 默认值4 列描述5 zerofill6 主键7 自增长8 唯一键9 外键 1 什么是约束? 约束是一种限制,它通过对表的行或列的数据做出限制,来确保数据的完整性、一致性。 真正约束字段的是数据类型&#xff0…

Electron快速入门

目录 前言 一、安装需知 二、安装electron 三、开始 3.1 修改package.json文件 3.2 创建main.js文件 3.3 启动预览窗口 3.4 显示内容 四、 热加载 五、主进程和渲染进程概念介绍 六、自定义原生菜单 6.1 自定义菜单 6.2 给菜单添加点击事件 6.3 抽离菜单定义 6.…

基于SpringBoot+vue的社区维修平台设计与实现

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

信号链噪声分析19

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示:这里可以添加技术概要 用于定量表示 ADC 动态性能的常用指标有六个,分别是:SINAD(信纳比)、ENOB (有效位数)、SNR(信…