problema de piping data into nc

1

Eu escrevi um pequeno servidor UDP no PHP que escuta em um soquete, aceita um comando e executa uma ação. É realmente muito básico. Eu posso conectar-me manualmente assim:

% nc -u host port

(onde nc = Ncat: Versão 7.50 ( link ))

Ao inserir comandos, vejo a resposta resultante. Ele funciona exatamente da maneira que eu quero que ele funcione na linha de comando.

No entanto, se eu simplesmente "cat" um arquivo assim:

cat FILE| nc -u host port

ou envie dados como este:

echo "command1\ncommand2\n" | nc -u host port

... então meu aplicativo PHP lê tudo, incluindo os caracteres de fim de linha, todos de uma vez. Eu só quero ler até o final da linha.

Claro, eu poderia envolver o conteúdo do arquivo e enviar cada linha para nc:

for x in 'cat <file>'; do
  echo $x | nc -u host port
done

... mas isso é um completo desperdício. Eu quero 1 conexão para nc, não muitos.

Os caracteres EOL fazem isso na sequência porque, quando imprimo a saída no aplicativo PHP, vejo: comando1 comando2 ... mas está tudo em uma cadeia.

Por que o modo interativo está se comportando de maneira diferente do modo não interativo?

Eu tenho experimentado a tarde toda, e não consigo fazer isso funcionar.

Tenho certeza de que há uma explicação.

Obrigado por qualquer informação que você possa fornecer.

PS: O básico do código PHP é:

$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($sock, '0.0.0.0', 2000);
for (;;) {
  socket_recvfrom($sock, $cmd, 1024, 0, $ip, $port);
  echo "command is $cmd";
  $reply = process($cmd);
  echo "reply is $reply";
  socket_sendto($sock, $reply, strlen($reply), 0, $ip, $port);
}

Ao inserir texto com nc interativamente, você pode pressionar CTRL-D para separar os pacotes. Se eu fizer a mesma coisa em um script de shell:

printf 'command1
stdbuf -oL -eL cat FILE | nc -u host port 
14commandd2
for x in command1 command2; do 
  echo $x 
  sleep 1
done | nc -u host port
14'| nc -u host port

... todos os comandos aparecem como um comando.

Se eu reduzir o tamanho do pacote no código PHP para dizer 5 e, em seguida, enviar dados com 20 bytes, os dados serão truncados e não separados.

Eu também estou pronto on-line, isso pode ser um problema de buffering. Eu tentei usar:

% nc -u host port

... mas isso surpreendentemente não fez diferença.

Finalmente, descobri que, se eu fizer isso:

cat FILE| nc -u host port

... tudo corre conforme o planejado. O servidor recebe o primeiro comando e, em seguida, o segundo comando.

O que não está claro é por que o sono 1 faz a diferença. Retire e isso falhará. O acima é certamente melhor do que enviar cada eco para nc.

    
por Jason K 04.10.2018 / 22:17

2 respostas

1

Seu código está na maior parte do caminho, tudo que você precisa fazer agora é tratar $cmd como uma fila de strings terminadas por nova linha. No pseudo-código ingênuo do Python, porque eu não escrevo PHP há muito tempo:

input = ''
try:
    first_command, remaining_input = input.split('\n', 1)
    execute(first_command)
    input = remaining_input
except ValueError:
    # No newline yet; we have to wait for more input
    input = socket.receive()

Dessa forma, você pode manipular os dois fluxos de entrada e comandos únicos e interativos, o que provavelmente não será o modo como seu aplicativo será usado.

    
por 05.10.2018 / 05:33
0

l0b0 resolveu seu problema, mas respondeu alguns de seus outros pontos:

Sure, I could wrap around the contents of the file, and send each line to nc: for x in $(cat <file>); do echo $x | nc -u host port; done but that's a complete waste. I want 1 connection to nc, not many.

1) que na verdade não envia linhas do arquivo, ele envia qualquer coisa separada por espaço em branco; o espaço em branco pode ser nova linha, mas também tabulação ou espaço (dado que você não alterou o IFS). Somente se a sua idéia de um 'comando' estiver restrita a uma única palavra, estas são as mesmas, mas em geral a maioria dos comandos Unix (y) são múltiplas palavras. Essas palavras também são processadas para a expansão do nome do caminho do shell (geralmente chamado de 'globbing'), portanto, se elas contiverem qualquer ?*[..] , o (s) valor (es) usado (s) por $x pode ser diferente do que estava no arquivo.

2) Nenhuma conexão é 'desperdiçada' porque o UDP é sem conexão e nenhuma conexão existe. Talvez você queira dizer que não deseja execuções do programa nc (localmente).

Why is the interactive mode behaving differently than the non-interactive mode?

A entrada em um terminal no Unix é normalmente tratada com uma linha por vez; quando o programa (aqui nc ) faz um read espera enquanto você digita quantos caracteres você quiser - e opcionalmente os edita - até que você retorne (ou equivalente), então a linha de entrada é entregue para o programa (que por nc envia como um pacote). (A menos que o buffer do programa seja muito pequeno, somente a parte que cabe é entregue, e qualquer resíduo permanece pendente para a próxima leitura.) Opcionalmente, isso pode ser desativado e tão pouco quanto cada caractere (ou grupo de caracteres produzido por uma tecla) como uma tecla de função) pode ser entregue separadamente, que é usado por programas como vi - e, embora seja menos óbvio, bash . Oficialmente, eles são chamados de modos 'canônico' e 'não-canônico' , mas historicamente o modo não canônico foi descrito como "cru" e o modo canônico como "cozido".

A leitura de qualquer outro tipo de arquivo - incluindo um canal - ignora as quebras de linha (ou seja, caracteres de nova linha) e lê o quanto se encaixa no buffer, o que, no seu caso, inclui várias linhas.

While entering text with nc interactively, you can hit CTRL-D to separate packets. If I do the same thing in a shell script: printf 'command1

I've also ready online this could be a buffering issue. I tried using: stdbuf -oL -eL cat FILE | nc -u host port ... but that surprisingly didn't make a difference either.

14commandd2

Finally, I have discovered that if I do: for x in command1 command2; do echo $x; sleep 1; done | nc -u host port that everything goes as planned. The server receives the first command, then the second command.

What isn't clear really is why the sleep 1 makes a difference. ...

14'| nc -u host port
... the commands all appear as one command.

3) o controle-D em um terminal é especial - ou mais exatamente, o caractere configurado como a configuração eof no driver do terminal, que geralmente é o controle-D, mas pode ser alterado, é especial. Quando você digita este caractere enquanto um programa está lendo, a leitura é terminada sem esperar por um retorno e também sem incluir o controle-D nos dados. O código de caractere que corresponde ao control-D não é especial quando ocorre em um arquivo.

4) e o personagem que você gerou não é controle-D de qualquer maneira. Control-D é echo4 na notação octal usada para muitas versões de \x04 e screen1 em alguns outros contextos. stdbuf é o controle-A, também chamado de STX, e praticamente não utilizado no Unix (exceto no programa cat , que o utiliza para controlar a alternância entre várias janelas do terminal virtual).

%bl0ck_qu0te%

5) strace afeta (somente) as rotinas 'stdio' da biblioteca C, e cat provavelmente não as utiliza porque foi projetado para trabalhar em dados binários de texto e não-texto. No meu sistema de teste mais conveniente (CentOS6) stdbuf confirma que GNU-coreutils-8.4 sed '' file; grep '' file; awk 1 file não é afetado por stdbuf -oL , mas não posso descartar que outras implementações poderiam ser. Usando programas projetados para manipular texto em linhas como nc confirmo que nc faz diferença.

Mas pode não fazer a diferença que você quer. Haverá uma condição de corrida entre o programa gravador adicionando linhas ao pipe e %code% lendo (e enviando) elas, e se o gravador for mais rápido, você ainda terá várias linhas agrupadas em um pacote de saída.

%bl0ck_qu0te%

6) Isso evita a condição de corrida. Cada vez que o shell escreve uma linha no pipe %code% definitivamente terá tempo para lê-lo (e enviá-lo) antes da próxima gravação do shell.

    
por 07.10.2018 / 02:37