Golang监听FTP消息的实现方法

Golang监听FTP消息的实现方法 我正在尝试与服务器建立FTP连接。我可以启动连接并发送消息,但在监听和读取响应时遇到了问题。有没有办法在活动的TCPConn连接上被动监听,还是我需要其他东西?

import (

    "fmt"

    "net"

    "os"

)

func main() {

    // 如果参数数量不匹配则报错
    if len(os.Args) < 2 {

        fmt.Fprintf(os.Stderr, "Usage:%s host:port", os.Args[0])

        os.Exit(1)

    }

    // 服务字符串形式
    // (这里是IPv4地址和端口号 X.X.X.X:PN)
    service := os.Args[1]

    // 从服务字符串创建TCPAddr
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)

    checkError(err)

    // 创建TCP连接
    conn, err := net.DialTCP("tcp", nil, tcpAddr)

    checkError(err)

    // 向FTP服务器发送查询
    _, err = conn.Write([]byte("USER admin\r\n"))

    checkError(err)

    // 从FTP服务器读取
    result, err = ioutil.ReadAll(conn)

    checkError(err)

    // 关闭连接
    conn.Close()

    // 打印结果
    fmt.Println(string(result))

    os.Exit(0)

}

func checkError(err error) {

    if err != nil {

        fmt.Fprintf(os.Stderr, "致命错误: %s", err.Error())

        os.Exit(1)

    }

}

更多关于Golang监听FTP消息的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

11 回复

请展示你代码的相关部分。

更多关于Golang监听FTP消息的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你使用的是哪个第三方FTP包?

看起来我替换了使用 bufio.NewReader 来读取消息的方式(不确定这是否是最优解决方案),现在它工作正常了。

result, err = ioutil.ReadAll(conn)

这个方法的调用结果是什么?result 包含了什么?

根据文档,它应该返回一个 []byte,但程序正是在这里卡住了。之前我尝试过一个使用 HEAD HTTP 消息的程序,它是可以正常工作的。

我没有使用第三方包。我知道GitHub上有一些FTP包,但我编写这个程序是为了测试未来的应用程序,届时我将需要定制消息处理,使用外部库就失去了意义。

我猜测 readall 期望要么读取完所有字节,要么遇到例如 \0\n 这样的结束符。尝试只读取几个字节,看看是否真的能收到任何响应。

根据文档,它会一直读取直到遇到EOF:

ReadAll 从 r 中读取数据,直到遇到错误或 EOF,并返回它读取的数据。

因此,“字节耗尽”可能是最准确的表述。

这是我的代码。我可以向服务器发送命令和参数,但 ReadAll 没有捕获到回复。我希望能够打印出来自服务器的回复。

func main() {
    fmt.Println("hello world")
}

在Go中监听FTP响应,你需要使用bufio.Scannerbufio.Reader来持续读取连接。FTP协议使用多行响应,以下是一个改进的实现:

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strings"
)

func main() {
    if len(os.Args) < 2 {
        fmt.Fprintf(os.Stderr, "Usage: %s host:port\n", os.Args[0])
        os.Exit(1)
    }

    service := os.Args[1]
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)

    conn, err := net.DialTCP("tcp", nil, tcpAddr)
    checkError(err)
    defer conn.Close()

    // 创建带缓冲的读取器
    reader := bufio.NewReader(conn)
    
    // 先读取欢迎消息
    welcome, err := reader.ReadString('\n')
    checkError(err)
    fmt.Printf("Server: %s", welcome)

    // 发送USER命令
    _, err = conn.Write([]byte("USER admin\r\n"))
    checkError(err)
    
    // 读取USER响应
    response, err := reader.ReadString('\n')
    checkError(err)
    fmt.Printf("USER response: %s", response)

    // 发送PASS命令
    _, err = conn.Write([]byte("PASS password\r\n"))
    checkError(err)
    
    // 读取PASS响应
    response, err = reader.ReadString('\n')
    checkError(err)
    fmt.Printf("PASS response: %s", response)

    // 持续监听响应的示例
    go listenForResponses(reader)

    // 发送其他命令
    _, err = conn.Write([]byte("PWD\r\n"))
    checkError(err)
    
    // 主线程等待
    select {}
}

func listenForResponses(reader *bufio.Reader) {
    for {
        response, err := reader.ReadString('\n')
        if err != nil {
            fmt.Printf("读取错误: %v\n", err)
            return
        }
        fmt.Printf("响应: %s", response)
        
        // 检查是否是多行响应的结束
        if strings.HasPrefix(response, "150") || strings.HasPrefix(response, "125") {
            // 继续读取直到单独的行包含响应代码
            for {
                line, err := reader.ReadString('\n')
                if err != nil {
                    break
                }
                fmt.Printf("数据: %s", line)
                if len(line) >= 4 && line[3] == ' ' {
                    break
                }
            }
        }
    }
}

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "错误: %s\n", err.Error())
        os.Exit(1)
    }
}

对于被动模式的数据连接,需要单独处理:

func enterPassiveMode(controlConn net.Conn, reader *bufio.Reader) (net.Conn, error) {
    // 发送PASV命令
    _, err := controlConn.Write([]byte("PASV\r\n"))
    if err != nil {
        return nil, err
    }

    // 读取PASV响应
    response, err := reader.ReadString('\n')
    if err != nil {
        return nil, err
    }

    // 解析PASV响应中的IP和端口
    // 响应格式: 227 Entering Passive Mode (192,168,1,100,12,34)
    start := strings.Index(response, "(")
    end := strings.Index(response, ")")
    if start == -1 || end == -1 {
        return nil, fmt.Errorf("无效的PASV响应")
    }

    pasvData := response[start+1 : end]
    parts := strings.Split(pasvData, ",")
    if len(parts) != 6 {
        return nil, fmt.Errorf("无效的PASV数据")
    }

    ip := fmt.Sprintf("%s.%s.%s.%s", parts[0], parts[1], parts[2], parts[3])
    port1, _ := strconv.Atoi(parts[4])
    port2, _ := strconv.Atoi(parts[5])
    port := port1*256 + port2

    // 建立数据连接
    dataConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ip, port))
    if err != nil {
        return nil, err
    }

    return dataConn, nil
}

使用bufio.Reader可以持续读取FTP控制连接的响应。FTP响应以\r\n结束,多行响应以三位状态码加空格开始,最后一行以相同的状态码加空格结束。

回到顶部