家用無線 AP 僅支援將公網 IP 映射到內網 Linux 伺服器,無法針對特定埠做端口轉發。需從外部經由 Linux 伺服器的某個埠,連線到內網 Windows 主機的 VNC 服務(預設埠 5900)。
程式在 Linux 伺服器監聽指定埠(如 9999),收到外部連線後,fork 子程序連接內網 VNC 埠,雙向透傳資料封包,實現「外部埠 → Linux → 內網 VNC」的通道。
<?php
$IP = "192.168.1.1"; // Windows 主機 IP
$Port = 5900; // VNC 埠
$ServerPort = 9999; // Linux 對外監聽埠
$RemoteSocket = false; // VNC 連線 Socket
// 訊號處理:父程序捕捉 SIGTERM/SIGTRAP 終止子程序,SIGCHLD 回收僵屍程序
function SignalFunction($Signal) { ... }
function ChildSignalFunction($Signal) { ... }
// 子程序:雙向資料轉發
function ProcessSocket($ConnectedServerSocket) {
global $ServerSocket, $RemoteSocket, $IP, $Port;
$ServerSocket = $ConnectedServerSocket;
$RemoteSocket = socket_create(AF_INET, SOCK_STREAM, 0);
socket_connect($RemoteSocket, $IP, $Port);
// 使用 socket_select 同時監聽兩個 Socket,實現雙向透傳
while (true) {
$read = array($ServerSocket, $RemoteSocket);
socket_select($read, $write, $except, NULL);
foreach ($read as $sock) {
$data = socket_read($sock, 8192);
if ($data === false || $data === "") break 2;
$target = ($sock === $ServerSocket) ? $RemoteSocket : $ServerSocket;
socket_write($target, $data, strlen($data));
}
}
DestroySocket();
}
// 主程序:建立監聽 Socket,accept 後 fork 子程序處理
$ServerSocket = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($ServerSocket, "0.0.0.0", $ServerPort);
socket_listen($ServerPort);
pcntl_signal(SIGTERM, "SignalFunction");
pcntl_signal(SIGCHLD, "SignalFunction");
while (true) {
$ClientSocket = socket_accept($ServerSocket);
$pid = pcntl_fork();
if ($pid == -1) { socket_close($ClientSocket); continue; }
if ($pid == 0) { // 子程序
pcntl_signal(SIGTERM, "ChildSignalFunction");
ProcessSocket($ClientSocket);
exit(0);
} else { // 父程序
$PID = $pid;
socket_close($ClientSocket);
}
}
?>
pcntl_fork() 建立子程序,各自處理獨立連線pcntl_signal)實現優雅關閉與僵屍程序回收socket_select() 多路複用實現高效雙向資料轉發此範例展示了 PHP 在系統層面網路程式設計的能力,可擴展為通用的 TCP 端口轉發工具。