Golang FTP服务器实现
我正在尝试用Golang实现一个简单的FTP服务器,但遇到了一些问题。目前已经实现了基本的连接和命令处理,但在处理文件传输时总是报错。具体表现为:当客户端尝试上传或下载文件时,服务器端会抛出"connection reset by peer"错误。我使用的是标准库net和os包,没有用第三方库。请问可能是什么原因导致的?有没有推荐的实现方案或者需要注意的关键点?另外,想请教下大家Golang实现FTP服务器时,如何处理并发连接和资源释放的问题?
2 回复
更多关于Golang FTP服务器实现的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中实现一个基础的FTP服务器,可以使用标准库net和自定义协议处理。以下是一个简化版本,支持基本的FTP命令(如USER、PASS、LIST、CWD、PWD、RETR、STOR等)。使用Go的并发特性处理多客户端连接。
核心代码示例
package main
import (
"fmt"
"net"
"strings"
"io"
"os"
"path/filepath"
)
type FTPConn struct {
conn net.Conn
dataConn net.Conn
rootDir string
workDir string
authenticated bool
}
func main() {
listener, err := net.Listen("tcp", ":21")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
defer listener.Close()
fmt.Println("FTP Server started on port 21")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Connection error:", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
ftp := &FTPConn{
conn: conn,
rootDir: "./ftp_root", // 设置服务器根目录
workDir: "/",
}
ftp.sendResponse(220, "FTP Server Ready")
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
break
}
command := strings.TrimSpace(string(buf[:n]))
ftp.handleCommand(command)
}
}
func (ftp *FTPConn) handleCommand(command string) {
parts := strings.Fields(command)
if len(parts) == 0 {
return
}
cmd := strings.ToUpper(parts[0])
args := ""
if len(parts) > 1 {
args = strings.Join(parts[1:], " ")
}
switch cmd {
case "USER":
ftp.authenticated = (args == "anonymous") // 简单匿名认证示例
if ftp.authenticated {
ftp.sendResponse(230, "User logged in, proceed.")
} else {
ftp.sendResponse(530, "Not logged in.")
}
case "PASS":
// 可扩展密码验证
case "SYST":
ftp.sendResponse(215, "UNIX Type: L8")
case "PWD":
ftp.sendResponse(257, "\""+ftp.workDir+"\" is current directory.")
case "CWD":
newDir := filepath.Join(ftp.rootDir, args)
if _, err := os.Stat(newDir); err == nil {
ftp.workDir = args
ftp.sendResponse(250, "Directory changed to "+args)
} else {
ftp.sendResponse(550, "Directory not found.")
}
case "LIST":
ftp.handleList()
case "RETR":
ftp.handleRetr(args)
case "STOR":
ftp.handleStor(args)
case "QUIT":
ftp.sendResponse(221, "Goodbye.")
ftp.conn.Close()
default:
ftp.sendResponse(502, "Command not implemented.")
}
}
func (ftp *FTPConn) sendResponse(code int, message string) {
fmt.Fprintf(ftp.conn, "%d %s\r\n", code, message)
}
// 处理LIST命令(简化版,仅列出当前目录文件)
func (ftp *FTPConn) handleList() {
dir := filepath.Join(ftp.rootDir, ftp.workDir)
files, err := os.ReadDir(dir)
if err != nil {
ftp.sendResponse(550, "Failed to list directory.")
return
}
// 建立数据连接(示例使用同一连接,实际需按PASV/PORT模式实现)
ftp.sendResponse(150, "Here comes the directory listing.")
var list string
for _, file := range files {
list += file.Name() + "\r\n"
}
ftp.conn.Write([]byte(list))
ftp.sendResponse(226, "Directory send OK.")
}
// 处理文件下载
func (ftp *FTPConn) handleRetr(filename string) {
path := filepath.Join(ftp.rootDir, ftp.workDir, filename)
file, err := os.Open(path)
if err != nil {
ftp.sendResponse(550, "File not found.")
return
}
defer file.Close()
ftp.sendResponse(150, "Opening data connection.")
// 实际传输需通过数据连接,此处简化
io.Copy(ftp.conn, file)
ftp.sendResponse(226, "Transfer complete.")
}
// 处理文件上传
func (ftp *FTPConn) handleStor(filename string) {
path := filepath.Join(ftp.rootDir, ftp.workDir, filename)
file, err := os.Create(path)
if err != nil {
ftp.sendResponse(550, "Could not create file.")
return
}
defer file.Close()
ftp.sendResponse(150, "Ready to receive file.")
// 实际接收数据需通过数据连接
io.Copy(file, ftp.conn)
ftp.sendResponse(226, "Transfer complete.")
}
关键说明:
- 基础结构:使用
FTPConn管理连接状态和目录。 - 命令处理:解析FTP命令并响应标准代码(如220、230等)。
- 文件操作:通过
LIST、RETR、STOR实现目录列表和文件传输。 - 认证:示例支持匿名登录,实际应用需扩展用户验证。
- 数据连接:简化版直接使用控制连接传输数据,标准FTP需实现PASV/PORT模式建立独立数据连接。
运行步骤:
- 创建根目录(如
./ftp_root)。 - 运行程序,监听端口21。
- 使用FTP客户端(如
ftp localhost 21)测试。
可根据需求扩展错误处理、日志、TLS支持及完整FTP命令。

