PHP ftp_connect() 函数

ftp_connect() 函数建立一个到指定FTP服务器的连接。

注意:该函数需要PHP FTP扩展支持。确保在php.ini中启用了ftp扩展(extension=ftp)。

语法

ftp_connect(string $hostname, int $port = 21, int $timeout = 90): resource|false

参数说明

参数 描述
$hostname 必需。要连接的FTP服务器主机名或IP地址。
可以是:
  • 域名:ftp.example.com
  • IP地址:192.168.1.100
  • IPv6地址:[2001:db8::1]
  • 本地主机:localhost127.0.0.1
$port 可选。FTP服务器端口号,默认是21。
注意:某些FTP服务器可能使用其他端口,如990(FTPS)或22(SFTP,但需使用SSH2扩展)。
$timeout 可选。连接超时时间(秒),默认是90秒。

返回值

  • 成功时返回FTP连接资源(resource)
  • 失败时返回 false

示例

示例1:基本使用

建立到FTP服务器的基本连接:

<?php
// FTP服务器信息
$ftp_server = "ftp.example.com";
$ftp_port = 21;
$timeout = 30;

// 建立FTP连接
$conn_id = ftp_connect($ftp_server, $ftp_port, $timeout);

if ($conn_id) {
    echo "成功连接到FTP服务器: {$ftp_server}:{$ftp_port}\n";

    // 后续操作...
    // 例如:ftp_login(), ftp_pasv(), ftp_nlist()等

    // 最后关闭连接
    ftp_close($conn_id);
} else {
    echo "无法连接到FTP服务器: {$ftp_server}:{$ftp_port}\n";
    echo "错误信息:请检查服务器地址、端口和网络连接\n";
}
?>

示例2:带登录和错误处理

<?php
function connectToFTP($server, $username, $password, $port = 21, $timeout = 30) {
    // 建立连接
    $conn = ftp_connect($server, $port, $timeout);

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

    // 尝试登录
    if (!ftp_login($conn, $username, $password)) {
        ftp_close($conn);
        throw new Exception("FTP登录失败: 用户名或密码错误");
    }

    // 开启被动模式(推荐,特别是通过防火墙时)
    if (!ftp_pasv($conn, true)) {
        // 被动模式失败不是致命错误,但记录一下
        error_log("警告: 无法开启FTP被动模式");
    }

    return $conn;
}

// 使用示例
try {
    $ftp_conn = connectToFTP(
        "ftp.example.com",
        "myusername",
        "mypassword",
        21,
        30
    );

    echo "FTP连接和登录成功\n";

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

    // 列出文件
    $files = ftp_nlist($ftp_conn, ".");
    if ($files !== false) {
        echo "文件列表:\n";
        foreach ($files as $file) {
            echo "- {$file}\n";
        }
    }

    // 关闭连接
    ftp_close($ftp_conn);

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

示例3:连接到不同端口

<?php
// 连接到非标准端口的FTP服务器
$servers = [
    ["host" => "ftp.example.com", "port" => 21, "desc" => "标准FTP"],
    ["host" => "ftps.example.com", "port" => 990, "desc" => "FTPS(隐式SSL)"],
    ["host" => "sftp.example.com", "port" => 22, "desc" => "SFTP(注意:需用SSH2扩展)"],
];

foreach ($servers as $server) {
    echo "尝试连接: {$server['desc']} ({$server['host']}:{$server['port']})...\n";

    $conn = @ftp_connect($server['host'], $server['port'], 10);

    if ($conn) {
        echo "✓ 连接成功\n";
        ftp_close($conn);
    } else {
        echo "✗ 连接失败\n";

        // 检查常见错误
        if ($server['port'] == 22) {
            echo "  提示:端口22通常用于SFTP,需要使用ssh2扩展而不是ftp扩展\n";
            echo "  考虑使用:ssh2_connect() 函数\n";
        }
    }

    echo str_repeat("-", 50) . "\n";
}
?>

示例4:连接池类

<?php
/**
 * FTP连接池管理器
 */
class FTPConnectionPool {
    private $connections = [];
    private $max_connections = 5;
    private $timeout = 30;

    public function __construct($max_connections = 5, $timeout = 30) {
        $this->max_connections = $max_connections;
        $this->timeout = $timeout;
    }

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

        // 检查是否有空闲连接
        if (isset($this->connections[$key]) && count($this->connections[$key]) > 0) {
            $conn = array_pop($this->connections[$key]);

            // 验证连接是否仍然有效
            if ($this->isConnectionAlive($conn)) {
                echo "从连接池获取现有连接\n";
                return $conn;
            }
        }

        // 创建新连接
        echo "创建新的FTP连接\n";
        $conn = ftp_connect($host, $port, $this->timeout);

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

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

        ftp_pasv($conn, true);

        return $conn;
    }

    /**
     * 归还连接到池中
     */
    public function returnConnection($host, $username, $port, $conn) {
        $key = $this->getConnectionKey($host, $username, $port);

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

        // 如果连接池已满,直接关闭连接
        if (count($this->connections[$key]) >= $this->max_connections) {
            ftp_close($conn);
            echo "连接池已满,关闭连接\n";
        } else {
            // 保持连接在池中
            $this->connections[$key][] = $conn;
            echo "连接已归还到连接池\n";
        }
    }

    /**
     * 关闭所有连接
     */
    public function closeAll() {
        foreach ($this->connections as $key => $pool) {
            foreach ($pool as $conn) {
                if (is_resource($conn)) {
                    ftp_close($conn);
                }
            }
            $this->connections[$key] = [];
        }
        echo "所有FTP连接已关闭\n";
    }

    /**
     * 检查连接是否有效
     */
    private function isConnectionAlive($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 __destruct() {
        $this->closeAll();
    }
}

// 使用示例
$pool = new FTPConnectionPool(3, 30);

try {
    // 获取连接
    $conn1 = $pool->getConnection("ftp1.example.com", "user1", "pass1");

    // 执行一些操作
    ftp_chdir($conn1, "/public_html");
    $files = ftp_nlist($conn1, ".");

    // 归还连接
    $pool->returnConnection("ftp1.example.com", "user1", 21, $conn1);

    // 再次获取连接(可能从池中获取)
    $conn2 = $pool->getConnection("ftp1.example.com", "user1", "pass1");

    // 更多操作...

    // 归还连接
    $pool->returnConnection("ftp1.example.com", "user1", 21, $conn2);

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

// 脚本结束时会自动关闭所有连接
?>

示例5:性能测试和超时设置

<?php
// 测试不同超时设置对连接的影响
function testConnectionWithTimeout($host, $port, $timeout) {
    $start_time = microtime(true);

    // 抑制警告信息
    $conn = @ftp_connect($host, $port, $timeout);

    $end_time = microtime(true);
    $duration = round(($end_time - $start_time) * 1000, 2); // 毫秒

    if ($conn) {
        echo "✓ 连接成功 ({$duration}ms)\n";
        ftp_close($conn);
        return true;
    } else {
        echo "✗ 连接失败 (耗时: {$duration}ms)\n";
        return false;
    }
}

echo "测试不同超时设置的连接效果:\n";
echo str_repeat("=", 50) . "\n";

$test_host = "ftp.example.com";
$test_port = 21;

// 测试不同的超时时间
$timeouts = [1, 3, 5, 10, 30, 60, 90];

foreach ($timeouts as $timeout) {
    echo "超时设置: {$timeout}秒 - ";
    testConnectionWithTimeout($test_host, $test_port, $timeout);
}

// 测试连接不存在的主机
echo "\n测试连接不存在的主机:\n";
echo str_repeat("=", 50) . "\n";

$nonexistent_host = "nonexistent-ftp-server.example.com";
echo "尝试连接: {$nonexistent_host}:21 (超时: 5秒) - ";
testConnectionWithTimeout($nonexistent_host, 21, 5);
?>

错误处理

常见错误及解决方案:

错误现象 可能原因 解决方案
连接超时 服务器无响应、网络问题、防火墙阻止 检查网络连接,增加超时时间,检查防火墙设置
连接被拒绝 服务器未运行、端口错误、IP被阻止 验证服务器状态,检查端口号,联系服务器管理员
无法解析主机名 DNS解析失败、主机名拼写错误 检查主机名拼写,尝试使用IP地址,检查DNS设置
SSL连接失败 尝试使用ftp_connect()连接SSL端口 使用ftp_ssl_connect()进行SSL连接
连接成功但登录失败 用户名/密码错误、权限不足 验证凭据,检查用户权限

最佳实践

推荐做法:
  • 总是检查ftp_connect()的返回值,处理连接失败的情况
  • 根据网络状况设置合适的超时时间,避免脚本长时间挂起
  • 使用ftp_ssl_connect()进行安全连接,保护数据传输
  • 连接使用完毕后立即调用ftp_close()释放资源
  • 对于频繁的FTP操作,考虑实现连接池以提高性能
  • 将FTP服务器信息存储在配置文件中,而不是硬编码在代码中

常见问题

ftp_connect() ftp_ssl_connect()
建立普通的FTP连接 建立SSL加密的FTP连接(FTPS)
使用端口21(默认) 通常使用端口990或21
数据传输不加密 数据传输加密,更安全
适合内部网络或不敏感数据 适合公共网络或敏感数据
// 普通FTP连接
$conn = ftp_connect("ftp.example.com", 21);

// SSL FTP连接
$ssl_conn = ftp_ssl_connect("ftps.example.com", 990);

<?php
// 方法1:使用extension_loaded()
if (extension_loaded('ftp')) {
    echo "FTP扩展已启用\n";
} else {
    echo "FTP扩展未启用\n";
    echo "需要在php.ini中启用: extension=ftp\n";
}

// 方法2:使用function_exists()
if (function_exists('ftp_connect')) {
    echo "ftp_connect() 函数可用\n";
}

// 方法3:查看phpinfo()
// phpinfo(); // 查看是否有ftp模块信息

// 方法4:命令行检查
// php -m | grep ftp
?>

  1. 检查登录状态:确保调用ftp_login()成功
  2. 开启被动模式:尝试ftp_pasv($conn, true)
  3. 检查权限:用户可能没有执行某些操作的权限
  4. 验证路径:确保文件/目录路径正确
  5. 查看错误日志:检查PHP和FTP服务器日志
  6. 测试简单命令:先测试ftp_pwd()等简单命令
  7. 网络诊断:检查防火墙和网络连接

相关函数

函数 描述
ftp_ssl_connect() 建立SSL加密的FTP连接
ftp_login() 登录FTP服务器
ftp_close() 关闭FTP连接
ftp_pasv() 开启或关闭被动模式
ssh2_connect() 建立SSH连接(用于SFTP)
安全警告:
  • 避免在代码中硬编码FTP凭据
  • 对于生产环境,使用SSL加密连接(FTPS)
  • 限制FTP用户的权限,遵循最小权限原则
  • 定期更新FTP服务器软件以修复安全漏洞
  • 考虑使用SFTP(SSH文件传输协议)代替FTP