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
你使用的是哪个第三方FTP包?
看起来我替换了使用 bufio.NewReader 来读取消息的方式(不确定这是否是最优解决方案),现在它工作正常了。
result, err = ioutil.ReadAll(conn)
这个方法的调用结果是什么?result 包含了什么?
为什么不看看现有的FTP库呢?参见 https://github.com/avelino/awesome-go/blob/master/README.md#networking
根据文档,它应该返回一个 []byte,但程序正是在这里卡住了。之前我尝试过一个使用 HEAD HTTP 消息的程序,它是可以正常工作的。
我没有使用第三方包。我知道GitHub上有一些FTP包,但我编写这个程序是为了测试未来的应用程序,届时我将需要定制消息处理,使用外部库就失去了意义。
我猜测 readall 期望要么读取完所有字节,要么遇到例如 \0 或 \n 这样的结束符。尝试只读取几个字节,看看是否真的能收到任何响应。
根据文档,它会一直读取直到遇到EOF:
ReadAll 从 r 中读取数据,直到遇到错误或 EOF,并返回它读取的数据。
因此,“字节耗尽”可能是最准确的表述。
这是我的代码。我可以向服务器发送命令和参数,但 ReadAll 没有捕获到回复。我希望能够打印出来自服务器的回复。
func main() {
fmt.Println("hello world")
}
在Go中监听FTP响应,你需要使用bufio.Scanner或bufio.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结束,多行响应以三位状态码加空格开始,最后一行以相同的状态码加空格结束。

