Problema com Sockets (resolvido!)

Pessoal,

Tenho uma aplicação desktop cliente-servidor com o seguinte cenário:

  • Servidor fica aguardando cliente se conectar. Quando cliente conecta (socket), cria-se uma
    Thread no servidor e o mesmo passa a aguardar o próximo cliente.
  • Vários clientes podem conectar-se ao mesmo tempo ao servidor. Cada solicitação é processada
    em uma Thread em paralelo com as outras.

Está ocorrendo o seguinte erro abaixo no servidor, quando muitos clientes conectam ao mesmo tempo.

INFO | jvm 1 | 2009/03/19 09:27:44 | java.net.SocketException: Connection reset by peer: socket write error
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.net.SocketOutputStream.socketWrite0(Native Method)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.net.SocketOutputStream.socketWrite(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.net.SocketOutputStream.write(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.io.ObjectOutputStream.(Unknown Source)

Este erro em si não é o maior problema. Acontece que, após um tempo, o módulo servidor trava com o seguinte
erro de falta de memória.

INFO | jvm 1 | 2009/03/19 09:31:23 | Exception in thread “DRDAConnThread_5”
INFO | jvm 1 | 2009/03/19 09:31:24 | java.lang.OutOfMemoryError: Java heap space
INFO | jvm 1 | 2009/03/19 09:31:27 | at java.lang.Class.getDeclaredMethods0(Native Method)
INFO | jvm 1 | 2009/03/19 09:31:28 | at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:29 | at java.lang.Class.getMethod0(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:30 | at java.lang.Class.getMethod0(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:30 | at java.lang.Class.getMethod0(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:30 | at java.lang.Class.getMethod(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:31 | at org.apache.derby.impl.drda.DRDAStatement.getParameterMetaData(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:31 | at org.apache.derby.impl.drda.DRDAConnThread.writeSQLDARD(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:31 | at org.apache.derby.impl.drda.DRDAConnThread.processCommands(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:32 | at org.apache.derby.impl.drda.DRDAConnThread.run(Unknown Source)

Abaixo parte do código no Servidor:

[code]public void run() {
try {
// Obtém pacote enviado pelo cliente.
input = new ObjectInputStream(socket.getInputStream());
pacote = (Pacote)input.readObject();
// Processa o pacote.
pacote = processarPacote(pacote);
// Envia resposta ao cliente.
output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(pacote);
output.flush();
output.reset();
} catch (ClassNotFoundException ex) {
desconectar();
ex.printStackTrace(System.out);
logger.error("[ComunicacaoThread]: “, ex);
} catch (IOException ex) {
desconectar();
ex.printStackTrace(System.out);
logger.error(”[ComunicacaoThread]: ", ex);
}
}

private void desconectar() {
try {
if ( input != null ) {
input.close();
}

        if ( output != null ) {
            output.close();
        }
        
        if ( socket != null && socket.isConnected() ) {
            socket.close();
        }
    } catch (IOException ex) {
        ex.printStackTrace(System.out);
        logger.error("[ComunicacaoThread]: ", ex);
    }
}

[/code]

E parte do código no Cliente:

// Envia pacote ao servidor.
    output = new ObjectOutputStream(socket.getOutputStream());                              
    output.writeObject(pacote);  
    output.flush();
    output.reset();
                
    boolean sucesso = false;
    int i = 0;
                
    do {
        try {
              // Recebe pacote do servidor.
                  count++;
                  input = new ObjectInputStream(socket.getInputStream());
                  pacote = (Pacote)input.readObject();                                                   
                  sucesso = true;                     
        } catch (Exception ex) {
           i++;                            
           sleep(500);                        
           ex.printStackTrace();
        }                                               
    } while(!sucesso && i < 5);

Alguém tem alguma sugestão para resolver o problema? Se for necessário mais código fonte para entender
o problema, favor avisar.

Abs,
Fabio

Não use um ObjectInputStream ou ObjectOutputStream se você não tiver lido a documentação completamente e souber para que serve o método “reset”. É que um ObjectInputStream ou um ObjectOutputStream tendem a ficar com os objetos já lidos/escritos “presos” na memória, como se desse um vazamento de memória. Se você sabe que os objetos não dependem uns dos outros, use “reset” depois de mandar cada objeto.

Obrigado pela resposta, thingol.
O que eu deveria usar ao invés de ObjectInputStream e ObjectOutputStream?

No código que postei acima, estou usando “reset” após chamar o método “writeObject” tanto no Cliente quando no Servidor.
É isso que você quis dizer ou não entendi direito?

Abs,
Fabio

  1. Se a classe Server não for muito grande envie ela toda.

  2. O stack trace está incompleto, está difícil de saber quais pontos do seu código foram processados antes do erro.

flws

Obrigado pela resposta, thingol.
O que eu deveria usar ao invés de ObjectInputStream e ObjectOutputStream?

No código que postei acima, estou usando “reset” após chamar o método “writeObject” tanto no Cliente quando no Servidor.
É isso que você quis dizer ou não entendi direito?

Abs,
Fabio[/quote]

Hum… não tinha visto que você estava usando reset (sabe como é que é, eu vejo que tem um ObjectInput/OutputStream e já ligo a resposta automática). Legal; está certo - pelo menos não é por causa disso que você vai ter problemas de vazamento de memória.

De qualquer maneira, deixe investigado se você não está ficando com conexões “perdidas” (veja com “netstat” se há muitas conexões pendentes); talvez você tenha de usar o método “shutdown” do socket quando for encerrar a conexão.
Outra coisa, veja se não há um número excessivo de threads no programa servidor.

[quote=thingol]Hum… não tinha visto que você estava usando reset (sabe como é que é, eu vejo que tem um ObjectInput/OutputStream e já ligo a resposta automática). Legal; está certo - pelo menos não é por causa disso que você vai ter problemas de vazamento de memória.

De qualquer maneira, deixe investigado se você não está ficando com conexões “perdidas” (veja com “netstat” se há muitas conexões pendentes); talvez você tenha de usar o método “shutdown” do socket quando for encerrar a conexão.
Outra coisa, veja se não há um número excessivo de threads no programa servidor.[/quote]

Então, estou desconfiado que está ocorrendo o seguinte:

  1. Cliente envia pacote ao servidor que o recebe e começa a processá-lo.
  2. Como o tráfego na rede pode estar intenso, a resposta do servidor ao cliente deve estar
    demorando para chegar. Isso causa erro na linha 9 do código abaixo (servidor) pois o cliente já
    fechou o socket.

public void run() { try { // Obtém pacote enviado pelo cliente. input = new ObjectInputStream(socket.getInputStream()); pacote = (Pacote)input.readObject(); // Processa o pacote. pacote = processarPacote(pacote); // Envia resposta ao cliente. output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(pacote); output.flush(); output.reset(); System.gc(); } catch (ClassNotFoundException ex) { desconectar(); ex.printStackTrace(System.out); logger.error("[ComunicacaoThread]: ", ex); } catch (IOException ex) { desconectar(); ex.printStackTrace(System.out); logger.error("[ComunicacaoThread]: ", ex); } }
Erro no log do servidor:

| jvm 1 | 2009/03/19 09:27:44 | java.net.SocketException: Connection reset by peer: socket write error
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.net.SocketOutputStream.socketWrite0(Native Method)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.net.SocketOutputStream.socketWrite(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.net.SocketOutputStream.write(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at java.io.ObjectOutputStream.<init>(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:27:44 | at atento.timesheet.net.ComunicacaoThread.run(ComunicacaoThread.java:123)

  1. Agora vem uma especulação de minha parte:
    Quando ocorre erro na chamada ao método socket.getOutputStream(), algum(s) objeto(s) são mantidos
    em memória, mesmo após a execução do bloco “catch” e saída do método “run”.
output = new ObjectOutputStream(socket.getOutputStream());
  1. Após muitos erros ocorrerem em sequência, a memória se esgota:

INFO | jvm 1 | 2009/03/19 09:31:23 | Exception in thread “DRDAConnThread_5”
INFO | jvm 1 | 2009/03/19 09:31:24 | java.lang.OutOfMemoryError: Java heap space
INFO | jvm 1 | 2009/03/19 09:31:27 | at java.lang.Class.getDeclaredMethods0(Native Method)
INFO | jvm 1 | 2009/03/19 09:31:28 | at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:29 | at java.lang.Class.getMethod0(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:30 | at java.lang.Class.getMethod0(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:30 | at java.lang.Class.getMethod0(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:30 | at java.lang.Class.getMethod(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:31 | at org.apache.derby.impl.drda.DRDAStatement.getParameterMetaData(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:31 | at org.apache.derby.impl.drda.DRDAConnThread.writeSQLDARD(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:31 | at org.apache.derby.impl.drda.DRDAConnThread.processCommands(Unknown Source)
INFO | jvm 1 | 2009/03/19 09:31:32 | at org.apache.derby.impl.drda.DRDAConnThread.run(Unknown Source)

Dúvida: O que fazer para liberar a memória no servidor quando o erro acima (na chamada do método socket.getOutputStream()) ocorre para evitar a exceção de OutOfMemory?

Na minha opinião o problema deve estar antes da criação dos objetos que tem este método run. Aliás acho que o socket está sendo fechado antes do método run ser efetivamente processado.

flws

Você fecha os ObjectInput___ ou ObjectOutput___ em algum “finally”?

Estava chamando o desconectar() somente nos blocos “catch”.
Alterei para o código abaixo. Será que agora elimino o problema de OutOfMemory?

[code]public void run() {
try {
// Obtém pacote enviado pelo cliente.
input = new ObjectInputStream(socket.getInputStream());
pacote = (Pacote)input.readObject();
// Processa o pacote.
pacote = processarPacote(pacote);
// Envia resposta ao cliente.
output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(pacote);
output.flush();
output.reset();
} catch (ClassNotFoundException ex) {
ex.printStackTrace(System.out);
logger.error("[ComunicacaoThread]: “, ex);
} catch (IOException ex) {
ex.printStackTrace(System.out);
logger.error(”[ComunicacaoThread]: ", ex);
} finally {
desconectar();
}
}

private void desconectar() {
     try {
        if ( input != null ) {               
            input.close();                           
        }
        
        if ( output != null ) {               
            output.close();               
        }
        
        if ( socket != null && socket.isConnected() ) {
            socket.close();              
        }
        
        anular();                
    } catch (IOException ex) {
        anular();
        ex.printStackTrace(System.out);
        logger.error("[ComunicacaoThread]: ", ex);
    }
}

private void anular() {
    input = null;
    output = null;
    socket = null;
    pacote = null;
    System.gc();      
}[/code]

será que o teu programa atinge a linha

output.reset(); ??

Se estiver dando erro no flush, ele não chega no reset e acontece o que o thingol falou… Memory Leak

[quote=fantomas]Na minha opinião o problema deve estar antes da criação dos objetos que tem este método run. Aliás acho que o socket está sendo fechado antes do método run ser efetivamente processado.
flws[/quote]
Supondo que o cliente feche o Socket antes de receber a resposta do servidor, devido ao longo tempo de espera pela resposta,
então ocorrerá erro na linha 9 do código que postei:

output = new ObjectOutputStream(socket.getOutputStream());

E acho que a memória vai enchendo com objetos que, por algum motivo, não são excluídos pelo Garbage Collector.
O que eu queria é alguma forma de, mesmo com os sockets sendo fechados no lado do cliente e com as exceções no servidor,
manter a memória alocada em um nível aceitável, sem o problema de estourar o Heap Space pois é isso que tá matando o
módulo servidor.

O seguinte erro acontece várias vezes em sequência no servidor:

INFO | jvm 2 | 2009/03/20 09:34:08 | 1518891 [ComunicacaoThread] ERROR atento.timesheet.net.ComunicacaoThread - [ComunicacaoThread]:
INFO | jvm 2 | 2009/03/20 09:34:08 | java.net.SocketException: Connection reset
INFO | jvm 2 | 2009/03/20 09:34:08 | at java.net.SocketInputStream.read(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:34:08 | at java.io.ObjectInputStream$PeekInputStream.read(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:34:08 | at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:34:08 | at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:34:08 | at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:34:08 | at java.io.ObjectInputStream.(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:34:08 | at atento.timesheet.net.ComunicacaoThread.run(ComunicacaoThread.java:118)
INFO | jvm 2 | 2009/03/20 09:34:08 | 1518891 [ComunicacaoThread] ERROR atento.timesheet.net.ComunicacaoThread -

Até que chega uma hora que dá o seguinte erro e o servidor fica indisponível para novas conexões:

INFO | jvm 2 | 2009/03/20 09:34:59 | Exception in thread “AcceptingThread” java.lang.OutOfMemoryError: Java heap space
INFO | jvm 2 | 2009/03/20 09:35:00 | at java.nio.HeapByteBuffer.(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:35:00 | at java.nio.ByteBuffer.allocate(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:35:00 | at sun.nio.cs.StreamEncoder.(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:35:00 | at sun.nio.cs.StreamEncoder.(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:35:00 | at sun.nio.cs.StreamEncoder.forOutputStreamWriter(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:35:00 | at java.io.OutputStreamWriter.(Unknown Source)
INFO | jvm 2 | 2009/03/20 09:35:01 | at org.apache.log4j.WriterAppender.createWriter(WriterAppender.java:243)
INFO | jvm 2 | 2009/03/20 09:35:01 | at org.apache.log4j.FileAppender.setFile(FileAppender.java:309)
INFO | jvm 2 | 2009/03/20 09:35:01 | at org.apache.log4j.FileAppender.(FileAppender.java:109)
INFO | jvm 2 | 2009/03/20 09:35:01 | at org.apache.log4j.FileAppender.(FileAppender.java:120)
INFO | jvm 2 | 2009/03/20 09:35:01 | at atento.timesheet.net.ComunicacaoThread.(ComunicacaoThread.java:83)
INFO | jvm 2 | 2009/03/20 09:35:02 | at atento.timesheet.view.TimesheetServerFrame$AcceptingThread.run(TimesheetServerFrame.java:1688)

[quote=fantomas]1) Se a classe Server não for muito grande envie ela toda.
2) O stack trace está incompleto, está difícil de saber quais pontos do seu código foram processados antes do erro.
flws[/quote]
Segue a classe ComunicacaoThread.

[code]/*

  • ComunicacaoThread.java
  • Created on 25 de Maio de 2007, 17:47
  • To change this template, choose Tools | Template Manager
  • and open the template in the editor.
    */

package atento.timesheet.net;

import atento.timesheet.dao.FuncionarioDAO;
import atento.timesheet.dao.FunhorDAO;
import atento.timesheet.dao.FunhorExcecaoDAO;
import atento.timesheet.dao.MarcacaoDAO;
import atento.timesheet.dao.MensagemDAO;
import atento.timesheet.entity.Funcionario;
import atento.timesheet.entity.Funhor;
import atento.timesheet.entity.FunhorExcecao;
import atento.timesheet.entity.Marcacao;
import atento.timesheet.txt.ExportaDadosTXT;
import atento.timesheet.util.Util;
import atento.timesheet.view.TimesheetServerFrame;
import atento.timesheet.xml.ExportaDadosXML;
import atento.timesheet.xml.ImportaDadosXML;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.log4j.Appender;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

/**
*

  • @author fmyamauti
    */
    public class ComunicacaoThread extends Thread {

    private Socket socket;
    private ObjectInputStream input;
    private ObjectOutputStream output;
    private Date datReferencia;
    private Pacote pacote;
    private Marcacao marcacao;
    private Funcionario funcionario;
    private Funhor funhor;
    private FunhorExcecao funhorExcecao;
    private MarcacaoDAO marcacaoDAO;
    private MensagemDAO mensagemDAO;
    private FuncionarioDAO funcionarioDAO;
    private FunhorDAO funhorDAO;
    private FunhorExcecaoDAO funhorExcecaoDAO;
    private String pathArquivoEquipe;
    private String pathMarcacoesXML;
    private String pathMarcacoesTXT;
    private String timeZone;

    static Logger logger = Logger.getLogger(ComunicacaoThread.class);
    private Appender fileAppender;

    private TimesheetServerFrame timesheetServerFrame;

    /** Creates a new instance of ComunicacaoThread */
    public ComunicacaoThread(Socket socket) {
    super(“ComunicacaoThread”);
    this.socket = socket;
    pacote = new Pacote();
    marcacaoDAO = new MarcacaoDAO();
    funcionarioDAO = new FuncionarioDAO();
    mensagemDAO = new MensagemDAO();
    funhorDAO = new FunhorDAO();
    funhorExcecaoDAO = new FunhorExcecaoDAO();
    BasicConfigurator.configure();
    try {
    fileAppender = new FileAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN), “debug.log”);
    logger.addAppender(fileAppender);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    public void setPathArquivoEquipe(String pathArquivoEquipe) {
    this.pathArquivoEquipe = pathArquivoEquipe;
    }

    public void setPathMarcacoesXML(String pathMarcacoesXML) {
    this.pathMarcacoesXML = pathMarcacoesXML;
    }

    public void setPathMarcacoesTXT(String pathMarcacoesTXT) {
    this.pathMarcacoesTXT = pathMarcacoesTXT;
    }

    public void setTimeZone(String timeZone) {
    this.timeZone = timeZone;
    }

    public int getCodOperacao() {
    return pacote.getCodOperacao();
    }

    public void setTimesheetServerFrame(TimesheetServerFrame timesheetServerFrame) {
    this.timesheetServerFrame = timesheetServerFrame;
    }

    @Override
    public void run() {
    try {
    // Obtém pacote enviado pelo cliente.
    input = new ObjectInputStream(socket.getInputStream());
    pacote = (Pacote)input.readObject();
    // Processa o pacote.
    pacote = processarPacote(pacote);
    // Envia resposta ao cliente.
    output = new ObjectOutputStream(socket.getOutputStream());
    output.writeObject(pacote);
    output.flush();
    output.reset();
    } catch (ClassNotFoundException ex) {
    desconectar();
    ex.printStackTrace(System.out);
    logger.error("[ComunicacaoThread]: “, ex);
    } catch (IOException ex) {
    desconectar();
    ex.printStackTrace(System.out);
    logger.error(”[ComunicacaoThread]: ", ex);
    }
    }

    private void desconectar() {
    try {
    if ( input != null ) {
    input.close();
    }

         if ( output != null ) {               
             output.close();               
         }
         
         if ( socket != null && socket.isConnected() ) {
             socket.close();              
         }
         
         anular();                
     } catch (IOException ex) {
         anular();
         ex.printStackTrace(System.out);
         logger.error("[ComunicacaoThread]: ", ex);
     }
    

    }

    private void anular() {
    input = null;
    output = null;
    socket = null;
    System.gc();
    }

    // Faz o processamento do pacote de solicitação enviado pelo cliente.
    private Pacote processarPacote(Pacote pacote) {
    switch (pacote.getCodOperacao()) {
    // Início da aplicação cliente
    case Pacote.OP_INICIO: {
    try {
    // Tenta obter o funcionário através de seu RE
    funcionario = funcionarioDAO.getByRe(pacote.getFuncionario().getCodReRh());
    } catch (Exception e) {
    pacote.setStatus(Pacote.NACK);
    pacote.setMensagem(e.getMessage());
    logger.error("[ComunicacaoThread]: ", e);
    }

             // Se encontrou o funcionário
             if ( funcionario != null ) {
                 datReferencia = pacote.getDatReferencia();                    
                 try {                                                
                     // Obtém as marcações do funcionário e seus dados como nome, escala, jornada, site, contrato, etc.
                     pacote.getListMarcacao().clear();
                     pacote.setListMarcacao(marcacaoDAO.getByCodReDatReferencia(funcionario.getCodReRh(), datReferencia));
                     pacote.setFuncionario(funcionario);
                     pacote.setTemMarcacoesHoje(marcacaoDAO.temMarcacoesHoje(funcionario.getCodReRh()));
                     
                     funhorExcecao = funhorExcecaoDAO.getByCodReRhDatExcecao(pacote.getFuncionario().getCodReRh(), datReferencia);                       
                     funhor = funhorDAO.getByCodReRhDatReferencia(pacote.getFuncionario().getCodReRh(), datReferencia);                       
                     
                     if (funhorExcecao == null) {                                                
                         pacote.getFuncionario().setFunhor(funhor);
                     } else {
                         funhorExcecao.setTempoPausa(funhor.getTempoPausa());
                         funhorExcecao.setTempoRefeicao(funhor.getTempoRefeicao());
                         pacote.getFuncionario().setFunhorExcecao(funhorExcecao);
                     }
                     
                     pacote.setStatus(Pacote.ACK);
                     pacote.setMensagem(mensagemDAO.getMensagem().getDesMensagem());
                 } catch (Exception e) {
                     pacote.setStatus(Pacote.NACK);
                     pacote.setMensagem(e.getMessage());
                     logger.error("[ComunicacaoThread]: ", e);
                 }
             } else {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem("Funcionário não encontrado. Caso pertença à esta supervisão, solicite a atualização da equipe.");
             }
             
             // Aproveito para enviar a data e hora atuais.
             pacote.setDatHorAtual(Calendar.getInstance(TimeZone.getTimeZone(timeZone), new Locale("pt", "BR")));
             pacote.setTimezone(timeZone);
             break;
         }
         
         // Solicitação de lista de marcações para determinada data referência.
         case Pacote.OP_OBTER_MARCACOES: {
             datReferencia = pacote.getDatReferencia();
             try {
                 // Obtém as marcações através do RE e data de referência
                 pacote.getListMarcacao().clear();
                 pacote.setListMarcacao(marcacaoDAO.getByCodReDatReferencia(pacote.getFuncionario().getCodReRh(), datReferencia));
                 pacote.setStatus(Pacote.ACK);
                 pacote.setMensagem("");
             } catch (Exception e) {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem(e.getMessage());
                 logger.error("[ComunicacaoThread]: ", e);
             }
             
             // Aproveito para enviar a data e hora atuais.
             pacote.setDatHorAtual(Calendar.getInstance(TimeZone.getTimeZone(timeZone), new Locale("pt", "BR")));
             pacote.setTimezone(timeZone);                
             break;
         }
         
         // Solicitação de marcação de ponto recebida.
         case Pacote.OP_MARCACAO: {
             
             try {
                 // Tento obter o funcionário que solicitou a marcação através do RE e senha informados.
                 funcionario = funcionarioDAO.getByReSenha(pacote.getFuncionario().getCodReRh(), pacote.getFuncionario().getDesSenha());
             } catch (Exception e) {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem(e.getMessage());
                 e.printStackTrace(System.out);
                 logger.error("[ComunicacaoThread]: ", e);
             }
             
             // Se encontrou o funcionário
             if ( funcionario != null ) {                
                 Iterator it = pacote.getListMarcacao().iterator();
                 marcacao = (Marcacao)it.next();
                 Marcacao marcacaoAux = null;
                 
                 try {                        
                     marcacaoAux = marcacaoDAO.getByReDataTipo(marcacao);
                 } catch (Exception ex) {
                     ex.printStackTrace();
                     logger.error("[ComunicacaoThread]: ", ex);
                 }
                 
                 pacote.getListMarcacao().clear();
                 
                 if (marcacaoAux!=null) {
                      pacote.setStatus(Pacote.NACK);
                      pacote.setMensagem("Marcação não efetuada: já existe marcação para este evento");
                      pacote.getListMarcacao().add(marcacaoAux);
                      pacote.setCodErro(Pacote.ERR_MARCACAO_JA_EXISTE);
                 } else {                    
                     try {
                         // Atribui a hora atual, do servidor, à marcação do funcionário.
                         marcacao.setHorMarcacao(Util.calHourMinToMin(Calendar.getInstance(TimeZone.getTimeZone(timeZone), new Locale("pt", "BR"))));
                         // Salva a marcação no BD.
                         marcacaoDAO.salvar(marcacao);                            
                         // Adiciona a marcação recém feita ao pacote pois será enviada de volta ao cliente.                            
                         pacote.getListMarcacao().add(marcacao);
                         pacote.setStatus(Pacote.ACK);
                         pacote.setMensagem("Marcação efetuada com sucesso");
                     } catch (Exception e) {
                         pacote.setStatus(Pacote.NACK);
                         pacote.setMensagem("Erro! Marcação não efetuada: " + e.getMessage());
                         e.printStackTrace(System.out);
                         logger.error("[ComunicacaoThread]: ", e);
                     }                    
                 }
             } else {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem("RE e/ou senha inválidos");
             }
             
             // Aproveito para enviar a data e hora atuais.
             pacote.setDatHorAtual(Calendar.getInstance(TimeZone.getTimeZone(timeZone), new Locale("pt", "BR")));
             pacote.setTimezone(timeZone);
             break;
         }
         
         // Solicitação de data e hora atuais recebida
         case Pacote.OP_HORA_ATUAL: {
             // Data e hora atual do servidor
             pacote.setDatHorAtual(Calendar.getInstance(TimeZone.getTimeZone(timeZone), new Locale("pt", "BR")));                
             pacote.setTimezone(timeZone);
             pacote.setStatus(Pacote.ACK);
             break;
         }
         
         // Solicitação de alteração de senha recebida
         case Pacote.OP_ALTERAR_SENHA: {
             try {
                 // Tento obter o funcionário através do RE e senha atual enviados.
                 funcionario = funcionarioDAO.getByReSenha(pacote.getFuncionario().getCodReRh(), pacote.getFuncionario().getDesSenha());
             } catch (Exception e) {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem(e.getMessage());
                 logger.error("[ComunicacaoThread]: ", e);
             }                
    
             // Se encontrou o funcionário
             if ( funcionario != null ) {
                 // Atualiza a senha
                 funcionario.setDesSenha(pacote.getNovaSenha());
                 try {
                     // Salva no BD
                     funcionarioDAO.salvar(funcionario);
                     pacote.setStatus(Pacote.ACK);
                     pacote.setMensagem("Senha alterada com sucesso!");
                 } catch (Exception ex) {
                     pacote.setStatus(Pacote.NACK);
                     pacote.setMensagem("Erro: Não foi possível alterar a senha");
                     ex.printStackTrace();
                     logger.error("[ComunicacaoThread]: ", ex);
                 }
             } else {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem("Senha atual incorreta");
             }
             break;
         }
         
         case Pacote.OP_OBTER_EQUIPE: {
             String pathArquivo = pathArquivoEquipe + "\\" + pacote.getNomeArquivo();
             
             if (Util.arquivoExiste(pathArquivo)) {
                 ImportaDadosXML importaDadosXML = new ImportaDadosXML(pathArquivo);
                 importaDadosXML.setTimeZone(timeZone);
                 // Executa método run() de ImportaDadosXML.
                 importaDadosXML.start();
                 
                 try {
                     // Faz a Thread principal aguardar até a Thread importaDadosXML finalizar.
                     importaDadosXML.join();
                 } catch (InterruptedException ex) {
                     ex.printStackTrace();
                     logger.error("[ComunicacaoThread]: ", ex);
                 }
                 
                 if (importaDadosXML.sucesso()) {
                     pacote.setStatus(Pacote.ACK);
                     pacote.setMensagem("O arquivo foi processado com sucesso!");                        
                     timesheetServerFrame.atualizaTimezone();
                 } else {
                     pacote.setStatus(Pacote.NACK);
                     pacote.setMensagem(importaDadosXML.getMensagem());
                 }
             } else {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem("Não foi possível encontrar o arquivo especificado na pasta do servidor");
             }
             break;
         }
         
         case Pacote.OP_EXPORTAR_MARCACOES_XML: {
             String pathArquivo = pathMarcacoesXML + "\\" + pacote.getNomeArquivo();
             java.sql.Date dataDe = pacote.getDataDe();
             java.sql.Date dataAte = pacote.getDataAte();
             
             ExportaDadosXML exportaDadosXML = new ExportaDadosXML(pathArquivo, dataDe, dataAte);
             exportaDadosXML.setTimeZone(timeZone);
             // Executa método run() de ExportaDadosXML.
             exportaDadosXML.start();
             
             try {
                 // Faz a Thread principal aguardar até a Thread exportaDadosXML finalizar.
                 exportaDadosXML.join();
             } catch (InterruptedException ex) {
                 ex.printStackTrace();
                 logger.error("[ComunicacaoThread]: ", ex);
             }   
             
             if (exportaDadosXML.sucesso()) {
                 pacote.setStatus(Pacote.ACK);
                 pacote.setMensagem("O arquivo foi gerado com sucesso!");
             } else {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem(exportaDadosXML.getMensagem());
             }                
             break;
         }
         
         case Pacote.OP_EXPORTAR_MARCACOES_TXT: {
             String pathArquivo = pathMarcacoesTXT + "\\" + pacote.getNomeArquivo();
             java.sql.Date dataDe = pacote.getDataDe();
             java.sql.Date dataAte = pacote.getDataAte();
             
             ExportaDadosTXT exportaDadosTXT = new ExportaDadosTXT(pathArquivo, 0, dataDe, dataAte);
             exportaDadosTXT.setTimeZone(timeZone);
             // Executa método run() de ExportaDadosTXT.
             exportaDadosTXT.start();
             
             try {
                 // Faz a Thread principal aguardar até a Thread exportaDadosTXT finalizar.
                 exportaDadosTXT.join();
             } catch (InterruptedException ex) {
                 ex.printStackTrace();
                 logger.error("[ComunicacaoThread]: ", ex);
             }
             
             if (exportaDadosTXT.sucesso()) {
                 pacote.setStatus(Pacote.ACK);
                 pacote.setMensagem("O arquivo foi gerado com sucesso!");
             } else {
                 pacote.setStatus(Pacote.NACK);
                 pacote.setMensagem(exportaDadosTXT.getMensagem());
             }                
             break;
         }            
     }
     return pacote;
    

    }
    }
    [/code]

Sem querer ser chato e já sendo…tem como vc enviar também a classe que instancia / manipula
os objetos da classe ComunicacaoThread (esta que vc enviou antes)?

flws

[quote=fantomas]Sem querer ser chato e já sendo…tem como vc enviar também a classe que instancia / manipula
os objetos da classe ComunicacaoThread (esta que vc enviou antes)?
flws [/quote]
A classe é meio grande. Vou colocar o código que acho relevante. Se precisar de mais me avise.

Método que inicia a Thread que fica aguardando conexões do cliente.

public void iniciar() { if ( "".equals(jFormattedTextFieldPorta.getText().trim()) ) { JOptionPane.showMessageDialog(this, "Informe o número da porta", "Atenção", JOptionPane.WARNING_MESSAGE); } else { setMensagem(null, ""); acceptingThread = new AcceptingThread(this); acceptingThread.start(); } }
Classe para aguardar as conexões:

[code] // Thread onde inicia-se o servidor que fica aguardando conexões sockets dos clientes.
private class AcceptingThread extends Thread {

    private int porta;
    private ComunicacaoThread comunicacaoThread;
    private TimesheetServerFrame timesheetServerFrame;
    
    public AcceptingThread(TimesheetServerFrame timesheetServerFrame) {
        super("AcceptingThread");
        this.timesheetServerFrame = timesheetServerFrame;
    }
    
    public void run() {
        try {
            jButtonIniciar.setEnabled(false);
            jButtonParar.setEnabled(true);
            porta = Integer.parseInt(jFormattedTextFieldPorta.getText());
            serverSocket = new ServerSocket(porta);
            
            // Loop infinito pois "n" clientes podem se conectar simultaneamente.
            while(true) {
                // serverSocket.accept() é bloqueante. A execução da aplicação "trava" nesta
                // linha e fica aguardando um cliente conectar-se. Quando o cliente conecta-se,
                // comunicacaoThread é instanciado, seu método run() é executado e o fluxo da
                // aplicação prossegue.
                comunicacaoThread = new ComunicacaoThread(serverSocket.accept());
                comunicacaoThread.setPathArquivoEquipe(pathArquivoEquipe);
                comunicacaoThread.setPathMarcacoesXML(pathMarcacoesXML);
                comunicacaoThread.setPathMarcacoesTXT(pathMarcacoesTXT);
                comunicacaoThread.setTimeZone(timeZone);
                comunicacaoThread.setTimesheetServerFrame(timesheetServerFrame);
                comunicacaoThread.start();                    
                comunicacaoThread = null;
                System.gc();
            }
        } catch (IOException e) {
            setMensagem(Color.BLUE, "Comunicação finalizada");
            e.printStackTrace();
        } finally {
            try {
                if ( serverSocket!=null && !serverSocket.isClosed() ) {
                    serverSocket.close();
                }
            } catch(IOException e) {
                e.printStackTrace(System.out);
            }
        }
    }
}[/code]

Até agora o que percebi foi o seguinte:

No método run tem o loop (while) que não termina nunca, ok normal até ai.
O problema é que vc tem um botão chamado jButtonParar; ao clicar neste botão o processamento não para realmente. Porque a coisa toda está em uma thread e não tem nada que interrompa o loop.
Se você clicar no botão iniciar você irá criar uma outra thread com outro servidor, o intrigante neste ponto é que se vc clicar no botão iniciar novamente e a porta for a mesma que a anterior daria um erro pelo fato da porta estar ocupada.

O que vc acha disso?

P.S Anotei com a palavra “AQUI” os pontos observados.

[code] // Thread onde inicia-se o servidor que fica aguardando conexões sockets dos clientes.
private class AcceptingThread extends Thread {

    private int porta;
    private ComunicacaoThread comunicacaoThread;
    private TimesheetServerFrame timesheetServerFrame;
    
    public AcceptingThread(TimesheetServerFrame timesheetServerFrame) {
        super("AcceptingThread");
        this.timesheetServerFrame = timesheetServerFrame;
    }
    
    public void run() {
        try {
            jButtonIniciar.setEnabled(false);
            jButtonParar.setEnabled(true);            // <--<< AQUI
            porta = Integer.parseInt(jFormattedTextFieldPorta.getText());
            serverSocket = new ServerSocket(porta);
            
            // Loop infinito pois "n" clientes podem se conectar simultaneamente.
            while(true) {             // <--<< AQUI
                // serverSocket.accept() é bloqueante. A execução da aplicação "trava" nesta
                // linha e fica aguardando um cliente conectar-se. Quando o cliente conecta-se,
                // comunicacaoThread é instanciado, seu método run() é executado e o fluxo da
                // aplicação prossegue.
                comunicacaoThread = new ComunicacaoThread(serverSocket.accept());
                comunicacaoThread.setPathArquivoEquipe(pathArquivoEquipe);
                comunicacaoThread.setPathMarcacoesXML(pathMarcacoesXML);
                comunicacaoThread.setPathMarcacoesTXT(pathMarcacoesTXT);
                comunicacaoThread.setTimeZone(timeZone);
                comunicacaoThread.setTimesheetServerFrame(timesheetServerFrame);
                comunicacaoThread.start();                    
                comunicacaoThread = null;
                System.gc();
            }
        } catch (IOException e) {
            setMensagem(Color.BLUE, "Comunicação finalizada");
            e.printStackTrace();
        } finally {
            try {
                if ( serverSocket!=null && !serverSocket.isClosed() ) {
                    serverSocket.close();
                }
            } catch(IOException e) {
                e.printStackTrace(System.out);
            }
        }
    }
}

[/code]

flws

O aplicativo realmente tem um botão iniciar e um botão parar mas, em produção, ele fica iniciado como um
serviço do Windows.
O método iniciar é executado uma vez quando inicia-se o serviço. Não há o risco de clicar duas vezes neste botão,
mesmo na interface gráfica pois eu o bloqueio quando clica a 1ª vez.

Abaixo a Thread que fecha o serverSocket.

[code]// Thread onde finaliza-se o servidor socket.
private class ClosingThread extends Thread {

    public ClosingThread() {
        super("ClosingThread");
    }
    
    public void run() {
        try {
            jButtonIniciar.setEnabled(true);
            jButtonParar.setEnabled(false);
            if (!acceptingThread.interrupted() && !serverSocket.isClosed()) {
                serverSocket.close();
                acceptingThread.interrupt();
            }
        } catch (SecurityException e) {
            setMensagem(Color.RED, "Erro ao fechar a conexão: " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            setMensagem(Color.RED, "Erro ao fechar a conexão: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            setMensagem(Color.RED, "Erro inesperado: " + e.getMessage());
            e.printStackTrace();
        }
    }
}[/code]

Humm…entendi.

Sobre o problema de memória eu entendi que vc está utilizando o bdo Derby e o erro partiu de dentro dele.

Tem como vc inicia-lo com algum parametro do tipo -Xmx512m -Xms256m para ver se “alivia” o lado dele?

flws

Aliás o problema ocorre após quantos clientes conectados, vc sabe?

flws

Outras coisas:

Faça este teste para nós.

a) Anule TODAS as execuções do método reset().

b) Executar close() nos objetos apenas com a condição de ser diferente de nulo, ou seja, ficaria assim:

if( output != null ) { output.close(); }

flws