PHP ftp_login() 函数

ftp_login() 函数用于登录到FTP服务器。

注意:在调用此函数之前,必须先使用ftp_connect()ftp_ssl_connect()建立到FTP服务器的连接。

语法

ftp_login(resource $ftp_stream, string $username, string $password): bool

参数说明

参数 描述
$ftp_stream 必需。FTP连接的资源标识符,由ftp_connect()ftp_ssl_connect()函数返回。
$username 必需。登录用户名。
$password 必需。登录密码。

返回值

  • 登录成功时返回 true
  • 登录失败时返回 false

示例

示例1:基本使用

建立FTP连接并登录:

<?php
// FTP服务器信息
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";

// 建立FTP连接
$conn_id = ftp_connect($ftp_server);
if ($conn_id === false) {
    die("无法连接到FTP服务器: $ftp_server");
}

echo "已连接到FTP服务器\n";

// 登录FTP服务器
if (ftp_login($conn_id, $ftp_user, $ftp_pass)) {
    echo "登录成功\n";

    // 显示欢迎消息(某些FTP服务器支持)
    echo ftp_systype($conn_id) . "\n";

    // 获取当前目录
    $current_dir = ftp_pwd($conn_id);
    echo "当前目录: $current_dir\n";

    // 执行其他操作...
} else {
    echo "登录失败: 用户名或密码错误\n";
}

// 关闭连接
ftp_close($conn_id);
?>

示例2:安全的FTP连接器

<?php
/**
 * 安全的FTP连接器类
 */
class SecureFTPConnector {
    private $conn;
    private $config;
    private $is_connected = false;
    private $is_logged_in = false;

    public function __construct($config = []) {
        $defaults = [
            'host' => 'localhost',
            'port' => 21,
            'timeout' => 90,
            'username' => 'anonymous',
            'password' => 'guest@example.com',
            'use_ssl' => false,
            'passive_mode' => true,
            'max_retries' => 3,
            'retry_delay' => 2 // seconds
        ];

        $this->config = array_merge($defaults, $config);
    }

    /**
     * 连接到FTP服务器并登录
     */
    public function connect() {
        $retry_count = 0;

        while ($retry_count < $this->config['max_retries']) {
            try {
                echo "尝试连接到 {$this->config['host']}:{$this->config['port']} (尝试 " . ($retry_count + 1) . ")...\n";

                // 建立连接(SSL或普通)
                if ($this->config['use_ssl']) {
                    $this->conn = ftp_ssl_connect(
                        $this->config['host'],
                        $this->config['port'],
                        $this->config['timeout']
                    );
                } else {
                    $this->conn = ftp_connect(
                        $this->config['host'],
                        $this->config['port'],
                        $this->config['timeout']
                    );
                }

                if (!$this->conn) {
                    throw new Exception("无法连接到FTP服务器");
                }

                $this->is_connected = true;

                // 登录
                if (!ftp_login($this->conn, $this->config['username'], $this->config['password'])) {
                    throw new Exception("FTP登录失败: 用户名或密码错误");
                }

                $this->is_logged_in = true;

                // 设置被动模式
                if ($this->config['passive_mode']) {
                    ftp_pasv($this->conn, true);
                }

                echo "连接并登录成功\n";
                $this->logConnection();
                return true;

            } catch (Exception $e) {
                echo "连接失败: " . $e->getMessage() . "\n";
                $retry_count++;

                if ($retry_count < $this->config['max_retries']) {
                    echo "等待 {$this->config['retry_delay']} 秒后重试...\n";
                    sleep($this->config['retry_delay']);
                }
            }
        }

        return false;
    }

    /**
     * 获取服务器信息
     */
    public function getServerInfo() {
        if (!$this->is_logged_in) {
            throw new Exception("未登录到FTP服务器");
        }

        $info = [
            'system_type' => @ftp_systype($this->conn),
            'current_directory' => @ftp_pwd($this->conn),
            'features' => [],
            'connection_status' => $this->isConnected() ? '活动' : '断开'
        ];

        // 尝试获取服务器特性
        if (function_exists('ftp_raw')) {
            $response = @ftp_raw($this->conn, "FEAT");
            if ($response) {
                foreach ($response as $line) {
                    if (preg_match('/^\s+([A-Z-]+)/', $line, $matches)) {
                        $info['features'][] = $matches[1];
                    }
                }
            }
        }

        return $info;
    }

    /**
     * 测试连接
     */
    public function testConnection() {
        if (!$this->is_logged_in) {
            return false;
        }

        try {
            // 执行一个简单的命令来测试连接
            $result = @ftp_pwd($this->conn);
            return $result !== false;
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * 检查是否已连接
     */
    public function isConnected() {
        return $this->is_connected && is_resource($this->conn);
    }

    /**
     * 检查是否已登录
     */
    public function isLoggedIn() {
        return $this->is_logged_in && $this->isConnected();
    }

    /**
     * 获取FTP连接资源
     */
    public function getConnection() {
        if (!$this->is_logged_in) {
            throw new Exception("未登录到FTP服务器");
        }
        return $this->conn;
    }

    /**
     * 记录连接信息
     */
    private function logConnection() {
        $log_entry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'host' => $this->config['host'],
            'port' => $this->config['port'],
            'username' => $this->config['username'],
            'use_ssl' => $this->config['use_ssl'],
            'success' => true
        ];

        // 这里可以保存到文件或数据库
        error_log("[FTP_LOGIN] " . json_encode($log_entry));
    }

    /**
     * 关闭连接
     */
    public function close() {
        if ($this->is_connected && is_resource($this->conn)) {
            ftp_close($this->conn);
            $this->conn = null;
            $this->is_connected = false;
            $this->is_logged_in = false;
            echo "FTP连接已关闭\n";
        }
    }

    public function __destruct() {
        $this->close();
    }
}

// 使用示例
try {
    // 创建安全连接器
    $ftp = new SecureFTPConnector([
        'host' => 'ftp.example.com',
        'port' => 21,
        'username' => 'myusername',
        'password' => 'mypassword',
        'use_ssl' => false,
        'passive_mode' => true,
        'max_retries' => 3
    ]);

    // 连接并登录
    if ($ftp->connect()) {
        // 获取服务器信息
        $info = $ftp->getServerInfo();
        echo "\n服务器信息:\n";
        echo "系统类型: " . ($info['system_type'] ?: '未知') . "\n";
        echo "当前目录: " . $info['current_directory'] . "\n";
        echo "连接状态: " . $info['connection_status'] . "\n";

        if (!empty($info['features'])) {
            echo "支持的特性: " . implode(', ', $info['features']) . "\n";
        }

        // 测试连接
        echo "\n连接测试: " . ($ftp->testConnection() ? "成功" : "失败") . "\n";

        // 可以继续执行其他操作...
    }

    // 会自动关闭连接
} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

示例3:匿名登录和认证登录处理

<?php
/**
 * FTP登录管理器(支持匿名和认证登录)
 */
class FTPLoginManager {
    private $conn;
    private $login_type = 'anonymous'; // anonymous 或 authenticated

    /**
     * 尝试多种方式登录
     */
    public function connectWithFallback($host, $options = []) {
        $defaults = [
            'port' => 21,
            'timeout' => 30,
            'username' => null,
            'password' => null,
            'try_anonymous' => true,
            'anonymous_email' => 'guest@example.com'
        ];

        $options = array_merge($defaults, $options);

        // 建立连接
        $this->conn = ftp_connect($host, $options['port'], $options['timeout']);
        if (!$this->conn) {
            throw new Exception("无法连接到FTP服务器: $host");
        }

        echo "已连接到FTP服务器: $host\n";

        // 尝试认证登录
        if ($options['username'] && $options['password']) {
            if (ftp_login($this->conn, $options['username'], $options['password'])) {
                $this->login_type = 'authenticated';
                echo "认证登录成功: {$options['username']}\n";
                return true;
            } else {
                echo "认证登录失败\n";
            }
        }

        // 尝试匿名登录
        if ($options['try_anonymous']) {
            if (ftp_login($this->conn, 'anonymous', $options['anonymous_email'])) {
                $this->login_type = 'anonymous';
                echo "匿名登录成功\n";

                // 匿名登录通常有限制,检查权限
                $this->checkAnonymousPermissions();
                return true;
            } else {
                echo "匿名登录失败\n";
            }
        }

        ftp_close($this->conn);
        throw new Exception("所有登录方式都失败");
    }

    /**
     * 检查匿名登录权限
     */
    private function checkAnonymousPermissions() {
        echo "检查匿名登录权限...\n";

        try {
            // 尝试获取当前目录
            $pwd = ftp_pwd($this->conn);
            echo "当前目录: $pwd\n";

            // 尝试列出文件
            $files = ftp_nlist($this->conn, ".");
            if ($files !== false) {
                echo "可列出文件数量: " . count($files) . "\n";
            } else {
                echo "无法列出文件(权限不足)\n";
            }

            // 尝试创建目录(通常会失败)
            $test_dir = "test_dir_" . time();
            if (@ftp_mkdir($this->conn, $test_dir)) {
                echo "可以创建目录(不寻常的匿名权限)\n";
                ftp_rmdir($this->conn, $test_dir); // 清理
            } else {
                echo "无法创建目录(正常匿名权限)\n";
            }

        } catch (Exception $e) {
            echo "权限检查错误: " . $e->getMessage() . "\n";
        }
    }

    /**
     * 获取登录类型
     */
    public function getLoginType() {
        return $this->login_type;
    }

    /**
     * 获取连接资源
     */
    public function getConnection() {
        if (!$this->conn) {
            throw new Exception("未连接到FTP服务器");
        }
        return $this->conn;
    }

    /**
     * 关闭连接
     */
    public function close() {
        if (is_resource($this->conn)) {
            ftp_close($this->conn);
            $this->conn = null;
            echo "FTP连接已关闭\n";
        }
    }
}

// 使用示例
try {
    $loginManager = new FTPLoginManager();

    echo "=== 场景1: 认证登录 ===\n";
    try {
        $loginManager->connectWithFallback('ftp.example.com', [
            'username' => 'myusername',
            'password' => 'mypassword',
            'try_anonymous' => false
        ]);
        echo "登录类型: " . $loginManager->getLoginType() . "\n";
        $loginManager->close();
    } catch (Exception $e) {
        echo "场景1失败: " . $e->getMessage() . "\n";
    }

    echo "\n=== 场景2: 匿名登录 ===\n";
    try {
        $loginManager->connectWithFallback('ftp.gnu.org', [
            'username' => null,
            'password' => null,
            'try_anonymous' => true,
            'anonymous_email' => 'me@example.com'
        ]);
        echo "登录类型: " . $loginManager->getLoginType() . "\n";

        // 使用连接执行操作
        $conn = $loginManager->getConnection();
        $files = ftp_nlist($conn, ".");
        echo "根目录文件数: " . count($files) . "\n";

        $loginManager->close();
    } catch (Exception $e) {
        echo "场景2失败: " . $e->getMessage() . "\n";
    }

    echo "\n=== 场景3: 自动回退 ===\n";
    try {
        $loginManager->connectWithFallback('ftp.example.com', [
            'username' => 'wronguser',  // 错误的用户名
            'password' => 'wrongpass',  // 错误的密码
            'try_anonymous' => true     // 认证失败后尝试匿名
        ]);
        echo "登录类型: " . $loginManager->getLoginType() . "\n";
        $loginManager->close();
    } catch (Exception $e) {
        echo "场景3失败: " . $e->getMessage() . "\n";
    }

} catch (Exception $e) {
    echo "总体错误: " . $e->getMessage() . "\n";
}
?>

示例4:Web界面的FTP登录表单

<?php
// Web界面FTP登录
session_start();

// 处理登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $host = $_POST['host'] ?? '';
    $port = $_POST['port'] ?? 21;
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';
    $use_ssl = isset($_POST['use_ssl']) ? true : false;

    // 验证输入
    $errors = [];
    if (empty($host)) {
        $errors[] = '主机名不能为空';
    }
    if (!is_numeric($port) || $port < 1 || $port > 65535) {
        $errors[] = '端口号无效';
    }

    if (empty($errors)) {
        // 尝试连接
        $start_time = microtime(true);

        if ($use_ssl && function_exists('ftp_ssl_connect')) {
            $conn = ftp_ssl_connect($host, $port, 10);
        } else {
            $conn = ftp_connect($host, $port, 10);
        }

        if ($conn) {
            // 尝试登录
            if (ftp_login($conn, $username, $password)) {
                $end_time = microtime(true);
                $connection_time = round(($end_time - $start_time) * 1000, 2);

                // 保存连接信息到会话
                $_SESSION['ftp_connection'] = [
                    'host' => $host,
                    'port' => $port,
                    'username' => $username,
                    'use_ssl' => $use_ssl,
                    'conn_time' => $connection_time,
                    'logged_in_at' => date('Y-m-d H:i:s')
                ];

                // 保存连接资源标识符(注意:资源不能直接序列化)
                $_SESSION['ftp_conn_id'] = null;
                // 在实际应用中,可能需要将连接保持在其他存储中

                // 重定向到文件管理器
                header('Location: ftp_file_manager.php');
                exit;
            } else {
                $errors[] = '登录失败:用户名或密码错误';
            }
            ftp_close($conn);
        } else {
            $errors[] = '无法连接到FTP服务器';
        }
    }

    // 如果有错误,保存到会话
    if (!empty($errors)) {
        $_SESSION['ftp_errors'] = $errors;
        header('Location: ' . $_SERVER['PHP_SELF']);
        exit;
    }
}

// 显示错误信息
$errors = $_SESSION['ftp_errors'] ?? [];
unset($_SESSION['ftp_errors']);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FTP登录</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <style>
        .login-container {
            max-width: 500px;
            margin: 50px auto;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 0 20px rgba(0,0,0,0.1);
            background-color: white;
        }
        .login-header {
            text-align: center;
            margin-bottom: 30px;
        }
        .login-header i {
            font-size: 3em;
            color: #0d6efd;
            margin-bottom: 15px;
        }
        .error-message {
            animation: fadeIn 0.5s;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .form-label {
            font-weight: 500;
        }
        .remember-me {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
    </style>
</head>
<body style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh;">
    <div class="login-container">
        <div class="login-header">
            <i class="fas fa-server"></i>
            <h1>FTP登录</h1>
            <p class="text-muted">连接到远程FTP服务器</p>
        </div>

        <?php if (!empty($errors)): ?>
            <div class="alert alert-danger error-message">
                <h5><i class="fas fa-exclamation-triangle me-2"></i>登录失败</h5>
                <ul class="mb-0">
                    <?php foreach ($errors as $error): ?>
                        <li><?php echo htmlspecialchars($error); ?></li>
                    <?php endforeach; ?>
                </ul>
            </div>
        <?php endif; ?>

        <form method="post" action="">
            <div class="mb-3">
                <label for="host" class="form-label"><i class="fas fa-globe me-2"></i>FTP服务器</label>
                <input type="text" class="form-control" id="host" name="host"
                       placeholder="例如: ftp.example.com" required
                       value="<?php echo htmlspecialchars($_POST['host'] ?? ''); ?>">
                <div class="form-text">输入FTP服务器的主机名或IP地址</div>
            </div>

            <div class="row mb-3">
                <div class="col-md-6">
                    <label for="port" class="form-label"><i class="fas fa-plug me-2"></i>端口</label>
                    <input type="number" class="form-control" id="port" name="port"
                           min="1" max="65535" value="<?php echo htmlspecialchars($_POST['port'] ?? '21'); ?>" required>
                    <div class="form-text">默认端口: 21</div>
                </div>
                <div class="col-md-6">
                    <label for="username" class="form-label"><i class="fas fa-user me-2"></i>用户名</label>
                    <input type="text" class="form-control" id="username" name="username"
                           placeholder="输入用户名"
                           value="<?php echo htmlspecialchars($_POST['username'] ?? ''); ?>" required>
                    <div class="form-text">匿名登录使用: anonymous</div>
                </div>
            </div>

            <div class="mb-3">
                <label for="password" class="form-label"><i class="fas fa-key me-2"></i>密码</label>
                <input type="password" class="form-control" id="password" name="password"
                       placeholder="输入密码" required>
                <div class="form-text">匿名登录密码通常为你的邮箱地址</div>
            </div>

            <div class="mb-3 form-check">
                <input type="checkbox" class="form-check-input" id="use_ssl" name="use_ssl"
                       <?php echo isset($_POST['use_ssl']) ? 'checked' : ''; ?>>
                <label class="form-check-label" for="use_ssl">
                    <i class="fas fa-lock me-1"></i>使用SSL安全连接 (FTPS)
                </label>
                <div class="form-text">启用加密连接以保护数据传输</div>
            </div>

            <div class="mb-3 form-check">
                <input type="checkbox" class="form-check-input" id="remember" name="remember">
                <label class="form-check-label" for="remember">记住我</label>
            </div>

            <div class="d-grid gap-2">
                <button type="submit" class="btn btn-primary btn-lg">
                    <i class="fas fa-sign-in-alt me-2"></i>登录
                </button>
                <button type="reset" class="btn btn-outline-secondary">
                    <i class="fas fa-redo me-2"></i>重置
                </button>
            </div>
        </form>

        <div class="mt-4 text-center">
            <hr>
            <p class="text-muted small">
                <i class="fas fa-info-circle me-1"></i>
                常用FTP服务器示例:
                <br>
                <code>ftp.gnu.org</code> (匿名登录) |
                <code>ftp.debian.org</code> (匿名登录) |
                <code>ftp.mozilla.org</code> (匿名登录)
            </p>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 自动填充示例
            const exampleServers = [
                {host: 'ftp.gnu.org', port: 21, username: 'anonymous', ssl: false},
                {host: 'ftp.debian.org', port: 21, username: 'anonymous', ssl: false},
                {host: 'ftp.mozilla.org', port: 21, username: 'anonymous', ssl: false}
            ];

            // 添加示例选择下拉框
            const hostInput = document.getElementById('host');
            const usernameInput = document.getElementById('username');
            const portInput = document.getElementById('port');
            const sslCheckbox = document.getElementById('use_ssl');

            // 创建示例选择器
            const exampleSelector = document.createElement('select');
            exampleSelector.className = 'form-select mb-2';
            exampleSelector.innerHTML = `
                <option value="">选择示例服务器...</option>
                <option value="gnu">GNU FTP服务器 (匿名)</option>
                <option value="debian">Debian FTP服务器 (匿名)</option>
                <option value="mozilla">Mozilla FTP服务器 (匿名)</option>
                <option value="custom">自定义服务器</option>
            `;

            hostInput.parentNode.insertBefore(exampleSelector, hostInput);

            exampleSelector.addEventListener('change', function() {
                const value = this.value;
                if (value === 'gnu') {
                    fillExample(exampleServers[0]);
                } else if (value === 'debian') {
                    fillExample(exampleServers[1]);
                } else if (value === 'mozilla') {
                    fillExample(exampleServers[2]);
                } else if (value === 'custom') {
                    clearFields();
                }
            });

            function fillExample(server) {
                hostInput.value = server.host;
                portInput.value = server.port;
                usernameInput.value = server.username;
                sslCheckbox.checked = server.ssl;

                // 密码字段提示
                const passwordInput = document.getElementById('password');
                passwordInput.placeholder = '匿名登录请输入你的邮箱地址';
                passwordInput.focus();
            }

            function clearFields() {
                hostInput.value = '';
                usernameInput.value = '';
                portInput.value = '21';
                sslCheckbox.checked = false;
                document.getElementById('password').placeholder = '输入密码';
            }

            // 表单验证
            const form = document.querySelector('form');
            form.addEventListener('submit', function(e) {
                const host = hostInput.value.trim();
                const port = parseInt(portInput.value);

                if (!host) {
                    e.preventDefault();
                    alert('请输入FTP服务器地址');
                    hostInput.focus();
                    return false;
                }

                if (isNaN(port) || port < 1 || port > 65535) {
                    e.preventDefault();
                    alert('请输入有效的端口号 (1-65535)');
                    portInput.focus();
                    return false;
                }

                // 显示加载状态
                const submitBtn = form.querySelector('button[type="submit"]');
                const originalText = submitBtn.innerHTML;
                submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>正在连接...';
                submitBtn.disabled = true;

                return true;
            });
        });
    </script>
</body>
</html>

示例5:连接池和会话管理

<?php
/**
 * FTP连接池管理器
 */
class FTPConnectionPool {
    private static $instance = null;
    private $connections = [];
    private $max_pool_size = 5;
    private $connection_timeout = 300; // 5分钟

    private function __construct() {
        // 私有构造函数,单例模式
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * 获取FTP连接
     */
    public function getConnection($host, $username, $password, $port = 21, $use_ssl = false) {
        $connection_key = $this->getConnectionKey($host, $username, $port);

        // 检查连接池中是否有可用的连接
        if (isset($this->connections[$connection_key])) {
            foreach ($this->connections[$connection_key] as $index => $connection) {
                // 检查连接是否仍然有效
                if ($this->isConnectionValid($connection['conn'])) {
                    // 检查连接是否过期
                    if (time() - $connection['last_used'] < $this->connection_timeout) {
                        $conn = array_splice($this->connections[$connection_key], $index, 1)[0];
                        $conn['last_used'] = time();
                        echo "从连接池获取现有连接\n";
                        return $conn['conn'];
                    }
                }
            }
        }

        // 没有可用连接,创建新连接
        echo "创建新的FTP连接\n";
        return $this->createNewConnection($host, $username, $password, $port, $use_ssl);
    }

    /**
     * 归还连接到池中
     */
    public function returnConnection($host, $username, $port, $conn) {
        if (!is_resource($conn)) {
            echo "连接无效,不归还到池中\n";
            return;
        }

        $connection_key = $this->getConnectionKey($host, $username, $port);

        if (!isset($this->connections[$connection_key])) {
            $this->connections[$connection_key] = [];
        }

        // 检查连接池是否已满
        if (count($this->connections[$connection_key]) >= $this->max_pool_size) {
            echo "连接池已满,关闭连接\n";
            ftp_close($conn);
            return;
        }

        // 将连接放回池中
        $this->connections[$connection_key][] = [
            'conn' => $conn,
            'last_used' => time(),
            'created_at' => time()
        ];

        echo "连接已归还到连接池\n";
    }

    /**
     * 创建新连接
     */
    private function createNewConnection($host, $username, $password, $port, $use_ssl) {
        // 建立连接
        if ($use_ssl && function_exists('ftp_ssl_connect')) {
            $conn = ftp_ssl_connect($host, $port, 30);
        } else {
            $conn = ftp_connect($host, $port, 30);
        }

        if (!$conn) {
            throw new Exception("无法连接到FTP服务器: $host");
        }

        // 登录
        if (!ftp_login($conn, $username, $password)) {
            ftp_close($conn);
            throw new Exception("FTP登录失败");
        }

        // 开启被动模式
        ftp_pasv($conn, true);

        return $conn;
    }

    /**
     * 检查连接是否有效
     */
    private function isConnectionValid($conn) {
        if (!is_resource($conn)) {
            return false;
        }

        try {
            $result = @ftp_pwd($conn);
            return $result !== false;
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * 生成连接键
     */
    private function getConnectionKey($host, $username, $port) {
        return md5("$host:$port:$username");
    }

    /**
     * 清理过期连接
     */
    public function cleanupExpiredConnections() {
        $cleaned_count = 0;

        foreach ($this->connections as $key => $pool) {
            foreach ($pool as $index => $connection) {
                if (time() - $connection['last_used'] > $this->connection_timeout) {
                    if (is_resource($connection['conn'])) {
                        ftp_close($connection['conn']);
                    }
                    unset($this->connections[$key][$index]);
                    $cleaned_count++;
                }
            }

            // 重新索引数组
            $this->connections[$key] = array_values($this->connections[$key]);

            // 如果池为空,移除键
            if (empty($this->connections[$key])) {
                unset($this->connections[$key]);
            }
        }

        echo "清理了 {$cleaned_count} 个过期连接\n";
        return $cleaned_count;
    }

    /**
     * 获取连接池状态
     */
    public function getPoolStatus() {
        $status = [
            'total_pools' => count($this->connections),
            'total_connections' => 0,
            'pools' => []
        ];

        foreach ($this->connections as $key => $pool) {
            $status['total_connections'] += count($pool);
            $status['pools'][$key] = [
                'connections' => count($pool),
                'oldest' => time() - min(array_column($pool, 'created_at')),
                'newest' => time() - max(array_column($pool, 'created_at'))
            ];
        }

        return $status;
    }

    /**
     * 关闭所有连接
     */
    public function closeAllConnections() {
        $closed_count = 0;

        foreach ($this->connections as $key => $pool) {
            foreach ($pool as $connection) {
                if (is_resource($connection['conn'])) {
                    ftp_close($connection['conn']);
                    $closed_count++;
                }
            }
        }

        $this->connections = [];
        echo "关闭了 {$closed_count} 个连接\n";
        return $closed_count;
    }
}

// 使用示例
try {
    $pool = FTPConnectionPool::getInstance();

    // 模拟多个并发请求
    $operations = [
        ['op' => 'download', 'file' => 'file1.txt'],
        ['op' => 'upload', 'file' => 'file2.txt'],
        ['op' => 'list', 'dir' => '/'],
        ['op' => 'delete', 'file' => 'old.txt']
    ];

    foreach ($operations as $operation) {
        echo "\n执行操作: {$operation['op']} - {$operation['file']}\n";

        // 从连接池获取连接
        $conn = $pool->getConnection(
            'ftp.example.com',
            'username',
            'password'
        );

        // 模拟操作
        echo "正在执行 {$operation['op']} 操作...\n";
        sleep(1); // 模拟耗时操作

        // 归还连接到池中
        $pool->returnConnection('ftp.example.com', 'username', 21, $conn);
    }

    // 查看连接池状态
    echo "\n连接池状态:\n";
    $status = $pool->getPoolStatus();
    echo "连接池数量: {$status['total_pools']}\n";
    echo "总连接数: {$status['total_connections']}\n";

    // 清理过期连接
    echo "\n清理过期连接:\n";
    $cleaned = $pool->cleanupExpiredConnections();
    echo "清理了 {$cleaned} 个连接\n";

    // 关闭所有连接
    echo "\n关闭所有连接:\n";
    $closed = $pool->closeAllConnections();
    echo "关闭了 {$closed} 个连接\n";

} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

常见错误和解决方案

常见登录问题:

错误现象 可能原因 解决方案
登录失败 用户名或密码错误 检查凭据,尝试匿名登录(anonymous/email@example.com)
连接成功但登录失败 FTP服务未运行或配置错误 确认FTP服务正在运行,检查服务器配置
SSL连接失败 服务器不支持SSL或证书问题 尝试普通连接,检查SSL证书
登录后权限不足 用户权限限制 检查用户权限设置,联系服务器管理员
连接超时 网络问题或服务器无响应 检查网络连接,增加超时时间

安全最佳实践

安全建议:
  • 使用强密码,定期更换FTP密码
  • 尽可能使用SSL/TLS加密连接(FTPS)
  • 避免在代码中硬编码密码,使用配置文件或环境变量
  • 限制FTP用户的权限,遵循最小权限原则
  • 定期检查登录日志,监控异常登录行为
  • 考虑使用SSH密钥认证替代密码认证
  • 使用防火墙限制FTP访问来源IP

常见问题

登录类型 用户名 密码 权限 用途
匿名登录 anonymous 邮箱地址(如user@example.com) 通常只读,有限权限 公开文件下载,公共资源访问
认证登录 服务器分配的用户名 用户设置的密码 根据用户配置的权限 文件管理,网站维护,数据备份

<?php
function debugFTPLogin($host, $username, $password, $port = 21) {
    echo "开始FTP登录调试...\n";
    echo "主机: $host\n";
    echo "端口: $port\n";
    echo "用户名: $username\n";

    // 测试连接
    $conn = @ftp_connect($host, $port, 10);
    if (!$conn) {
        echo "❌ 连接失败\n";

        // 检查网络
        echo "网络测试:\n";
        $ping_result = @fsockopen($host, $port, $errno, $errstr, 10);
        if ($ping_result) {
            echo "  ✓ 端口 $port 可达\n";
            fclose($ping_result);
        } else {
            echo "  ✗ 端口 $port 不可达: $errstr\n";
        }
        return false;
    }

    echo "✓ 连接成功\n";

    // 测试登录
    if (!@ftp_login($conn, $username, $password)) {
        echo "❌ 登录失败\n";

        // 测试匿名登录
        echo "尝试匿名登录...\n";
        if (@ftp_login($conn, 'anonymous', 'guest@example.com')) {
            echo "  ✓ 匿名登录成功\n";
            echo "  问题: 认证凭据错误或用户不存在\n";
        } else {
            echo "  ✗ 匿名登录也失败\n";
            echo "  问题: 服务器可能禁用了该用户名或所有登录\n";
        }

        ftp_close($conn);
        return false;
    }

    echo "✓ 登录成功\n";

    // 获取服务器信息
    $system = @ftp_systype($conn);
    $pwd = @ftp_pwd($conn);
    echo "系统类型: " . ($system ?: '未知') . "\n";
    echo "当前目录: " . ($pwd ?: '未知') . "\n";

    ftp_close($conn);
    return true;
}

// 使用示例
debugFTPLogin('ftp.example.com', 'myuser', 'mypass');
?>

协议 端口 加密 认证方式 安全性
FTP 21 无(或通过FTPS) 用户名/密码 低(除非使用FTPS)
SFTP (SSH) 22 是(SSH加密) 用户名/密码或密钥

建议:对于敏感数据传输,推荐使用SFTP代替FTP。SFTP提供更好的安全性和更多的功能。

相关函数

函数 描述
ftp_connect() 建立FTP连接
ftp_ssl_connect() 建立SSL加密的FTP连接
ftp_close() 关闭FTP连接
ftp_pasv() 开启或关闭被动模式
ssh2_connect() 建立SSH连接(用于SFTP)
ssh2_auth_password() SSH密码认证