You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang UDP文件传输遇端口占用错误及代码问题求助

问题分析与解决方案

一、端口占用错误的核心原因

你的端口占用错误完全来自接收端代码的逻辑缺陷:

  • take_file函数外层是无限for循环,每次循环都会调用ListenUDP绑定同一个端口0.0.0.0:12345,但defer connection.Close()要等到整个函数结束才会执行,第一次绑定的连接永远不会被释放,后续循环再次绑定同一端口时必然触发“地址已被占用”的错误。
  • UDP是无连接协议,监听端口只需要初始化一次,不需要反复创建监听连接。

二、全代码逻辑错误修复

1. 接收函数take_file修复

将UDP监听移到外层循环之外,只初始化一次连接:

func take_file() {
    udp_address, address_error := net.ResolveUDPAddr("udp", "0.0.0.0:"+port)
    if address_error != nil {
        fmt.Println(address_error.Error())
        os.Exit(1)
    }

    // 仅创建一次UDP监听,避免重复绑定端口
    connection, connection_error := net.ListenUDP("udp", udp_address)
    if connection_error != nil {
        fmt.Println(connection_error.Error())
        os.Exit(1)
    }
    defer connection.Close() // 函数退出时统一关闭连接

    for {
        datas := make([]byte, buffer)
        n, _, reading_error := connection.ReadFromUDP(datas)
        if reading_error != nil {
            fmt.Println(reading_error.Error())
            os.Exit(1)
        }

        if string(datas[:n]) == EOF {
            fmt.Println("文件接收完成")
            continue
        }

        fmt.Println(string(datas[:n]))
    }
}

2. 发送函数send_file修复

移除不必要的内层循环,同时避免defer导致的连接无法及时释放:

func send_file(file_path, receiver_ip string) {
    udp_address, address_error := net.ResolveUDPAddr("udp", receiver_ip+":"+port)
    if address_error != nil {
        fmt.Println(address_error.Error())
        os.Exit(1)
    }

    for {
        connection, connection_error := net.DialUDP("udp", nil, udp_address)
        if connection_error != nil {
            fmt.Println(connection_error.Error())
            time.Sleep(time.Second) // 出错后延迟重试,避免频繁打印日志
            continue
        }

        // 提取文件名
        file_name := file_name_extracter(file_path)
        
        _, writing_error := connection.Write([]byte(file_name))
        if writing_error != nil {
            fmt.Println(writing_error.Error())
            connection.Close()
            continue
        }

        // 分块发送文件,新增错误处理
        if err := read_file_and_send(file_path, connection); err != nil {
            fmt.Println(err.Error())
            connection.Close()
            continue
        }

        _, writing_error = connection.Write([]byte(EOF))
        if writing_error != nil {
            fmt.Println(writing_error.Error())
            connection.Close()
            continue
        }

        fmt.Println("文件发送完成")
        connection.Close() // 每次发送完成后手动关闭连接
        // 如果不需要无限重复发送,此处可添加break
        // break
    }
}

3. 分块读取函数read_file_and_send修复

修复变量冲突、分块逻辑错误,优化文件读取效率:

// 修改函数返回错误,方便上层统一处理
func read_file_and_send(file_path string, connection net.UDPConn) error {
    file, opening_error := os.Open(file_path)
    if opening_error != nil {
        return opening_error
    }
    defer file.Close()

    // 使用全局buffer定义固定大小的缓冲区,避免变量名冲突
    buf := make([]byte, buffer)
    for {
        n, reading_error := file.Read(buf)
        if reading_error != nil {
            if reading_error == io.EOF {
                break // 文件读取完成
            }
            return reading_error
        }

        // 仅发送实际读取到的字节,避免冗余空字节
        if _, err := connection.Write(buf[:n]); err != nil {
            return err
        }
    }
    return nil
}

三、额外提示

  • UDP是无连接、不可靠协议,当前代码没有处理丢包、乱序问题,实际传输大文件大概率会出现数据损坏,建议添加序列号、确认应答机制,或直接改用TCP协议实现可靠传输。
  • 如果修改代码后仍出现端口占用,可通过系统命令排查:Windows用netstat -ano | findstr :12345,Linux/macOS用lsof -i :12345,找到占用进程后关闭即可。

内容的提问来源于stack exchange,提问作者shiva13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 04:50:42