atualizar javadoc dos das classes SocketTCP, ConnectionManager, ConnectionFactory e TcpConnection
This commit is contained in:
parent
93ac292f72
commit
b5bbd1df2d
|
|
@ -11,10 +11,44 @@ import java.net.SocketTimeoutException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
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 {
|
public class ConnectionFactory {
|
||||||
private static final int HANDSHAKE_TIMEOUT = 5000;
|
private static final int HANDSHAKE_TIMEOUT = 5000;
|
||||||
private static final int STANDARD_TIMEOUT = 300_000;
|
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) {
|
public static TcpConnection createConnection(InetAddress address, int port, String sender, String receiver, boolean isPrivate) {
|
||||||
try {
|
try {
|
||||||
TcpConnection connection;
|
TcpConnection connection;
|
||||||
|
|
@ -42,6 +76,22 @@ public class ConnectionFactory {
|
||||||
return null;
|
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 já 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) {
|
public static TcpConnection receiveConnection(Socket socket) {
|
||||||
try {
|
try {
|
||||||
TcpConnection connection;
|
TcpConnection connection;
|
||||||
|
|
@ -66,6 +116,27 @@ public class ConnectionFactory {
|
||||||
return null;
|
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) {
|
private static HandshakeMessage validateConnection(TcpConnection connection) {
|
||||||
try {
|
try {
|
||||||
connection.setTimeout(HANDSHAKE_TIMEOUT);
|
connection.setTimeout(HANDSHAKE_TIMEOUT);
|
||||||
|
|
@ -113,6 +184,20 @@ public class ConnectionFactory {
|
||||||
return null;
|
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) {
|
private static boolean validateHandshake(HandshakeMessage handshake, TcpConnection connection) {
|
||||||
String sender = handshake.getSender();
|
String sender = handshake.getSender();
|
||||||
String receiver = handshake.getReceiver();
|
String receiver = handshake.getReceiver();
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,27 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
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 Logger logger;
|
||||||
private final Map<InetAddress, TcpConnection> publicConnections;
|
private final Map<InetAddress, TcpConnection> publicConnections;
|
||||||
private final Map<InetAddress, List<TcpConnection>> privateConnections;
|
private final Map<InetAddress, List<TcpConnection>> privateConnections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cria um gerenciador de conexões TCP vazio.
|
||||||
|
*/
|
||||||
public ConnectionManager() {
|
public ConnectionManager() {
|
||||||
publicConnections = new ConcurrentHashMap<>();
|
publicConnections = new ConcurrentHashMap<>();
|
||||||
privateConnections = new ConcurrentHashMap<>();
|
privateConnections = new ConcurrentHashMap<>();
|
||||||
|
|
@ -26,11 +42,28 @@ public class ConnectionManager implements PeerListener {
|
||||||
logger = Logger.getLogger(getClass().getName());
|
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) {
|
public TcpConnection getPublicConnection(InetAddress address) {
|
||||||
return publicConnections.get(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 =
|
List<TcpConnection> connections =
|
||||||
privateConnections.computeIfAbsent(connection.getAddress(), _ -> new ArrayList<>());
|
privateConnections.computeIfAbsent(connection.getAddress(), _ -> new ArrayList<>());
|
||||||
|
|
||||||
|
|
@ -38,17 +71,54 @@ public class ConnectionManager implements PeerListener {
|
||||||
connections.add(connection);
|
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 já 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);
|
TcpConnection oldConnection = publicConnections.put(connection.getAddress(), connection);
|
||||||
if (oldConnection != null && oldConnection != connection) {
|
if (oldConnection != null && oldConnection != connection) {
|
||||||
closeConnection(oldConnection);
|
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) {
|
public void closeConnection(TcpConnection connection, String reason) {
|
||||||
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason);
|
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason);
|
||||||
closeConnection(connection);
|
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 já 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) {
|
public void closeConnection(TcpConnection connection) {
|
||||||
if (connection == null) return;
|
if (connection == null) return;
|
||||||
if (connection.isPrivate()) {
|
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) {
|
public void closeByAdress(InetAddress address, String reason) {
|
||||||
List<TcpConnection> connections = privateConnections.get(address);
|
List<TcpConnection> connections = privateConnections.get(address);
|
||||||
for (TcpConnection connection : connections) {
|
for (TcpConnection connection : connections) {
|
||||||
|
|
@ -81,6 +158,14 @@ public class ConnectionManager implements PeerListener {
|
||||||
closeByAdress(address);
|
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) {
|
public void closeByAdress(InetAddress address) {
|
||||||
TcpConnection publicConnection = publicConnections.remove(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) {
|
public void closeAllConnections(String reason) {
|
||||||
for (List<TcpConnection> connections : privateConnections.values()) {
|
for (List<TcpConnection> connections : privateConnections.values()) {
|
||||||
for (TcpConnection connection : connections) {
|
for (TcpConnection connection : connections) {
|
||||||
|
|
@ -119,6 +210,14 @@ public class ConnectionManager implements PeerListener {
|
||||||
}
|
}
|
||||||
closeAllConnections();
|
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() {
|
public void closeAllConnections() {
|
||||||
for (TcpConnection connection : publicConnections.values()) {
|
for (TcpConnection connection : publicConnections.values()) {
|
||||||
closeConnection(connection);
|
closeConnection(connection);
|
||||||
|
|
@ -137,11 +236,14 @@ public class ConnectionManager implements PeerListener {
|
||||||
/**
|
/**
|
||||||
* Atualiza as conexões de acordo com os peers atualmente ativos.
|
* 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
|
* <p>Os endereços presentes na lista de peers são utilizados como
|
||||||
* e removidas.</p>
|
* 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.
|
* @param peers lista atualizada de peers ativos.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPeersChanged(List<PeerInfo> peers) {
|
public void onPeersChanged(List<PeerInfo> peers) {
|
||||||
Set<InetAddress> activePeers = peers.stream().map(PeerInfo::getAddress).collect(Collectors.toSet());
|
Set<InetAddress> activePeers = peers.stream().map(PeerInfo::getAddress).collect(Collectors.toSet());
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,12 @@ public class SocketTCP extends Thread {
|
||||||
/**
|
/**
|
||||||
* Cria o servidor TCP da aplicação.
|
* Cria o servidor TCP da aplicação.
|
||||||
*
|
*
|
||||||
* <p>O servidor utiliza a porta definida pela aplicação e inicia sem
|
* <p>O servidor é criado na porta definida pela aplicação. As conexões
|
||||||
* estabelecer conexões com os peers. As conexões são criadas conforme
|
* com os peers são estabelecidas sob demanda durante o envio ou aceitas
|
||||||
* necessário durante o envio ou recebidas de outros peers.</p>
|
* 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) {
|
public SocketTCP(MainWindow main) {
|
||||||
this.main = 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) {
|
public void send(byte[] msg, InetAddress destinationAddress) {
|
||||||
try {
|
try {
|
||||||
TcpConnection connection = getPublicConnection(destinationAddress, main.getIdentifier(), main.getIdentifier());
|
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) {
|
public TcpConnection createPrivateConnection(InetAddress destinationAddress, String senderId, String receiverId) {
|
||||||
return establishOutgoingConnection(destinationAddress, senderId, receiverId, true);
|
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 já 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) {
|
private synchronized TcpConnection getPublicConnection(InetAddress destinationAddress, String senderId, String receiverId) {
|
||||||
TcpConnection connection;
|
TcpConnection connection;
|
||||||
|
|
||||||
|
|
@ -98,9 +144,9 @@ public class SocketTCP extends Thread {
|
||||||
/**
|
/**
|
||||||
* Aguarda e aceita novas conexões TCP.
|
* Aguarda e aceita novas conexões TCP.
|
||||||
*
|
*
|
||||||
* <p>Cada conexão aceita é associada ao endereço do peer e registrada para
|
* <p>Cada socket aceito é encaminhado para processamento assíncrono,
|
||||||
* que possa ser utilizada tanto para recepção quanto para envio de
|
* interpretado como uma {@link TcpConnection}, registrado pelo
|
||||||
* mensagens.</p>
|
* {@link ConnectionManager} e associado ao receptor adequado.</p>
|
||||||
*/
|
*/
|
||||||
private void receive() {
|
private void receive() {
|
||||||
while (running) {
|
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) {
|
private TcpConnection establishOutgoingConnection(InetAddress destinationAddress, String sender, String receiver, boolean isPrivate) {
|
||||||
if (address.equals(destinationAddress)) {
|
if (address.equals(destinationAddress)) {
|
||||||
System.out.println("Failed to get socket.");
|
System.out.println("Failed to get socket.");
|
||||||
|
|
@ -138,6 +200,15 @@ public class SocketTCP extends Thread {
|
||||||
return connection;
|
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) {
|
private void acceptIncomingConnection(Socket socket) {
|
||||||
connectionExecutor.submit(() -> {
|
connectionExecutor.submit(() -> {
|
||||||
TcpConnection connection = ConnectionFactory.receiveConnection(socket);
|
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) {
|
private void saveConnection(TcpConnection connection) {
|
||||||
if (connection.isPrivate()) {
|
if (connection.isPrivate()) {
|
||||||
connectionManager.addPrivateConnection(connection.getAddress(), connection);
|
connectionManager.addPrivateConnection(connection);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
private void startReceiver(TcpConnection connection, boolean notify) {
|
||||||
try {
|
|
||||||
if (!connection.isPrivate()) {
|
if (!connection.isPrivate()) {
|
||||||
publicConnectionReceiver(connection);
|
publicConnectionReceiver(connection);
|
||||||
return;
|
return;
|
||||||
|
|
@ -177,11 +268,18 @@ public class SocketTCP extends Thread {
|
||||||
privateConnectionReceiver(connection, plugin);
|
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) {
|
private Plugin findConnectionReceiver(TcpConnection connection) {
|
||||||
Plugin receiver = null;
|
Plugin receiver = null;
|
||||||
for(Plugin plugin : main.getPlugins()) {
|
for(Plugin plugin : main.getPlugins()) {
|
||||||
|
|
@ -201,6 +299,19 @@ public class SocketTCP extends Thread {
|
||||||
return receiver;
|
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) {
|
private void publicConnectionReceiver(TcpConnection connection) {
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
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) {
|
private void privateConnectionReceiver(TcpConnection connection, Plugin plugin) {
|
||||||
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
|
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
|
||||||
while (running) {
|
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 há 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) {
|
while (running) {
|
||||||
try {
|
try {
|
||||||
byte[] message = connection.receive();
|
byte[] message = connection.receive();
|
||||||
|
|
@ -308,12 +440,25 @@ public class SocketTCP extends Thread {
|
||||||
//TODO: Ainda não existem mensagens trocadas entre o sistema.
|
//TODO: Ainda não existem mensagens trocadas entre o sistema.
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SocketTimeoutException ex) {
|
}
|
||||||
|
catch (SocketTimeoutException ex) {
|
||||||
if (connection.hasTimedOut()) return;
|
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) {
|
public void disconnect(TcpConnection connection, String description) {
|
||||||
connectionManager.closeConnection(
|
connectionManager.closeConnection(
|
||||||
connection,
|
connection,
|
||||||
|
|
@ -321,11 +466,22 @@ public class SocketTCP extends Thread {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
receive();
|
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() {
|
public void close() {
|
||||||
running = false;
|
running = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,15 @@ import java.net.Socket;
|
||||||
import java.net.SocketException;
|
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
|
* <p>A conexão permite o envio e recebimento de mensagens, além de manter
|
||||||
* mensagens. Também é responsável pelo framing das mensagens, permitindo
|
* informações sobre o endereço remoto e metadados da conexão, como remetente,
|
||||||
* que várias mensagens sejam transmitidas pela mesma conexão.</p>
|
* destinatário e se a conexão é privada.</p>
|
||||||
*
|
*
|
||||||
* @author Gustavo
|
* @author Gustavo
|
||||||
*/
|
*/
|
||||||
public class TcpConnection {
|
public class TcpConnection {
|
||||||
private static final long TIMEOUT = 300_000; // 5 minutos
|
|
||||||
|
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
private final DataInputStream input;
|
private final DataInputStream input;
|
||||||
|
|
@ -25,19 +24,35 @@ public class TcpConnection {
|
||||||
private final InetAddress address;
|
private final InetAddress address;
|
||||||
private String sender;
|
private String sender;
|
||||||
private String receiver;
|
private String receiver;
|
||||||
|
|
||||||
private boolean isPrivate;
|
private boolean isPrivate;
|
||||||
|
|
||||||
private volatile long lastActivity;
|
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 {
|
TcpConnection(Socket socket) throws IOException {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.input = new DataInputStream(socket.getInputStream());
|
this.input = new DataInputStream(socket.getInputStream());
|
||||||
this.output = new DataOutputStream(socket.getOutputStream());
|
this.output = new DataOutputStream(socket.getOutputStream());
|
||||||
this.address = socket.getInetAddress();
|
this.address = socket.getInetAddress();
|
||||||
this.lastActivity = System.currentTimeMillis();
|
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 {
|
public synchronized void send(byte[] message) throws IOException {
|
||||||
output.writeInt(message.length);
|
output.writeInt(message.length);
|
||||||
output.write(message);
|
output.write(message);
|
||||||
|
|
@ -45,6 +60,16 @@ public class TcpConnection {
|
||||||
updateLastActivity();
|
updateLastActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recebe uma mensagem da conexão.
|
||||||
|
*
|
||||||
|
* <p>Primeiro lê o tamanho da mensagem e, em seguida, lê 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 {
|
byte[] receive() throws IOException {
|
||||||
int length = input.readInt();
|
int length = input.readInt();
|
||||||
|
|
||||||
|
|
@ -61,28 +86,63 @@ public class TcpConnection {
|
||||||
return message;
|
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() {
|
public boolean isClosed() {
|
||||||
return socket.isClosed();
|
return socket.isClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fecha o socket da conexão.
|
||||||
|
*
|
||||||
|
* @throws IOException se ocorrer um erro ao fechar o socket
|
||||||
|
*/
|
||||||
void close() throws IOException {
|
void close() throws IOException {
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna o endereço do peer remoto conectado.
|
||||||
|
*
|
||||||
|
* @return endereço IP do peer remoto
|
||||||
|
*/
|
||||||
public InetAddress getAddress() {
|
public InetAddress getAddress() {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atualiza o instante da última atividade da conexão.
|
||||||
|
*/
|
||||||
private void updateLastActivity() {
|
private void updateLastActivity() {
|
||||||
lastActivity = System.currentTimeMillis();
|
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 {
|
void setTimeout(int timeout) throws SocketException {
|
||||||
socket.setSoTimeout(timeout);
|
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 há pelo menos 5 minutos;
|
||||||
|
* {@code false} caso contrário
|
||||||
|
*/
|
||||||
boolean hasTimedOut() {
|
boolean hasTimedOut() {
|
||||||
return System.currentTimeMillis() - lastActivity >= TIMEOUT;
|
return System.currentTimeMillis() - lastActivity >= timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getReceiver() {
|
public String getReceiver() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue