atualizar javadoc dos das classes SocketTCP, ConnectionManager, ConnectionFactory e TcpConnection

This commit is contained in:
GustavoHMDS 2026-09-09 00:14:15 -03:00
parent 93ac292f72
commit b5bbd1df2d
4 changed files with 447 additions and 44 deletions

View File

@ -11,10 +11,44 @@ import java.net.SocketTimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Fábrica responsável pela criação e configuração de conexões TCP.
*
* <p>Esta classe centraliza o estabelecimento de conexões de saída e a
* configuração de conexões recebidas. Antes de uma conexão ser considerada
* válida, é realizado um handshake entre os peers para identificar o remetente,
* o destinatário e se a conexão é privada.</p>
*
* <p>Durante o handshake é utilizado um timeout menor, definido por
* {@link #HANDSHAKE_TIMEOUT}. Após a validação bem-sucedida, a conexão passa
* a utilizar o timeout padrão definido por {@link #STANDARD_TIMEOUT}.</p>
*/
public class ConnectionFactory {
private static final int HANDSHAKE_TIMEOUT = 5000;
private static final int STANDARD_TIMEOUT = 300_000;
/**
* Cria uma conexão TCP de saída com o peer de destino e realiza o handshake
* necessário para validá-la.
*
* <p>A conexão é configurada com os identificadores do remetente e do
* destinatário e com o tipo indicado pelo parâmetro {@code isPrivate}.
* Após o estabelecimento do socket, o método envia um
* {@link HandshakeMessage} e aguarda uma resposta válida.</p>
*
* <p>Se o handshake for validado com sucesso, o timeout da conexão é
* alterado de {@link #HANDSHAKE_TIMEOUT} para {@link #STANDARD_TIMEOUT}
* e a conexão é retornada. Caso contrário, {@code null} é retornado.</p>
*
* @param address endereço IP do peer de destino.
* @param port porta TCP na qual o peer de destino está aguardando conexões.
* @param sender identificador do remetente da conexão.
* @param receiver identificador do destinatário da conexão.
* @param isPrivate {@code true} se a conexão deve ser privada;
* {@code false} se deve ser pública.
* @return a conexão TCP estabelecida e validada ou {@code null} caso
* não seja possível estabelecer ou validar a conexão.
*/
public static TcpConnection createConnection(InetAddress address, int port, String sender, String receiver, boolean isPrivate) {
try {
TcpConnection connection;
@ -42,6 +76,22 @@ public class ConnectionFactory {
return null;
}
/**
* Configura uma conexão TCP recebida e valida o handshake enviado pelo
* peer remoto.
*
* <p>O handshake recebido é utilizado para obter e configurar os
* identificadores do remetente e do destinatário, bem como o tipo da
* conexão. Após a validação bem-sucedida, o timeout da conexão é alterado
* para {@link #STANDARD_TIMEOUT}.</p>
*
* <p>O {@link Socket} recebido deve representar uma conexão TCP aceita
* pelo servidor.</p>
*
* @param socket socket TCP correspondente à conexão recebida.
* @return a conexão TCP configurada e validada ou {@code null} caso
* o handshake seja inválido ou não possa ser processado.
*/
public static TcpConnection receiveConnection(Socket socket) {
try {
TcpConnection connection;
@ -66,6 +116,27 @@ public class ConnectionFactory {
return null;
}
/**
* Aguarda e valida a mensagem de handshake de uma conexão TCP.
*
* <p>Durante a validação, a conexão utiliza {@link #HANDSHAKE_TIMEOUT}
* como limite para o recebimento do handshake. A mensagem recebida deve
* ser uma instância de {@link Message} e, especificamente, de
* {@link HandshakeMessage}. Mensagens de sistema ou mensagens de outros
* tipos são rejeitadas.</p>
*
* <p>Quando o handshake é recebido, seus campos de remetente e destinatário
* são validados por {@link #validateHandshake(HandshakeMessage, TcpConnection)}.
* Caso a validação seja bem-sucedida, o próprio handshake é retornado.</p>
*
* <p>Em caso de timeout, mensagem inválida ou erro durante o processamento,
* uma mensagem de sistema apropriada pode ser enviada ao peer e
* {@code null} é retornado.</p>
*
* @param connection conexão TCP da qual o handshake será recebido.
* @return o {@link HandshakeMessage} validado ou {@code null} caso o
* handshake não seja recebido ou seja inválido.
*/
private static HandshakeMessage validateConnection(TcpConnection connection) {
try {
connection.setTimeout(HANDSHAKE_TIMEOUT);
@ -113,6 +184,20 @@ public class ConnectionFactory {
return null;
}
/**
* Valida os identificadores presentes em uma mensagem de handshake.
*
* <p>O remetente e o destinatário devem estar presentes e não podem ser
* vazios ou conter apenas espaços em branco. Quando um dos identificadores
* é inválido, uma mensagem de rejeição é enviada ao peer e a validação
* falha.</p>
*
* @param handshake mensagem de handshake a ser validada.
* @param connection conexão associada ao handshake, utilizada para enviar
* uma mensagem de rejeição quando necessário.
* @return {@code true} se os identificadores do remetente e do destinatário
* forem válidos; {@code false} caso contrário.
*/
private static boolean validateHandshake(HandshakeMessage handshake, TcpConnection connection) {
String sender = handshake.getSender();
String receiver = handshake.getReceiver();

View File

@ -14,11 +14,27 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class ConnectionManager implements PeerListener {
/**
* Gerencia as conexões TCP estabelecidas com os peers da aplicação.
*
* <p>As conexões públicas são armazenadas individualmente por endereço IP,
* enquanto as conexões privadas são agrupadas por endereço, permitindo que
* um mesmo peer mantenha múltiplas conexões privadas simultaneamente.</p>
*
* <p>Além de manter o registro das conexões ativas, esta classe é responsável
* por encerrá-las quando necessário e por remover do gerenciamento as
* conexões que forem fechadas. Também acompanha as alterações na lista de
* peers ativos para encerrar conexões associadas a peers que deixaram de
* estar disponíveis.</p>
*/
class ConnectionManager implements PeerListener {
private final Logger logger;
private final Map<InetAddress, TcpConnection> publicConnections;
private final Map<InetAddress, List<TcpConnection>> privateConnections;
/**
* Cria um gerenciador de conexões TCP vazio.
*/
public ConnectionManager() {
publicConnections = new ConcurrentHashMap<>();
privateConnections = new ConcurrentHashMap<>();
@ -26,11 +42,28 @@ public class ConnectionManager implements PeerListener {
logger = Logger.getLogger(getClass().getName());
}
/**
* Obtém a conexão pública registrada para um determinado peer.
*
* @param address endereço IP do peer.
* @return a conexão pública registrada para o endereço ou {@code null}
* caso não exista uma conexão pública registrada.
*/
public TcpConnection getPublicConnection(InetAddress address) {
return publicConnections.get(address);
}
public void addPrivateConnection(InetAddress address, TcpConnection connection) {
/**
* Registra uma conexão privada associada a um peer.
*
* <p>Um mesmo peer pode possuir múltiplas conexões privadas. Por isso,
* as conexões são armazenadas em uma lista associada ao endereço do
* peer.</p>
*
* @param connection conexão privada a ser registrada.
*/
public void addPrivateConnection(TcpConnection connection) {
List<TcpConnection> connections =
privateConnections.computeIfAbsent(connection.getAddress(), _ -> new ArrayList<>());
@ -38,17 +71,54 @@ public class ConnectionManager implements PeerListener {
connections.add(connection);
}
}
public void addPublicConnection(InetAddress address, TcpConnection connection) {
/**
* Registra uma conexão pública associada a um peer.
*
* <p>Cada peer pode possuir apenas uma conexão pública registrada.
* Quando existe uma conexão pública para o endereço da nova conexão,
* a conexão anterior é encerrada e substituída pela nova.</p>
*
* @param connection nova conexão pública a ser registrada.
*/
public void addPublicConnection(TcpConnection connection) {
TcpConnection oldConnection = publicConnections.put(connection.getAddress(), connection);
if (oldConnection != null && oldConnection != connection) {
closeConnection(oldConnection);
}
}
/**
* Envia uma mensagem de encerramento ao peer e fecha a conexão.
*
* <p>A mensagem enviada informa o motivo do encerramento antes que a
* conexão seja removida do gerenciamento e fechada.</p>
*
* @param connection conexão TCP a ser encerrada.
* @param reason motivo do encerramento que será informado ao peer.
*/
public void closeConnection(TcpConnection connection, String reason) {
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason);
closeConnection(connection);
}
/**
* Remove uma conexão do gerenciamento e encerra seu socket.
*
* <p>Para conexões privadas, a conexão é removida da lista associada ao
* endereço do peer. Caso essa seja a última conexão privada do endereço,
* a lista também é removida do mapa.</p>
*
* <p>Para conexões públicas, a conexão é removida do mapa somente se
* ainda for a conexão registrada para seu endereço.</p>
*
* <p>Se a conexão estiver fechada ou for {@code null}, nenhuma ação
* adicional de fechamento é realizada.</p>
*
* @param connection conexão TCP a ser removida e encerrada.
*/
public void closeConnection(TcpConnection connection) {
if (connection == null) return;
if (connection.isPrivate()) {
@ -73,6 +143,13 @@ public class ConnectionManager implements PeerListener {
}
}
/**
* Envia uma mensagem de encerramento para todas as conexões de um peer
* e, em seguida, fecha essas conexões.
*
* @param address endereço IP do peer cujas conexões serão encerradas.
* @param reason motivo do encerramento informado ao peer.
*/
public void closeByAdress(InetAddress address, String reason) {
List<TcpConnection> connections = privateConnections.get(address);
for (TcpConnection connection : connections) {
@ -81,6 +158,14 @@ public class ConnectionManager implements PeerListener {
closeByAdress(address);
}
/**
* Fecha e remove todas as conexões associadas a um determinado endereço.
*
* <p>Tanto a conexão pública quanto todas as conexões privadas associadas
* ao endereço são encerradas e removidas do gerenciamento.</p>
*
* @param address endereço IP do peer cujas conexões serão encerradas.
*/
public void closeByAdress(InetAddress address) {
TcpConnection publicConnection = publicConnections.remove(address);
@ -111,6 +196,12 @@ public class ConnectionManager implements PeerListener {
}
}
/**
* Envia uma mensagem de encerramento para todas as conexões privadas
* gerenciadas e, em seguida, fecha todas elas.
*
* @param reason motivo do encerramento informado aos peers.
*/
public void closeAllConnections(String reason) {
for (List<TcpConnection> connections : privateConnections.values()) {
for (TcpConnection connection : connections) {
@ -119,6 +210,14 @@ public class ConnectionManager implements PeerListener {
}
closeAllConnections();
}
/**
* Fecha e remove todas as conexões públicas e privadas gerenciadas.
*
* <p>Após a execução, não devem permanecer conexões registradas nos
* mapas de conexões deste gerenciador.</p>
*/
public void closeAllConnections() {
for (TcpConnection connection : publicConnections.values()) {
closeConnection(connection);
@ -137,11 +236,14 @@ public class ConnectionManager implements PeerListener {
/**
* Atualiza as conexões de acordo com os peers atualmente ativos.
*
* <p>Conexões associadas a peers que não estão mais ativos são encerradas
* e removidas.</p>
* <p>Os endereços presentes na lista de peers são utilizados como
* referência para determinar quais conexões ainda devem permanecer
* ativas. Conexões públicas e privadas associadas a endereços que não
* estão mais presentes na lista de peers são encerradas e removidas.</p>
*
* @param peers lista atualizada de peers ativos.
*/
@Override
public void onPeersChanged(List<PeerInfo> peers) {
Set<InetAddress> activePeers = peers.stream().map(PeerInfo::getAddress).collect(Collectors.toSet());

View File

@ -47,11 +47,12 @@ public class SocketTCP extends Thread {
/**
* Cria o servidor TCP da aplicação.
*
* <p>O servidor utiliza a porta definida pela aplicação e inicia sem
* estabelecer conexões com os peers. As conexões são criadas conforme
* necessário durante o envio ou recebidas de outros peers.</p>
* <p>O servidor é criado na porta definida pela aplicação. As conexões
* com os peers são estabelecidas sob demanda durante o envio ou aceitas
* quando iniciadas por outros peers.</p>
*
* @param main janela principal da aplicação.
* @param main janela principal da aplicação, usada para obter a porta,
* o identificador local e os plugins disponíveis.
*/
public SocketTCP(MainWindow main) {
this.main = main;
@ -66,6 +67,17 @@ public class SocketTCP extends Thread {
}
}
/**
* Envia uma mensagem por meio de uma conexão TCP pública com o peer de destino.
*
* <p>A conexão pública existente para o endereço informado é reutilizada.
* Caso não exista uma conexão válida, uma nova conexão é estabelecida antes
* do envio. Falhas durante o estabelecimento ou envio são registradas na
* saída padrão e não são propagadas ao chamador.</p>
*
* @param msg mensagem serializada em bytes que será enviada.
* @param destinationAddress endereço IP do peer que receberá a mensagem.
*/
public void send(byte[] msg, InetAddress destinationAddress) {
try {
TcpConnection connection = getPublicConnection(destinationAddress, main.getIdentifier(), main.getIdentifier());
@ -80,10 +92,44 @@ public class SocketTCP extends Thread {
}
}
/**
* Estabelece uma conexão TCP privada com um plugin específico do peer de destino.
*
* <p>A conexão é criada como privada e passa a ser gerenciada pelo
* {@link ConnectionManager}. O método também inicia a tarefa responsável
* por receber os dados dessa conexão.</p>
*
* @param destinationAddress endereço IP do peer de destino.
* @param senderId identificador do remetente da conexão.
* @param receiverId identificador do plugin que receberá os dados.
* @return a conexão privada estabelecida ou {@code null} quando a criação
* da conexão falhar.
* @throws IllegalArgumentException se o endereço de destino for o endereço
* local desta instância.
*/
public TcpConnection createPrivateConnection(InetAddress destinationAddress, String senderId, String receiverId) {
return establishOutgoingConnection(destinationAddress, senderId, receiverId, true);
}
/**
* Obtém uma conexão TCP pública existente com o peer de destino ou estabelece
* uma nova caso não exista uma conexão válida.
*
* <p>As conexões públicas são gerenciadas pelo {@link ConnectionManager}.
* O método primeiro verifica se existe uma conexão pública registrada
* para o endereço de destino e, caso ela exista e não esteja fechada,
* reutiliza-a. Caso contrário, uma nova conexão pública é estabelecida,
* registrada no {@link ConnectionManager} e disponibilizada para uso.</p>
*
* <p>O acesso ao método é sincronizado para evitar que múltiplas threads
* estabeleçam simultaneamente conexões públicas para o mesmo peer.</p>
*
* @param destinationAddress endereço IP do peer de destino.
* @param senderId identificador usado como remetente no estabelecimento da conexão.
* @param receiverId identificador usado como destinatário no estabelecimento da conexão.
* @return uma conexão pública válida ou {@code null} caso não seja possível
* estabelecê-la.
*/
private synchronized TcpConnection getPublicConnection(InetAddress destinationAddress, String senderId, String receiverId) {
TcpConnection connection;
@ -98,9 +144,9 @@ public class SocketTCP extends Thread {
/**
* Aguarda e aceita novas conexões TCP.
*
* <p>Cada conexão aceita é associada ao endereço do peer e registrada para
* que possa ser utilizada tanto para recepção quanto para envio de
* mensagens.</p>
* <p>Cada socket aceito é encaminhado para processamento assíncrono,
* interpretado como uma {@link TcpConnection}, registrado pelo
* {@link ConnectionManager} e associado ao receptor adequado.</p>
*/
private void receive() {
while (running) {
@ -120,6 +166,22 @@ public class SocketTCP extends Thread {
}
}
/**
* Estabelece uma conexão TCP de saída com outro peer.
*
* <p>O método impede conexões com o próprio endereço local, cria a conexão
* por meio da {@link ConnectionFactory}, registra-a no gerenciador de
* conexões e inicia uma tarefa para receber dados continuamente.</p>
*
* @param destinationAddress endereço IP do peer de destino.
* @param sender identificador do remetente.
* @param receiver identificador do destinatário.
* @param isPrivate {@code true} para uma conexão destinada a um plugin
* específico; {@code false} para uma conexão pública.
* @return a conexão estabelecida ou {@code null} se a
* {@link ConnectionFactory} não conseguir criá-la.
* @throws IllegalArgumentException se o destino for o endereço desta máquina.
*/
private TcpConnection establishOutgoingConnection(InetAddress destinationAddress, String sender, String receiver, boolean isPrivate) {
if (address.equals(destinationAddress)) {
System.out.println("Failed to get socket.");
@ -138,6 +200,15 @@ public class SocketTCP extends Thread {
return connection;
}
/**
* Agenda o processamento de uma conexão TCP recebida.
*
* <p>A conexão é interpretada pela {@link ConnectionFactory}, registrada
* no gerenciador e associada a uma tarefa de recepção. O processamento é
* executado de forma assíncrona pelo executor compartilhado.</p>
*
* @param socket socket aceito pelo {@link ServerSocket}.
*/
private void acceptIncomingConnection(Socket socket) {
connectionExecutor.submit(() -> {
TcpConnection connection = ConnectionFactory.receiveConnection(socket);
@ -148,17 +219,37 @@ public class SocketTCP extends Thread {
});
}
/**
* Registra uma conexão no gerenciador de acordo com seu tipo.
*
* <p>Conexões privadas são armazenadas na coleção de conexões privadas;
* as demais são armazenadas na coleção de conexões públicas.</p>
*
* @param connection conexão que será registrada.
*/
private void saveConnection(TcpConnection connection) {
if (connection.isPrivate()) {
connectionManager.addPrivateConnection(connection.getAddress(), connection);
connectionManager.addPrivateConnection(connection);
}
else {
connectionManager.addPublicConnection(connection.getAddress(), connection);
connectionManager.addPublicConnection(connection);
}
}
/**
* Seleciona e inicia o receptor adequado para uma conexão.
*
* <p>Conexões públicas são tratadas pelo receptor de mensagens públicas.
* Conexões privadas destinadas ao próprio sistema são tratadas
* separadamente; as demais são encaminhadas ao plugin identificado como
* destinatário. Quando solicitado, o plugin também é notificado sobre o
* estabelecimento da conexão.</p>
*
* @param connection conexão cujo fluxo de entrada será processado.
* @param notify indica se o plugin destinatário deve ser notificado sobre
* o recebimento de uma nova conexão privada.
*/
private void startReceiver(TcpConnection connection, boolean notify) {
try {
if (!connection.isPrivate()) {
publicConnectionReceiver(connection);
return;
@ -177,11 +268,18 @@ public class SocketTCP extends Thread {
privateConnectionReceiver(connection, plugin);
}
}
catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
/**
* Localiza o plugin responsável por receber uma conexão privada.
*
* <p>A busca é feita pelo identificador do destinatário informado na
* própria conexão. Caso nenhum plugin correspondente seja encontrado,
* uma mensagem de erro é enviada ao peer remoto.</p>
*
* @param connection conexão privada cujo destinatário será localizado.
* @return o plugin com o identificador correspondente ao destinatário,
* ou {@code null} se nenhum plugin for encontrado.
*/
private Plugin findConnectionReceiver(TcpConnection connection) {
Plugin receiver = null;
for(Plugin plugin : main.getPlugins()) {
@ -201,6 +299,19 @@ public class SocketTCP extends Thread {
return receiver;
}
/**
* Processa continuamente as mensagens recebidas em uma conexão pública.
*
* <p>As mensagens são desserializadas e, quando são instâncias de
* {@link Message}, encaminhadas a todos os plugins da aplicação. Mensagens
* de controle do sistema podem encerrar a conexão ou registrar uma
* condição de erro.</p>
*
* <p>O método também trata timeouts e erros de I/O, removendo a conexão
* do {@link ConnectionManager} quando ela deixa de ser utilizável.</p>
*
* @param connection conexão pública da qual as mensagens serão recebidas.
*/
private void publicConnectionReceiver(TcpConnection connection) {
while (running) {
try {
@ -260,6 +371,18 @@ public class SocketTCP extends Thread {
}
}
/**
* Processa os dados recebidos em uma conexão privada e os encaminha
* ao plugin destinatário.
*
* <p>Dados que não representam mensagens de controle são entregues
* diretamente ao {@link PrivateConnectionPlugin}. Mensagens de controle
* relacionadas ao encerramento ou a erros internos provocam a notificação
* do plugin e o fechamento da conexão.</p>
*
* @param connection conexão privada da qual os dados serão recebidos.
* @param plugin plugin responsável pelo processamento dos dados recebidos.
*/
private void privateConnectionReceiver(TcpConnection connection, Plugin plugin) {
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
while (running) {
@ -295,7 +418,16 @@ public class SocketTCP extends Thread {
}
}
private void systemConnectionReceiver(TcpConnection connection) throws IOException {
/**
* Processa mensagens recebidas em uma conexão destinada ao próprio sistema.
*
* <p>Atualmente, mensagens destinadas ao sistema são apenas verificadas
* quanto ao seu formato de controle. Não processamento adicional de
* mensagens de sistema neste método.</p>
*
* @param connection conexão destinada ao identificador da própria aplicação.
*/
private void systemConnectionReceiver(TcpConnection connection) {
while (running) {
try {
byte[] message = connection.receive();
@ -308,12 +440,25 @@ public class SocketTCP extends Thread {
//TODO: Ainda não existem mensagens trocadas entre o sistema.
}
} catch (SocketTimeoutException ex) {
}
catch (SocketTimeoutException ex) {
if (connection.hasTimedOut()) return;
}
catch (IOException ex) {
connectionManager.closeConnection(connection);
}
}
}
/**
* Encerra uma conexão TCP e registra o motivo informado.
*
* <p>Quando nenhuma descrição é fornecida, é utilizado um motivo padrão.
* O fechamento efetivo da conexão é delegado ao {@link ConnectionManager}.</p>
*
* @param connection conexão que será encerrada.
* @param description motivo do encerramento; pode ser {@code null}.
*/
public void disconnect(TcpConnection connection, String description) {
connectionManager.closeConnection(
connection,
@ -321,11 +466,22 @@ public class SocketTCP extends Thread {
);
}
@Override
public void run() {
receive();
}
/**
* Encerra o servidor TCP e libera os recursos associados.
*
* <p>Interrompe o laço de aceitação de conexões, fecha o
* {@link ServerSocket}, encerra todas as conexões gerenciadas e interrompe
* as tarefas do executor.</p>
*
* <p>Após a chamada, esta instância não deve ser reutilizada para aceitar
* novas conexões.</p>
*/
public void close() {
running = false;

View File

@ -8,16 +8,15 @@ import java.net.Socket;
import java.net.SocketException;
/**
* Representa uma conexão TCP persistente com um peer.
* Representa uma conexão TCP ativa com um peer remoto.
*
* <p>Encapsula o socket e os streams utilizados para enviar e receber
* mensagens. Também é responsável pelo framing das mensagens, permitindo
* que várias mensagens sejam transmitidas pela mesma conexão.</p>
* <p>A conexão permite o envio e recebimento de mensagens, além de manter
* informações sobre o endereço remoto e metadados da conexão, como remetente,
* destinatário e se a conexão é privada.</p>
*
* @author Gustavo
*/
public class TcpConnection {
private static final long TIMEOUT = 300_000; // 5 minutos
private final Socket socket;
private final DataInputStream input;
@ -25,19 +24,35 @@ public class TcpConnection {
private final InetAddress address;
private String sender;
private String receiver;
private boolean isPrivate;
private volatile long lastActivity;
private long timeout;
/**
* Cria uma conexão TCP a partir de um socket existente.
*
* @param socket socket utilizado para a comunicação
* @throws IOException se não for possível obter os fluxos de entrada ou saída
* do socket
*/
TcpConnection(Socket socket) throws IOException {
this.socket = socket;
this.input = new DataInputStream(socket.getInputStream());
this.output = new DataOutputStream(socket.getOutputStream());
this.address = socket.getInetAddress();
this.lastActivity = System.currentTimeMillis();
this.timeout = 300_000; // 5 minutos
}
/**
* Envia uma mensagem pela conexão.
*
* <p>A mensagem é enviada precedida pelo seu tamanho, permitindo que o
* receptor determine quantos bytes devem ser lidos.</p>
*
* @param message mensagem a ser enviada
* @throws IOException se ocorrer um erro durante o envio
*/
public synchronized void send(byte[] message) throws IOException {
output.writeInt(message.length);
output.write(message);
@ -45,6 +60,16 @@ public class TcpConnection {
updateLastActivity();
}
/**
* Recebe uma mensagem da conexão.
*
* <p>Primeiro o tamanho da mensagem e, em seguida, aquantidade
* correspondente de bytes.</p>
*
* @return mensagem recebida
* @throws IOException se o tamanho da mensagem for inválido ou se a conexão
* for encerrada antes que a mensagem seja completamente recebida
*/
byte[] receive() throws IOException {
int length = input.readInt();
@ -61,28 +86,63 @@ public class TcpConnection {
return message;
}
/**
* Verifica se o socket da conexão está fechado.
*
* @return {@code true} se o socket estiver fechado;
* {@code false} caso contrário
*/
public boolean isClosed() {
return socket.isClosed();
}
/**
* Fecha o socket da conexão.
*
* @throws IOException se ocorrer um erro ao fechar o socket
*/
void close() throws IOException {
socket.close();
}
/**
* Retorna o endereço do peer remoto conectado.
*
* @return endereço IP do peer remoto
*/
public InetAddress getAddress() {
return address;
}
/**
* Atualiza o instante da última atividade da conexão.
*/
private void updateLastActivity() {
lastActivity = System.currentTimeMillis();
}
/**
* Define o tempo limite de leitura do socket.
*
* @param timeout tempo limite, em milissegundos
* @throws SocketException se ocorrer um erro ao definir o tempo limite
*/
void setTimeout(int timeout) throws SocketException {
socket.setSoTimeout(timeout);
this.timeout = timeout;
}
/**
* Verifica se a conexão ficou inativa pelo tempo limite configurado.
*
* <p>A verificação é baseada no instante da última atividade registrada
* durante o envio ou recebimento de uma mensagem.</p>
*
* @return {@code true} se a conexão estiver inativa pelo menos 5 minutos;
* {@code false} caso contrário
*/
boolean hasTimedOut() {
return System.currentTimeMillis() - lastActivity >= TIMEOUT;
return System.currentTimeMillis() - lastActivity >= timeout;
}
public String getReceiver() {