Tiago,

mesmo sabendo usar um framework tipo Net::Server (p.exe), 
vai ser sua responsabilidade tratar a conversa com o cliente de forma robusta, 
sem impactos negativos no seu ‘server’.

estando no contexto de um processo filho do server, vai ter que ler o socket 
até receber a estrutura completa da msg estipulada:

- seja por tamanho da msg (meu caso no exemplo do codigo abaixo)
- seja por CRLF (seu caso)
- seja por outra estrutura qqe

Tendo recebido a msg completa, passa-se então para o parser validá-la.

O seu problema, pelo que entendi, é tratar de forma robusta a conversa com o 
cliente, independente do que ele faça ou não faça – sem afetar o funcionamento 
do server.

Leia o trecho de codigo abaixo assumindo que:

- o processo que chama a sub _read_socket foi forkado pelo server
- o socket acceptado na conversa com o cliente esta mapeado no STDIN
- importante, nesse caso, _read_socket le o socket ate atingir $sz bytes de 
conversa => mude a sub para ler enquanto nao encontra o seu CRLF
- $$pin vai conter a msg de $sz bytes
- em caso de timeout na conversa, devolve erro

Nao tem probl ficarmos blocados nessa sub esperando a msg completa dado que 
somos um filho forkado do server e estamos suportados pelo timeout na conversa.

Assuma tb que o server nao starta mais do que N filhos e os controla bonitinho 
(cria e mata se for o caso). Net::Server faz isso por vc.

No nosso caso, refizemos o server para ser mais leve e acabamos nao usando o 
Net::Server.

Alguem, acima, vai tratar e parsear a msg em $$pin, depois de ter recebido a 
msg completa.

Espero que ajude, pontualmente, na sua questão principal que é ler o socket de 
forma segura, sem impactos negativos no processo.

sub _read_socket {
    my $self = shift;
    my $pin  = shift;
    my $sz   = shift;

    # tratamento de timeout por select e nao por alarm!
    my $idle_max = $self->{TIMEOUT_SOCKET} || 10;
    my $idle     = 0;
    my $mask;
    vec($mask, fileno(STDIN), 1) = 1;

    my $nr;
    my $buf;
    my $len = 0;
    my $off = 0;
    while (1) {

        if ($idle > $idle_max) {
            return(0,3,"Socket read timeout ($idle_max segs). Esperados $sz 
bytes, recebido(s) $len");
        }
        my $rmask = undef;
        my $sel   = select($rmask=$mask, undef, undef, 1); # 1seg
        if (!$sel) { $idle++; next; };
        if(vec($rmask, fileno(STDIN), 1)) {

            eval { $nr = sysread(STDIN, $buf, $sz-$len, $off); };
            if ($@) {
                return(0,3,"Erro ao ler socket: $@: $buf");
            }
            if ($!) {
                return(0,3,"Erro ao ler socket: $!: $buf");
            }
            unless ($nr) {
                return(0,3,"Erro ao ler socket: EOF");
            }
            $self->_info("SOCKET IN $nr = [". substr($buf, $off, $nr) . ']'); # 
if ($self->{DEBUG} > 2);
            $off += $nr;
            $len += $nr;
            last if ($len >= $sz);
        }
    }

    $$pin = $buf;
    return(1,0,'');
}


            Ulisses Gomes
      Tecnologia da Informação

      IBIZ Tecnologia
      +55 11 5579-3178 r. 226
      [email protected]
      www.ibiz.com.br  

 



Esta mensagem de correio eletrônico e seus documentos anexos estão dirigidos 
EXCLUSIVAMENTE aos destinatários especificados. A informação contida pode ser 
CONFIDENCIAL e/ou estar LEGALMENTE PROTEGIDA e não necessariamente reflete a 
opinião da IBIZ. Se você receber esta mensagem por ENGANO, por favor, comunique 
imediatamente ao remetente e ELIMINE-A já que você NÃO ESTÁ AUTORIZADO ao uso, 
revelação, distribuição, impressão ou cópia de toda ou alguma parte da 
informação contida. Obrigado. 


From: Tiago Peczenyj 
Sent: Sunday, November 25, 2012 1:47 PM
To: [email protected] 
Subject: Re: [SP-pm] duvida sockets e select

uma parte do meu protocolo é 

timestamp<SEPARADOR>tipo<SEPARADOR>etc..CRLF

sendo meu separador algo arbitrario, uma virgula por exemplo. é uma string utf-8

eu criei um buffer para entra entrada e vou acumulando até encontrar uma linha 
e assim processar

meu problema era que como eu tenho que rotear estes eventos para outros 
clientes baseados em regras internas, tenho que usar I/O nonblocking para não 
ficar travado se um cliente não esta lendo/escrevendo (meu cliente só se 
identifica), ou se a fonte de eventos não esta escrevendo. agora eu resolvi 
isso de forma razoavel mas tudo esta sobrecarregado em um processo. se eu 
quiser  usar outros processos ou mesmo outras maquinas fisicas eu tenho que 
pensar mais um pouco pois eu tenho um estado interno (baseado nos eventos) que 
preciso replicar e aquela coisa toda que a gente resolve usando banco de dados, 
filas de mensagens, load balance, sistema de arquivos, etc. como é prova de 
conceito não quero sobracarregar com muita coisa que agora eu não preciso.


2012/11/25 Thiago Rondon <[email protected]>


  Tiago,

  Uma pergunta: Qual o padrão do teu protocolo ? Me parece que o maior problema 
aqui é a falta de um padrão para disposição e processamento.

  Basicamente, a pergunta: É orientado a linhas ou é a blocos ? Deve ser 
respondida tanto do lado de disposição, como de processamento. E ela deve ser 
bem "clara" e "suave" para ambas na minha opinião.

  Me parece que teu problema, exige algo orientada a blocos, algo como alguns 
protocolos já funcionam assim.

  Mas, voltando ao problema. Se for a linha, para solucionar o seu problema, 
você provavelmente vai ter que depender de um "histórico" comum, como por 
exemplo criar uma pilha de processamento, e manter o tamanho desta pilha em um 
determinado tamanho, o que também deve ser "especificado" no protocolo, qual o 
tamanho que ela pode ter, e quais características ela deve ter. Como por 
exemplo, o que defini que uma linha vai para um buffer, ou não, e por aí vai.

  Se for por blocos, por exemplo separado por duas linhas, fica mais simples, 
pois você pode ter algo como:

  $buffer =~ s/^(.+)(?:\015\012\015\012)//sox

  O uso do POE é bem interessante neste caso, pois ele oferece o método 
"anyevent_read_type" para definir o formato do seu protocolo na sua classe, e 
"configurando" ela de forma que corresponde ao teu protocolo, todo o resto fica 
bem mais simples de se manusear e sem muitos efeitos colaterais.

  abs!
  -Thiago Rondon



  On Saturday, November 24, 2012 at 7:52 PM, Tiago Peczenyj wrote:

  > Ok
  >
  > Recebi a dica de usar sysread e "funciona"
  >
  > entretanto o meu problema é: eu tenho um protocolo orientado a linhas, cada 
linha é um "evento" que eu preciso processar. posso pegar uma linha pela metade 
e eu teria que guardar para "emendar" no proximo loop.
  >
  > pensei em algo como
  >
  > while ($buf =~ s/^(.*)\n//) { process_message("$1"); }
  > mas como eu faço na situação
  >
  > envio
  >
  > linha1
  > linha2
  > linha3
  >
  > e quando leio...
  >
  > buffer => "linha1\r\nlin";
  >
  > eu preciso guardar o "lin" pra proxima fase. tem alguma forma menos obscura 
de faze-lo?
  >

  > 2012/11/24 Tiago Peczenyj <[email protected] 
(mailto:[email protected])>

  > > Ola Galera
  > >
  > > Eu tenho algo mais ou menos assim
  > >
  > > https://gist.github.com/4141422
  > >
  > > O que acontece é que, se eu fizer
  > >
  > > bash$ (echo "xxx" ;sleep 30 ; echo "yyy") | nc localhost 9090
  > >
  > > ta de boa, o xxx e o yyy são impressos em momentos distintos mas
  > >
  > > bash$ (echo -n "xxx" ;sleep 30 ; echo "yyy") | nc localhost 9090
  > >
  > > O server TRAVA enquanto
  > >
  > > Agora o que acontece: eu tenho que escutar em outra porta (otimi o codigo 
mas basicamente eu inicio outro IO::Socket::INET em outra porta e trato dentro 
do main loop) outros clientes e fazer coisas divertidas com eles. com o server 
travado eu não faço mais nada.
  > >
  > > achei q era o getline mas substituindo por getc para ler caracter a 
caracter eu só consigo ler o primeiro antes de travar.
  > >
  > > alguma ideia?
  > >
  > > outra coisa, eu suportamente tenho que ler um protocolo simples, uma 
linha com um padrão, como
  > >
  > > a|b|c|d\r\n
  > >
  > > então o getline me é util, não achei outra forma de brincar com um buffer 
sem ficar maluco. alias eu deveria suportar utf-8 na entrada porem eu não vi 
diferença no binmode, tem algum truque? estou esquecendo de algo? o 
common::sense ja faz algo por mim?
  > >
  > > Obrigado
  > >
  > >
  > >
  > > --
  > > Tiago B. Peczenyj
  > > Linux User #405772
  > >
  > > http://pacman.blog.br
  >
  >
  >
  > --
  > Tiago B. Peczenyj
  > Linux User #405772
  >
  > http://pacman.blog.br

  > =begin disclaimer
  > Sao Paulo Perl Mongers: http://sao-paulo.pm.org/

  > SaoPaulo-pm mailing list: [email protected] (mailto:[email protected])

  > L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
  > =end disclaimer



  =begin disclaimer
     Sao Paulo Perl Mongers: http://sao-paulo.pm.org/
  SaoPaulo-pm mailing list: [email protected]
  L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
  =end disclaimer





-- 
Tiago B. Peczenyj
Linux User #405772

http://pacman.blog.br



--------------------------------------------------------------------------------
=begin disclaimer
   Sao Paulo Perl Mongers: http://sao-paulo.pm.org/
SaoPaulo-pm mailing list: [email protected]
L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
=end disclaimer
=begin disclaimer
   Sao Paulo Perl Mongers: http://sao-paulo.pm.org/
 SaoPaulo-pm mailing list: [email protected]
 L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
=end disclaimer

Responder a