Problema com OutputStream.flush()

Pessoal,

possuo uma classe client socket que esta enviando os dados para o servidor da seguinte forma :

OutputStream os = sk.getOutputStream(); os.write(v1.getBytes()); os.flush();

O problema é que os dados nao sao enviados no momento que faco o write nem o flush. Os dados só são enviados quando faço sk.close(); :?

O que posso estar fazendo de errado? :?:
Obrigado!

Como chegou a esta conclusão? Seu programa cliente é quem imprime a msg? Coloca um trecho do cliente aqui.

Só flush não resolve, você provavelmente tem de setar uma opção do socket com setTcpNoDelay (true), se estiver mandando mensagens muito pequenas.

Eu fiz um programa server pra testar isso e fico com os dois na tela.
Dai eu faco um loop no cliente com write e flush e depois faco ele esperar 4 segundos com Thread.Sleep(4000);

Enquanto isso eu fico de olho no servidor e nada aparece.

Depois que no terminad do cliente eu vejo os 4 envios e faco o sk.close, aparece na tela do servidor :?

System.out.println("Impressora - Obtendo buffer..."); OutputStream os = sk.getOutputStream(); os.write(vZPL.getBytes()); os.flush(); //PrintWriter saida = new PrintWriter(sk.getOutputStream(),true); //os.close(); os = null; //saida.print(vZPL.toString()); //saida.write(vZPL.toString()); System.out.println(vZPL.toString()); System.out.println(" ENVIOU "); Thread.sleep(4000);

Essa minha classe só faz o envio, entao nao tem muito como colocar o loop pois esta em outra classe, mas imagine que eu chamo esse método 4 vezes. Na tela cliente ele imprime uma vez o texto e o ENVIOU e no servidor nada, faz isso mais 3 vezes e no servidor nada. Dai quando chamo o metodo fechaPorta do cliente que ele faz o sk.close, sao impressos todos so 4 frames no terminal do servidor :cry:

[code]public void fechaPorta(){
try
{
sk.close();
sk = null;

     System.out.println(" Desconectou...");
  }
  catch (Exception e)
  {
     e.printStackTrace();
  }

}[/code]

Como pode ver no comentario acima, ja usei o printWriter e deu na mesma…

Obrigado!

P.S. acho que esse cara tava com o mesmo problema que eu, mas acho que nao resolveu tambem
http://forum.java.sun.com/thread.jspa?threadID=531181&tstart=255

thingol, acabei de fazer isso e continuou na mesma.

Tambem tentei alterar o limite do buffer de saida utilizando o setSendBufferSize, mas tambem nao resolveu.

Eu devo estar fazendo alguma besteira :frowning:

Pessoal, meu ingles nao é lá essas coisas, mas pelo que entendi do JavaDoc, o método flush não é implementado na classe OutPutSream

[quote]Flushes this output stream and forces any buffered output bytes to be written out. The general contract of flush is that calling it is an indication that, if any bytes previously written have been buffered by the implementation of the output stream, such bytes should immediately be written to their intended destination.
The flush method of OutputStream does nothing.

[/quote]

No source do OutPutStream.flush()

    /**
     * Flushes this output stream and forces any buffered output bytes 
     * to be written out. The general contract of <code>flush</code> is 
     * that calling it is an indication that, if any bytes previously 
     * written have been buffered by the implementation of the output 
     * stream, such bytes should immediately be written to their 
     * intended destination.
     * <p>
     * The &lt;code&gt;flush&lt;/code&gt; method of &lt;code&gt;OutputStream&lt;/code&gt; does nothing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void flush() throws IOException {
    }

OutputStream é uma classe abstrata, por isso alguns métodos não são implementados.

Eu vi :frowning:

Mas o Flush eu achei que era implementado!

Como faco pra forcar um flush?
Fui ver o flush da classe PrintWriter e ela tambem nao implementa o flush!
:frowning:

Pãtz!

Essas classes são apenas a “interface” de uso. As classes que as implementam geralmente IMPLEMENTAM o flush() e outros métodos.

Bom, pelo menos a classe PrintWriter nao implementa.

/** * Flush the stream. * @see #checkError() */ public void flush() { try { synchronized (lock) { ensureOpen(); out.flush(); } } catch (IOException x) { trouble = true; } }

Quando entro no out.flus() pra ver o que ele faz :

/** * Flush the stream. If the stream has saved any characters from the * various write() methods in a buffer, write them immediately to their * intended destination. Then, if that destination is another character or * byte stream, flush it. Thus one flush() invocation will flush all the * buffers in a chain of Writers and OutputStreams. * * @exception IOException If an I/O error occurs */ abstract public void flush() throws IOException;

Ele tambem nao faz :frowning:

Vou ver as outras classes que herdam do OutPutStream…

Quando trabalho com sockets não gosto de mandar mensagens “texto” - normalmente (se for possível para mim definir o protocolo; nem sempre dá para fazer isso) uso o seguinte:

  • um cabeçalho contendo pelo menos o comprimento dos dados que serão enviados - isso em binário;
  • os dados.

Assim, quando vou ler os dados do outro lado, tento ler o cabeçalho completo, e fico esperando e tentando ler o resto dos dados, em vez de ficar achando que quando enviamos algo via sockets, eles chegam imediatamente ao outro lado, sem problemas de bufferização - há 200 lugares em que os dados são bufferizados (no próprio Java, no driver de rede, na definição do protocolo, etc.).

Na verdade, eu normalmente defino um cabeçalho com mais coisas:

  • Número mágico (valor fixo, normalmente com 2 bytes, que permite verificar se o cliente e o servidor estão sincronizados. Se o cliente não conseguir ler o número mágico no início da mensagem, descarta a mensagem até que consiga encontrar o número mágico novamente.)
  • Valor de verificação (normalmente um CRC-16 ou 32);
  • Tamanho dos dados;
  • Talvez o tipo da mensagem (mas não muitos tipos diferentes - para isso é que serve o XML e outros métodos de codificar mensagens auto-descritivas)
  • E os dados em si.

o problema é que eu não controlo o lado do servidor.

Na verdade o servidor é um PrintServer que espera uma string no formato de ZPL. Por isso não tem nem protocolo.

Eu envio a string ZPL e o PrintServer envia pra impressora na rede.

Hum, vamos ver o que dá para fazer. Deve ser nesta ordem (fora de ordem, as coisas funcionam um pouco diferente)

Você criou um socket (java.net.Socket) -> OK
Setou a opção do socket (setTcpNoDelay) -> OK
Obteve a OutputStream com getOutputStream -> OK
Mandou os bytes com write (não use nenhum wrapping, tal como um PrintWriter) -> OK
Efetuou o flush -> OK
Não sei como é que funciona essa impressora Zebra, mas se você precisa também fechar o socket (shutdownOutput, close) -> ?

É isso ai. Estou fazendo todos estes passos.

Só que o problema nao esta na impressora.
O problemas é que quando faco o write e depois o flush, o socket nao envia nada pro outro lado. Pois eu criei um sever socket pra ficar vendo o que chega e nao chega nada depois que faco o write e o flush.
Posso fazer o write e o flush varias vezes que nada aparece no lado do servidor, dai quando fecho o socket, ele envia tudo de uma vez.

Ai que esta o problema, pois quando isso acontece o buffer do printServer que recebe os bytes nao suporta o tamanho todo que foi enviado. Se o socket enviasse quando eu faco o flush dai nao teria problema, pois o printServer receberia vários pacotes pequenos de dados e iria processando em fila.

Achei muito estranho isso de o flush nao ser implementado. Realmente fiquei surpreso. Como eu faria pra forcar essa escrita? Teria que fazer algum código em C ?

Valeu pela ajuda!

Ainda tem alguma coisa esquisita aí. Por via das dúvidas, fiz a seguinte busca no google:

“Zebra printers” Java

e apareceram algumas coisas curiosas (não exatamente na primeira página).

Mas acho que não tem essa de o flush não ser implementado. Para provar isso você pode fazer o seguinte:

  • Pegar o nome da classe retornada por getOutputStream (deve ser algo como com.sun.____)
  • Pegar o fonte do JDK (não é o src.zip mas o JRL ou SCSL);
  • Ver a implementação dessa classe com.sun.___ que foi retornada por getOutputStream. Acho que ou “write” já executa direto um write no socket, ou então “flush” deve forçar algum buffer. Que esquisito mesmo.