Não é possível especificar um mapa de portas para uma porta de um IP interno
Mas mapeei o IP externo para o servidor Linux interno.
Mas também quero usar o VNC para conectar-me externamente a um computador Windows interno.
Então eu escrevi este programa e o princípio é assim
Este programa abrirá uma porta no servidor Linux para ação de escuta. Quando a conexão externa estiver conectada a esta porta, o programa abrirá outra conexão com o VNC interno do Windows e lançará os pacotes externos intactos para a conexão VNC online. jogue os dados retornados pela conexão VNC intactos de volta para a porta externa.
Código do programa:
#!/usr/bin/php -q
<?php
$IP = '192.168.1.1' ; //IP do computador Windows
$Port = '5900' ; //Porta usada pelo VNC
$ServerPort = '9999' ; //Porta usada externamente pelo servidor Linux
$RemoteSocket = false ; //Conecta ao soquete do VNC
função SignalFunction & #40;$Signal)
& #123;
//Esta é a função de processamento de mensagens do Processo principal
global $PID ; //PID do processo filho
alterne & #40;$Signal)
& #123;
caso SIGTRAP & #58;
caso SIGTERM & #58;
//Recebe o sinal para encerrar o programa
se& #40;$PID)
& #123;
//Envia um sinal SIGTERM para a criança para dizer-lhe para terminar rapidamente.
posix_kill & #40;$PID,SIGTERM);
//Aguarde o processo filho terminar para evitar zumbis
pcntl_wait & #40;$Status);
& #125;
//Fecha o Socket aberto pelo Processo principal
DestruirSocket & #40;);
exit& #40;0); //Termina o processo principal
quebrar;
caso SIGCHLD & #58;
/*
Quando o processo filho terminar, o filho enviará um sinal SIGCHLD para o pai
Quando o Pai recebe o SIGCHLD, ele sabe que o Processo Filho terminou e deve realizar algumas ações finais*/
unset& #40;$PID); //Limpa $PID para indicar que o processo filho terminou
pcntl_wait & #40;$Status); //Evite Zumbi
quebrar;
padrão& #58;
& #125;
& #125;
função ChildSignalFunction & #40;$Signal)
& #123;
//Esta é a função de processamento de mensagens do Processo Filho
alterne & #40;$Signal)
& #123;
caso SIGTRAP & #58;
caso SIGTERM & #58;
//Processo filho recebe a mensagem final
DestroySocket & #40;); //Fechar soquete
exit& #40;0); //Finalizar processo filho
padrão& #58;
& #125;
& #125;
função ProcessSocket & #40;$ConnectedServerSocket)
& #123;
//Função de processamento do soquete do processo filho
//$ConnectedServerSocket -> Socket conectado externamente
globais $ServerSocket , $RemoteSocket , $IP , $Port ;
$ServerSocket = $ConnectedServerSocket ;
declare& #40;ticks = 1); //Esta linha deve ser adicionada, caso contrário a função de processamento de mensagens não pode ser definida.
//Definir função de processamento de mensagens
if& #40;!pcntl_signal(SIGTERM, "ChildSignalFunction"))
if& #40;!pcntl_signal(SIGTRAP, "ChildSignalFunction"))
//Cria um Socket conectado ao VNC
$RemoteSocket = socket_create & #40;AF_INET, SOCK_STREAM,SOL_TCP);
//Conecta ao VNC interno
@ $RemoteConnected = socket_connect & #40;$RemoteSocket,$IP,$Port);
if& #40;!$RemoteConnected) //Não é possível conectar ao final do VNC
//Defina o processamento do Socket como Nonblock para evitar que o programa seja bloqueado
if& #40;!socket_set_nonblock($RemoteSocket))
if& #40;!socket_set_nonblock($ServerSocket))
enquanto& #40;verdadeiro)
& #123;
//Aqui usamos pooling para obter dados
$NoRecvData = false ; //Esta variável é usada para determinar se a conexão externa leu dados.
$NoRemoteRecvData = false ; //Esta variável é usada para determinar se a conexão VNC leu dados.
@ $RecvData = socket_read & #40;$ServerSocket,4096,PHP_BINARY_READ);
//Lê 4.096 bytes de dados da conexão externa
@ $RemoteRecvData = socket_read & #40;$RemoteSocket,4096,PHP_BINARY_READ);
//Lê 4.096 bytes de dados da conexão vnc
se& #40;$RemoteRecvData==='')
& #123;
//A conexão VNC foi interrompida, é hora de terminar
echo "Fechar conexão remotan" ;
retornar;
& #125;
se& #40;$RemoteRecvData===falso)
& #123;
/*
Como estamos usando o modo não-blobk, a situação aqui é que a conexão vnc não possui dados para ler.
*/
$NoRemoteRecvData = verdadeiro ;
//Limpa o último erro
socket_clear_error & #40;$RemoteSocket);
& #125;
se& #40;$RecvData==='')
& #123;
//A conexão externa foi interrompida, é hora de terminar
echo "Conexão do cliente fechadan" ;
retornar;
& #125;
se& #40;$RecvData===falso)
& #123;
/*
Como estamos usando o modo não-blobk, a situação aqui é que não há dados disponíveis para leitura na conexão externa.
*/
$NoRecvData = verdadeiro ;
//Limpa o último erro
socket_clear_error & #40;$ServerSocket);
& #125;
if& #40;$NoRecvData&&$NoRemoteRecvData)
& #123;
//Se nem a conexão externa nem a conexão VNC tiverem dados para ler,
//Deixe o programa dormir por 0,1 segundos para evitar o uso prolongado de recursos da CPU
dormir & #40;100000);
//Depois de acordar, continue a ação de pool para ler o soquete
continuar;
& #125;
//Dados recebidos
se& #40;!$NoRecvData)
& #123;
//Conexão externa lê dados
enquanto& #40;verdadeiro)
& #123;
//Transfere os dados lidos da conexão externa para a conexão VNC
@ $WriteLen = socket_write & #40;$RemoteSocket,$RecvData);
se& #40;$WriteLen===falso)
& #123;
//Devido a problemas de transmissão de rede, os dados não podem ser gravados no momento.
//Durma por 0,1 segundos antes de tentar novamente.
dormir & #40;100000);
continuar;
& #125;
se& #40;$WriteLen===0)
& #123;
//A conexão remota é interrompida, o programa deve terminar
echo "Fechar conexão de gravação remotan" ;
retornar;
& #125;
//Quando os dados lidos da conexão externa forem completamente enviados para a conexão VNC, este loop é interrompido.
if& #40;$WriteLen==strlen($RecvData))
//Se os dados não puderem ser enviados de uma vez, eles deverão ser divididos em várias transmissões até que todos os dados sejam enviados.
$RecvData = substr & #40;$RecvData,$WriteLen);
& #125;
& #125;
if& #40;!$NoRemoteRecvData)
& #123;
//Aqui estão os dados lidos da conexão VNC e depois transferidos de volta para a conexão externa.
//O princípio é quase o mesmo acima e não entrarei em detalhes novamente.
enquanto& #40;verdadeiro)
& #123;
@ $WriteLen = socket_write & #40;$ServerSocket,$RemoteRecvData);
se& #40;$WriteLen===falso)
& #123;
dormir & #40;100000);
continuar;
& #125;
se& #40;$WriteLen===0)
& #123;
echo "Fechar conexão de gravação remotan" ;
retornar;
& #125;
if& #40;$WriteLen==strlen($RemoteRecvData))
$RemoteRecvData = substr & #40;$RemoteRecvData,$WriteLen);
& #125;
& #125;
& #125;
& #125;
função DestroySocket & #40;)
& #123;
//Usado para fechar o Socket aberto
global $ServerSocket , $RemoteSocket ;
se& #40;$RemoteSocket)
& #123;
//Se a conexão VNC já estiver habilitada
//O Socket deve ser desligado antes de fechar o Socket, caso contrário a outra parte não saberá que você encerrou a conexão.
@socket_shutdown & # 40;$RemoteSocket,2);
socket_clear_error & #40;$RemoteSocket);
//Fecha o soquete
socket_close & #40;$RemoteSocket);
& #125;
//Fecha conexões externas
@ socket_shutdown & #40;$ServerSocket,2);
socket_clear_error & #40;$ServerSocket);
socket_close & #40;$ServerSocket);
& #125;
//Este é o início de todo o programa, e a execução do programa começa a partir daqui
//Primeiro execute um fork aqui
$PID = pcntl_fork & #40;);
se& #40;$PID==-1) morrer("não foi possível bifurcar");
//Se $PID não for 0, significa que este é um processo pai
//$PID é processo filho
//Este é o Processo Pai. Termine você mesmo e deixe o Filho se tornar um Daemon.
se& #40;$PID) morrer("Daemon PID:$PIDn");
//A partir daqui o modo Daemon é executado.
//Desconecte o processo atual do terminal e entre no modo daemon
if& #40;!posix_setsid()) die("não foi possível desconectar do terminaln");
//Define a função de processamento de mensagens do daemon
declare& #40;carrapatos = 1);
if& #40;!pcntl_signal(SIGTERM, "SignalFunction"))
if& #40;!pcntl_signal(SIGTRAP, "SignalFunction"))
if& #40;!pcntl_signal(SIGCHLD, "SignalFunction"))
//Estabelece um Socket para conexão externa
$ServerSocket = socket_create & #40;AF_INET, SOCK_STREAM,SOL_TCP);
//Defina o IP e a Porta para monitoramento da conexão externa Defina o campo IP como 0, o que significa escutar os IPs de todas as interfaces.
if& #40;!socket_bind($ServerSocket,0,$ServerPort))
//Comece a ouvir o Porto
if& #40;!socket_listen($ServerSocket)) morrer("Não é possível ouvir!n");
//Configura o Socket para modo sem bloqueio
if& #40;!socket_set_nonblock($ServerSocket)) die("Não é possível definir o soquete do servidor para bloquear!n");
//Limpa a variável $PID, indicando que atualmente não há Processo Filho
não definido& #40;$PID);
enquanto& #40;verdadeiro)
& #123;
//Entra no modo pooling e verifica se há uma conexão a cada 1 segundo.
dormir & #40;1);
//Verifica se há uma conexão chegando
@ $ConnectedServerSocket = socket_accept & #40;$ServerSocket);
if& #40;$ConnectedServerSocket!==falso)
& #123;
//Alguém está entrando
//Iniciar um processo filho para lidar com a conexão
$PID = pcntl_fork & #40;);
se& #40;$PID==-1) morrer("não foi possível bifurcar");
if& #40;$PID) continue; //Este é o processo do daemon, continue monitorando.
//Aqui está o início do Processo Filho
//Executa a função no Socket
ProcessSocket & #40;$ConnectedServerSocket);
//Depois de processar o Socket, finaliza o Socket
DestruirSocket & #40;);
//Finalizar processo filho
sair& #40;0);
& #125;
& #125;
?>