Невозможно указать сопоставление портов с портами внутреннего IP-адреса.
Но я сопоставил внешний IP-адрес с внутренним Linux- сервером.
Но я также хочу использовать VNC для подключения к внутреннему компьютеру Windows извне.
Итак, я написал эту программу, и принцип такой
Эта программа откроет порт на Linux-сервере для прослушивания. Когда к этому порту подключено внешнее соединение, программа откроет другое соединение с внутренним VNC Windows и передаст внешние пакеты в неповрежденном виде в соединение VNC, а затем в онлайн-режиме. передать данные, возвращенные соединением VNC, обратно во внешний порт в целости и сохранности.
Код программы:
#!/usr/bin/php -q
<?php
$IP = '192.168.1.1' ; //IP-адрес компьютера с ОС Windows
$Port = '5900' ; //Порт, используемый VNC
$ServerPort = '9999 ' //Порт, используемый Linux-сервером извне
$ RemoteSocket = false //Подключаемся к сокету VNC
функция SignalFunction & #40;$Signal)
& #123;
//Это функция обработки сообщений основного процесса
global $PID ; //PID дочернего процесса
переключатель & #40;$Signal)
& #123;
случай SIGTRAP & #58;
случай SIGTERM & #58;
//Получаем сигнал о завершении программы
if& #40;$PID)
& #123;
//Отправляем сигнал SIGTERM Чайлду, чтобы попросить его быстро завершить работу.
posix_kill & #40;$PID,SIGTERM);
//Подождите, пока дочерний процесс завершится, чтобы избежать зомби
pcntl_wait & #40;$Status);
& #125;
//Закрываем сокет, открытый основным процессом
УничтожитьSocket & #40;);
выход& #40;0) //Завершить основной процесс
перерыв;
случай SIGCHLD & #58;
/*
Когда дочерний процесс завершится, дочерний процесс отправит сигнал SIGCHLD родителю.
Когда родительский процесс получает SIGCHLD, он знает, что дочерний процесс завершился и должен предпринять некоторые завершающие действия*/
unset& #40;$PID) //Очистить $PID, чтобы указать, что дочерний процесс завершился
pcntl_wait & #40;$Status) //Избегайте зомби
перерыв;
по умолчанию& #58;
& #125;
& #125;
функция ChildSignalFunction & #40;$Signal)
& #123;
//Это функция обработки сообщений дочернего процесса
переключатель & #40;$Signal)
& #123;
случай SIGTRAP & #58;
случай SIGTERM & #58;
//Дочерний процесс получает сообщение о завершении
DestroySocket & #40;); //Закрыть сокет
выход& #40;0) //Завершить дочерний процесс
по умолчанию& #58;
& #125;
& #125;
функция ProcessSocket & #40;$ConnectedServerSocket)
& #123;
//Функция обработки сокета дочернего процесса
//$ConnectedServerSocket -> Сокет с внешним подключением
глобальный $ServerSocket , $RemoteSocket , $IP , $Port ;
$ServerSocket = $ConnectedServerSocket ;
Declare& #40;ticks = 1) //Эту строку необходимо добавить, иначе нельзя будет установить функцию обработки сообщений.
//Устанавливаем функцию обработки сообщений
if& #40;!pcntl_signal(SIGTERM, "ChildSignalFunction")) return;
if& #40;!pcntl_signal(SIGTRAP, "ChildSignalFunction")) return;
//Создаем сокет, подключенный к VNC
$RemoteSocket = socket_create & #40;AF_INET, SOCK_STREAM,SOL_TCP);
//Подключаемся к внутреннему VNC
@ $RemoteConnected = Socket_connect & #40;$RemoteSocket,$IP,$Port);
if& #40;!$RemoteConnected) return; //Невозможно подключиться к концу VNC.
//Установите для обработки сокетов значение «Неблокировать», чтобы предотвратить блокировку программы
if& #40;!socket_set_nonblock($RemoteSocket)) return;
if& #40;!socket_set_nonblock($ServerSocket)) return;
хотя& #40;правда)
& #123;
//Здесь мы используем пул для получения данных
$NoRecvData = false ; //Эта переменная используется для определения того, прочитало ли внешнее соединение данные.
$NoRemoteRecvData = false ; //Эта переменная используется для определения того, прочитало ли соединение VNC данные.
@ $RecvData = socket_read & #40;$ServerSocket,4096,PHP_BINARY_READ);
//Читаем 4096 байт данных из внешнего соединения
@ $RemoteRecvData = socket_read & #40;$RemoteSocket,4096,PHP_BINARY_READ);
//Читаем 4096 байт данных из vnc-соединения
if& #40;$RemoteRecvData==='')
& #123;
//VNC-соединение прервано, пора заканчивать
echo "Закрытие удаленного соединенияn" ;
возвращаться;
& #125;
if& #40;$RemoteRecvData===false)
& #123;
/*
Поскольку мы используем режим nonblobk, ситуация здесь такова, что у vnc-соединения нет данных для чтения.
*/
$NoRemoteRecvData = правда ;
//Очистить последнюю ошибку
сокет_clear_error & #40;$RemoteSocket);
& #125;
if& #40;$RecvData==='')
& #123;
//Внешнее соединение прервано, пора заканчивать
echo "Закрытие клиентского соединенияn" ;
возвращаться;
& #125;
if& #40;$RecvData===false)
& #123;
/*
Поскольку мы используем режим nonblobk, ситуация здесь такова, что данные для чтения из внешнего соединения отсутствуют.
*/
$NoRecvData = правда ;
//Очистить последнюю ошибку
сокет_clear_error & #40;$ServerSocket);
& #125;
if& #40;$NoRecvData&&$NoRemoteRecvData)
& #123;
//Если ни внешнее соединение, ни соединение VNC не имеют данных для чтения,
//Дайте программе спать 0,1 секунды, чтобы избежать длительного использования ресурсов процессора
мыспим & #40;100000);
//После пробуждения продолжаем операцию объединения для чтения сокета
продолжать;
& #125;
//Полученные данные
if& #40;!$NoRecvData)
& #123;
//Внешнее соединение считывает данные
хотя& #40;правда)
& #123;
//Переносим данные, считанные из внешнего соединения, в соединение VNC
@ $WriteLen = socket_write & #40;$RemoteSocket,$RecvData);
if& #40;$WriteLen===false)
& #123;
//Из-за проблем с передачей данных в данный момент запись данных невозможна.
//Остановитесь на 0,1 секунды, прежде чем повторить попытку.
мыспим & #40;100000);
продолжать;
& #125;
if& #40;$WriteLen===0)
& #123;
//Удаленное соединение прервано, программа должна завершиться
echo "Закрытие соединения удаленной записиn" ;
возвращаться;
& #125;
//Когда данные, считанные из внешнего соединения, полностью отправлены на соединение VNC, этот цикл прерывается.
if& #40;$WriteLen==strlen($RecvData))
//Если данные не могут быть отправлены одновременно, их необходимо разделить на несколько передач, пока все данные не будут отправлены.
$RecvData = substr & #40;$RecvData,$WriteLen);
& #125;
& #125;
if& #40;!$NoRemoteRecvData)
& #123;
//Вот данные, считанные из VNC-соединения, а затем переданные обратно во внешнее соединение.
//Принцип почти такой же, как указано выше, и я не буду снова вдаваться в подробности.
хотя& #40;правда)
& #123;
@ $WriteLen = socket_write & #40;$ServerSocket,$RemoteRecvData);
if& #40;$WriteLen===false)
& #123;
мыспим & #40;100000);
продолжать;
& #125;
if& #40;$WriteLen===0)
& #123;
echo "Закрытие соединения удаленной записиn" ;
возвращаться;
& #125;
if& #40;$WriteLen==strlen($RemoteRecvData))
$RemoteRecvData = substr & #40;$RemoteRecvData,$WriteLen);
& #125;
& #125;
& #125;
& #125;
функция DestroySocket & #40;)
& #123;
//Используется для закрытия открытого сокета
глобальный $ServerSocket , $RemoteSocket ;
if& #40;$RemoteSocket)
& #123;
//Если VNC-соединение уже включено
//Socket должен быть выключен перед закрытием Socket, иначе другая сторона не узнает, что вы закрыли соединение.
@socket_shutdown & # 40;$RemoteSocket,2);
сокет_clear_error & #40;$RemoteSocket);
//Закрыть сокет
Socket_close & #40;$RemoteSocket);
& #125;
//Закрываем внешние соединения
@socket_shutdown & # 40;$ServerSocket,2);
сокет_clear_error & #40;$ServerSocket);
сокет_закрыть & #40;$ServerSocket);
& #125;
//Это начало всей программы, отсюда программа начинает выполнение
//Сначала выполняем здесь вилку
$PID = pcntl_fork & #40;);
if& #40;$PID==-1) die("не удалось выполнить форк");
//Если $PID не равен 0, это означает, что это родительский процесс
//$PID — дочерний процесс
//Это родительский процесс. Завершите его сами и позвольте дочернему процессу стать демоном.
если& #40;$PID) умрет("Daemon PID:$PIDn");
//С этого момента выполняется режим демона.
//Отключаем текущий процесс от терминала и переходим в режим демона
if& #40;!posix_setsid()) die("не удалось отсоединиться от терминалаn");
//Устанавливаем функцию обработки сообщений демона
объявить& #40;ticks = 1);
if& #40;!pcntl_signal(SIGTERM, "SignalFunction")) die("Error!!!n");
if& #40;!pcntl_signal(SIGTRAP, "SignalFunction")) die("Error!!!n");
if& #40;!pcntl_signal(SIGCHLD, "SignalFunction")) die("Error!!!n");
//Устанавливаем сокет для внешнего подключения
$ServerSocket = socket_create & #40;AF_INET, SOCK_STREAM,SOL_TCP);
//Установим IP и порт для мониторинга внешнего соединения. Установите в поле IP значение 0, что означает прослушивание IP-адресов всех интерфейсов.
if& #40;!socket_bind($ServerSocket,0,$ServerPort)) die("Невозможно привязать сокет!n");
//Начинаем слушать порт
if& #40;!socket_listen($ServerSocket)) die("Невозможно прослушивать!n");
//Устанавливаем Socket в неблокирующий режим
if& #40;!socket_set_nonblock($ServerSocket)) die("Невозможно установить блокировку сокета сервера!n");
//Очистим переменную $PID, указывая, что в данный момент дочерний процесс отсутствует
unset& #40;$PID);
хотя& #40;правда)
& #123;
//Входим в режим пула и проверяем, есть ли соединение каждую 1 секунду.
спать & #40;1);
//Проверяем, есть ли входящее соединение
@ $ConnectedServerSocket = socket_accept & #40;$ServerSocket);
if& #40;$ConnectedServerSocket!==false)
& #123;
//Кто-то входит
//Запускаем дочерний процесс для обработки соединения
$PID = pcntl_fork & #40;);
if& #40;$PID==-1) die("не удалось выполнить форк");
if& #40;$PID) continue;//Это процесс демона, продолжайте наблюдение.
//Вот начало дочернего процесса
//Выполняем функцию в Socket
ProcessSocket & #40;$ConnectedServerSocket);
//После обработки Socket завершаем Socket
УничтожитьSocket & #40;);
//Завершаем дочерний процесс
выход& #40;0);
& #125;
& #125;
?>