当前位置: 首页> 最新文章列表> 利用 socket_accept() 构建简易文件传输服务(FTP-like)

利用 socket_accept() 构建简易文件传输服务(FTP-like)

M66 2025-05-23

在 PHP 中,socket_accept() 是实现服务器端 socket 通信的关键函数。通过它,我们可以接受客户端发起的连接请求,并进一步进行数据收发。在本文中,我们将基于原生 PHP 的 socket 扩展构建一个简易的类 FTP 文件传输服务,演示如何实现基础的文件上传与下载功能。

一、准备工作

首先,确保 PHP 已经启用了 socket 扩展。在 php.ini 中检查是否有以下配置:

extension=sockets

你还需要有一个可以读写文件的目录,以及 PHP 脚本的运行权限。

二、服务端代码

服务端的职责包括监听端口、接受连接、解析命令并完成文件读写操作。以下是一个基础实现:

<?php

$host = '0.0.0.0';
$port = 2121; // 类似FTP的自定义端口
$saveDir = __DIR__ . '/uploads';

if (!is_dir($saveDir)) {
    mkdir($saveDir, 0777, true);
}

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, $host, $port);
socket_listen($socket);

echo "FTP-like server started on $host:$port\n";

while (true) {
    $client = socket_accept($socket);
    if ($client === false) continue;

    socket_write($client, "220 m66.net FTP Service Ready\r\n");

    $command = trim(socket_read($client, 1024));

    if (preg_match('/^UPLOAD (\S+)$/', $command, $matches)) {
        $filename = basename($matches[1]);
        $filepath = $saveDir . '/' . $filename;

        socket_write($client, "150 Opening binary mode data connection.\r\n");

        $file = fopen($filepath, 'w');
        while ($data = socket_read($client, 2048)) {
            fwrite($file, $data);
            if (feof($file)) break;
        }
        fclose($file);
        socket_write($client, "226 Transfer complete.\r\n");
    }

    elseif (preg_match('/^DOWNLOAD (\S+)$/', $command, $matches)) {
        $filename = basename($matches[1]);
        $filepath = $saveDir . '/' . $filename;

        if (!file_exists($filepath)) {
            socket_write($client, "550 File not found.\r\n");
        } else {
            socket_write($client, "150 Opening binary mode data connection.\r\n");
            $file = fopen($filepath, 'r');
            while (!feof($file)) {
                $buffer = fread($file, 2048);
                socket_write($client, $buffer);
            }
            fclose($file);
            socket_write($client, "226 Transfer complete.\r\n");
        }
    }

    else {
        socket_write($client, "500 Unknown command.\r\n");
    }

    socket_close($client);
}

三、客户端模拟

虽然可以使用 telnet/netcat 测试,但为了方便,你也可以写一个简单的 PHP 客户端进行上传:

<?php

$host = 'm66.net';
$port = 2121;

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, $host, $port);

$response = socket_read($socket, 1024);
echo $response;

$filename = 'test.txt';
$data = file_get_contents($filename);
$command = "UPLOAD $filename\r\n";

socket_write($socket, $command);
usleep(100000); // 稍作延迟
socket_write($socket, $data);

$response = socket_read($socket, 1024);
echo $response;

socket_close($socket);

四、安全与改进建议

  1. 权限控制:实际部署时需要添加认证机制防止非法访问。

  2. 多线程/并发:使用 pcntl_fork() 或使用 swoole 等扩展提升性能。

  3. 文件校验:通过 MD5 校验码确保文件完整性。

  4. 加密传输:通过 TLS/SSL 实现安全通信。

五、总结

本文展示了如何通过 PHP 原生的 socket 编程手段,结合 socket_accept() 实现一个简易的类 FTP 文件传输服务。这种方式适合用于学习 socket 通信的基本原理,也可作为轻量级传输服务的雏形。对于更大规模的项目,建议使用更成熟的解决方案,如 FTP/SFTP 服务器或 WebDAV 服务。