美文网首页
go语言实现udp端口转发

go语言实现udp端口转发

作者: 今天i你好吗 | 来源:发表于2024-04-24 23:10 被阅读0次

    直接上代码, 功能同 socat UDP6-LISTEN:1781,fork UDP4:127.0.0.1:1701

    package main
    
    import (
        "errors"
        "fmt"
        "log"
        "net"
        "os"
        "strings"
        "time"
    )
    
    var (
        timeOut = time.Second * 60 * 5
    )
    
    func main() {
        println("入参监听地址和转发地址: " + strings.Join(os.Args[1:], " "))
        listenerAddress := ":1701"
        forwardAddress := "[fe80::542b:6dc7:f5de:261e]:1781"
        if len(os.Args) == 3 {
            listenerAddress = os.Args[1]
            forwardAddress = os.Args[2]
        }
        log.SetFlags(log.LstdFlags | log.Lshortfile)
    
        udpForwardBean := Start(listenerAddress, forwardAddress)
        if udpForwardBean != nil {
            for {
                println(udpForwardBean.String())
                println()
                time.Sleep(time.Second * 60)
            }
        }
    }
    
    func Start(listenerAddress string, forwardAddress string) *UdpForwardBean {
        message := "Udp监听地址: " + listenerAddress + "    转发地址: " + forwardAddress
        log.Println(message)
    
        listenerAddr, err := net.ResolveUDPAddr("udp", listenerAddress)
        if err != nil {
            log.Println(err)
            return nil
        }
        network := "udp"
        if listenerAddr.IP.To4() != nil {
            network = "udp4"
        } else if listenerAddr.IP.To16() != nil {
            network = "udp6"
        }
        listenerConn, err := net.ListenUDP(network, listenerAddr)
        if err != nil {
            log.Println(err)
            return nil
        }
        forwardMap := map[string]*net.UDPConn{}
        udpForwardBean := &UdpForwardBean{isClosed: false, listenerAddress: listenerAddress,
            forwardAddress: forwardAddress, listenerConn: listenerConn, forwardMap: forwardMap}
        buffer := make([]byte, 1024*64)
        go func() {
            defer listenerConn.Close()
            for {
                len, clientAddr, err := listenerConn.ReadFromUDP(buffer)
                if err != nil {
                    if errors.Is(err, net.ErrClosed) {
                        break
                    }
                    log.Println(err)
                    time.Sleep(time.Second)
                    continue
                }
                data := make([]byte, len)
                copy(data, buffer)
                handleClientRequest(clientAddr, data, listenerConn, forwardAddress, forwardMap)
            }
            udpForwardBean.isClosed = true
        }()
        return udpForwardBean
    }
    
    func handleClientRequest(clientAddr *net.UDPAddr, clientData []byte, listenerConn *net.UDPConn,
        forwardAddress string, forwardMap map[string]*net.UDPConn) {
        if clientAddr == nil {
            return
        }
        forwardConn := forwardMap[clientAddr.String()]
        if forwardConn == nil {
            log.Println("添加udp转发", clientAddr, forwardAddress)
            forwardAddr, err := net.ResolveUDPAddr("udp", forwardAddress)
            if err != nil {
                log.Println(err)
                return
            }
            forwardConn, err = net.DialUDP("udp", nil, forwardAddr)
            if err != nil {
                log.Println(err)
                return
            }
            forwardMap[clientAddr.String()] = forwardConn
            buffer := make([]byte, 1024*64)
            go func() {
                defer forwardConn.Close()
                for {
                    forwardConn.SetReadDeadline(time.Now().Add(timeOut))
                    len, serverAddr, err := forwardConn.ReadFromUDP(buffer)
                    if err != nil {
                        if errors.Is(err, net.ErrClosed) {
                            break
                        }
                        if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
                            break
                        }
                        log.Println(err)
                        time.Sleep(time.Second)
                        continue
                    }
                    if serverAddr.Port != forwardAddr.Port || serverAddr.IP.String() != forwardAddr.IP.String() {
                        log.Println("异常消息:", serverAddr.String(), forwardAddr.String())
                        continue
                    }
                    // log.Println("服务端消息:", serverAddr.String(), len, clientAddr.String())
                    data := make([]byte, len)
                    copy(data, buffer)
                    listenerConn.WriteToUDP(data, clientAddr)
                }
                log.Println("移除", clientAddr, forwardAddress)
                delete(forwardMap, clientAddr.String())
            }()
        }
        // log.Println("客户端消息:", clientAddr.String(), len(clientData))
        forwardConn.Write(clientData)
        forwardConn.SetReadDeadline(time.Now().Add(timeOut))
    }
    
    type UdpForwardBean struct {
        isClosed        bool
        listenerAddress string
        forwardAddress  string
        listenerConn    *net.UDPConn
        forwardMap      map[string]*net.UDPConn
    }
    
    func (bean *UdpForwardBean) String() string {
        var keys []string
        for key := range bean.forwardMap {
            keys = append(keys, key)
        }
        return fmt.Sprintf("udp转发中: %s %s %t %s", bean.listenerAddress, bean.forwardAddress, bean.isClosed, keys)
    }
    
    func (bean *UdpForwardBean) Close() {
        bean.listenerConn.Close()
        for _, v := range bean.forwardMap {
            v.Close()
        }
    }
    
    

    相关文章

      网友评论

          本文标题:go语言实现udp端口转发

          本文链接:https://www.haomeiwen.com/subject/baggxjtx.html