Prechádzať zdrojové kódy

加入有线网卡eth0支持, 有线网可用时优先使用, 无效时, 切换移动蜂窝网络; 通过有线网口的网线插拔触发一次网络的选择和切换

niujiuru 1 mesiac pred
rodič
commit
2014fd360d
2 zmenil súbory, kde vykonal 195 pridanie a 22 odobranie
  1. 21 12
      netmgrd/eth0net.go
  2. 174 10
      netmgrd/netmgrd.go

+ 21 - 12
netmgrd/eth0net.go

@@ -39,20 +39,29 @@ func disableEthBut0() error {
 	return nil
 }
 
+// 启动"eth0"网口, 使其变可用
+func enableEth0() error {
+	cmd := exec.Command("ip", "link", "set", "dev", "eth0", "up")
+	cmd.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
+	cmd.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("启动网口\"eth0\"失败: %w", err)
+	}
+
+	return nil
+}
+
 // 启动DHCP客户端, 请求IP地址
 func dialupEth0() error {
-	cmd1 := exec.Command("ip", "link", "set", "dev", "eth0", "up")
-	cmd1.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
-	cmd1.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
-	if err := cmd1.Run(); err != nil {
-		return fmt.Errorf("启动网口\"eth0\"失败: %w", err)
+	if err := enableEth0(); err != nil {
+		return err
 	}
 
 	eth0pid := baseapp.RUN_DIR + "/udhcpc.eth0.pid"
-	cmd2 := exec.Command("udhcpc", "-b", "-i", "eth0", "-p", eth0pid)
-	cmd2.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
-	cmd2.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
-	if err := cmd2.Run(); err != nil {
+	cmd := exec.Command("udhcpc", "-b", "-i", "eth0", "-p", eth0pid)
+	cmd.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
+	cmd.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
+	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("\"eth0\"请求地址失败: %w", err)
 	}
 
@@ -81,7 +90,7 @@ func udhcpcEth0Exists() (bool, error) {
 }
 
 // 杀死"eth0"网口上的"udhcpc"进程(根据上次运行时它的PID-文件记录)
-func KillEth0Udhcpc() error {
+func killEth0Udhcpc() error {
 	eth0pid := baseapp.RUN_DIR + "/udhcpc.eth0.pid"
 	data, err := os.ReadFile(eth0pid)
 	if err != nil {
@@ -151,7 +160,7 @@ func getEth0Addr() (ip, mask string, err error) {
 }
 
 // 定时检测"eth0"网卡上的"udhcpc"后台服务进程, 发现退出时自动拉起
-func MonitorEth0Udhcpc(exitCh <-chan struct{}) {
+func monitorEth0Udhcpc(exitCh <-chan struct{}) {
 	t := time.NewTicker(time.Duration(5) * time.Second)
 	defer t.Stop()
 
@@ -173,7 +182,7 @@ func MonitorEth0Udhcpc(exitCh <-chan struct{}) {
 	} // for end
 }
 
-func IsEth0CableConnected() (bool, error) {
+func isEth0CableConnected() (bool, error) {
 	if data, err := os.ReadFile("/sys/class/net/eth0/carrier"); err == nil {
 		return strings.TrimSpace(string(data)) == "1", nil
 	}

+ 174 - 10
netmgrd/netmgrd.go

@@ -3,9 +3,12 @@ package netmgrd
 import "C"
 
 import (
+	"sync"
 	"sync/atomic"
+	"syscall"
 	"time"
 
+	"github.com/vishvananda/netlink"
 	modem "hnyfkj.com.cn/rtu/linux/air720u"
 	"hnyfkj.com.cn/rtu/linux/baseapp"
 )
@@ -16,6 +19,19 @@ var (
 	isOnline        atomic.Bool  // 标记是否联网
 	offlineStartTs  atomic.Int64 // 离线开始时间
 	isSyncNTPTimeOK atomic.Bool  // 标记本地时间是否已同步成功
+	mu1             sync.Mutex
+	isRunning1      bool
+	exitCh1         chan struct{}
+	wg1             sync.WaitGroup
+	curNetType      NetType // 当前使用的网络类型
+)
+
+type NetType int
+
+const (
+	NetNone NetType = iota
+	NetEth          // 有线网络
+	NetLTE          // 蜂窝网络
 )
 
 const (
@@ -29,17 +45,26 @@ func ModuleInit() {
 
 // 联网保持服务
 func serviceRun() {
+	// 1, 首次连接网络
+	network_reconnect()
+
+	// 2, 监听有线插拔
+	eth0PlugCycleCh := make(chan bool, 1)
+	done := make(chan struct{})
+	defer close(done)
+	go monitorEth0PlugCycle(eth0PlugCycleCh, done)
+
+	// 3, 循环监控网络
 	t := time.NewTimer(interval1)
 	defer t.Stop()
-
 	for {
 		select {
 		case <-t.C:
-			// 1, 联网检测-看结果
+			// 3.1, 联网检测-看结果
 			dnsOK, pingOK, tcpOK, httpOK := CheckNetwork()
 			baseapp.Logger.Infof("[%s] 联网检测: DNS OK=%v, PING OK=%v, TCP OK=%v, HTTP OK=%v", MODULE_NAME, dnsOK, pingOK, tcpOK, httpOK)
 
-			// 2, 检测成功-在线时
+			// 3.2, 检测成功-在线时
 			if dnsOK && pingOK && tcpOK {
 				isOnline.Store(true)
 				offlineStartTs.Store(0)
@@ -62,19 +87,22 @@ func serviceRun() {
 				}
 			}
 
-			// 3, 检测失败-离线时
+			// 3.3, 检测失败-离线时
 			if isOnline.Load() { // 状态由"1"变为"0"
 				isOnline.Store(false)
 				offlineStartTs.Store(time.Now().UnixNano()) // 记录离线开始时间
 			}
 
 			if OfflineDuration() >= (time.Duration(60) * time.Second) {
-				baseapp.Logger.Warnf("[%s] 检测到网络长时间断开, 将重置4G模块!", MODULE_NAME)
-				reset4gNetwork()
+				baseapp.Logger.Warnf("[%s] 检测到网络长时间断开, 将重新尝试连接!", MODULE_NAME)
+				network_reconnect()
 				offlineStartTs.Store(time.Now().UnixNano()) // 重置离线开始时间
 			}
 
 			t.Reset(interval1)
+		case <-eth0PlugCycleCh: // 有线网口插拔事件, 重新连接网络, 有线网优先
+			network_reconnect()
+			continue
 		case <-baseapp.IsExit2():
 			return
 		} // select end
@@ -123,10 +151,146 @@ func OfflineDuration() time.Duration {
 	return time.Since(time.Unix(0, ts))
 }
 
-// 重启模块联网
-func reset4gNetwork() {
-	modem.ModuleExit()
-	modem.ModuleInit(true)
+// 连接有线网络
+func OpenEth0Net() bool {
+	mu1.Lock()
+	defer mu1.Unlock()
+
+	if isRunning1 {
+		return true
+	}
+
+	killAllUdhcpc()
+	err := disableEthBut0()
+	if err != nil {
+		baseapp.Logger.Errorf("[%s] 错误: %v!!", MODULE_NAME, err)
+		return false
+	}
+
+	bExists, _ := udhcpcEth0Exists()
+	if bExists {
+		killEth0Udhcpc()
+	}
+
+	err = dialupEth0()
+	if err != nil {
+		baseapp.Logger.Errorf("[%s] 拨号连接\"eth0\"时发生错误: %v!!", MODULE_NAME, err)
+		return false
+	}
+	ipv4, mask, err := getEth0Addr()
+	if err != nil {
+		baseapp.Logger.Errorf("[%s] 读取\"eth0\"地址时发生错误: %v!!", MODULE_NAME, err)
+		return false
+	}
+	baseapp.Logger.Infof("[%s] \"eth0\"分配的地址: %s/%s", MODULE_NAME, ipv4, mask)
+
+	exitCh1 = make(chan struct{})
+	wg1.Add(1)
+	go func() { // 启动携程守护"eth0"网卡上的 "udhcpc"后台服务进程
+		defer wg1.Done()
+		monitorEth0Udhcpc(exitCh1)
+	}()
+
+	isRunning1 = true
+	return isRunning1
+}
+
+// 断开有线网络
+func CloseEth0Net() {
+	mu1.Lock()
+	defer mu1.Unlock()
+
+	if !isRunning1 {
+		return
+	}
+
+	close(exitCh1)
+	wg1.Wait() // 等待守护"eth0"网卡上的 "udhcpc"后台服务进程的携程退出
+
+	bExists, _ := udhcpcEth0Exists()
+	if bExists {
+		killEth0Udhcpc()
+	}
+
+	isRunning1 = false
+}
+
+// 重新连接网络
+func network_reconnect() {
+	// 1, 断开所有网络
+	CloseEth0Net()
+	modem.CloseEth2Net()
+
+	// 2, 尝试有线网络
+	eth0CableOK, _ := isEth0CableConnected()
+	if eth0CableOK && curNetType != NetEth && OpenEth0Net() {
+		curNetType = NetEth
+		baseapp.Logger.Infof("[%s] ✅ 已连接有线网络", MODULE_NAME)
+		return
+	}
+
+	// 3, 尝试蜂窝网络
+	tryOpenLTE := func() bool {
+		if eth2CableOK, _ := modem.IsEth2CableConnected(); eth2CableOK {
+			if modem.OpenEth2Net() {
+				return true
+			}
+			// 重试: 4G模组重新上电初始化
+			modem.ModuleExit()
+			modem.ModuleInit(true)
+			return modem.OpenEth2Net()
+		}
+		return false
+	}
+
+	if tryOpenLTE() {
+		curNetType = NetLTE
+		enableEth0() // 保持 eth0 可用, 以便监听网线的插拔, 触发切换网络
+		baseapp.Logger.Infof("[%s] ✅ 已连接蜂窝网络", MODULE_NAME)
+		return
+	}
+
+	// 4, 都不可用
+	curNetType = NetNone
+}
+
+// 监控网线插拔
+func monitorEth0PlugCycle(plugCycle chan bool, done chan struct{}) error {
+	updates := make(chan netlink.LinkUpdate)
+	if err := netlink.LinkSubscribe(updates, done); err != nil {
+		return err
+	}
+
+	link, err := netlink.LinkByName("eth0")
+	if err != nil {
+		return err
+	}
+
+	isUp := link.Attrs().Flags&syscall.IFF_RUNNING != 0
+	waitingForPlug := false
+
+	for u := range updates {
+		if u.Link.Attrs().Name != "eth0" {
+			continue
+		}
+
+		newUp := u.IfInfomsg.Flags&syscall.IFF_RUNNING != 0
+		if newUp == isUp {
+			continue
+		}
+		isUp = newUp
+
+		if !isUp {
+			baseapp.Logger.Warnf("[%s] ❌ eth0 cable unplugged!", MODULE_NAME)
+			waitingForPlug = true
+		} else if waitingForPlug {
+			baseapp.Logger.Infof("[%s] ✅ eth0 cable plugged in", MODULE_NAME)
+			plugCycle <- true
+			waitingForPlug = false
+		}
+	}
+
+	return nil
 }
 
 //export RTU_IsInetAvailable