Compare commits
No commits in common. "93ac292f72b3275c26717a9c79227bdb67c4a2ce" and "3efc59444f7dc654df4209744e873bf6664b035a" have entirely different histories.
93ac292f72
...
3efc59444f
|
|
@ -5,8 +5,8 @@
|
||||||
*/
|
*/
|
||||||
package pitiupi.GUI;
|
package pitiupi.GUI;
|
||||||
|
|
||||||
import pitiupi.net.tcp.SocketTCP;
|
import pitiupi.net.SocketTCP;
|
||||||
import pitiupi.net.udp.SocketUDP;
|
import pitiupi.net.SocketUDP;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
|
|
@ -44,9 +44,7 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
private SocketTCP socketTCP;
|
private SocketTCP socketTCP;
|
||||||
|
|
||||||
private String userName;
|
private String userName;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private String pitiupiIdentifier;
|
|
||||||
|
|
||||||
private PluginLoader pluginManager;
|
private PluginLoader pluginManager;
|
||||||
private final List<Plugin> plugins;
|
private final List<Plugin> plugins;
|
||||||
|
|
@ -60,7 +58,6 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
public MainWindow(String userName, int port) {
|
public MainWindow(String userName, int port) {
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.pitiupiIdentifier = "Pitiupi.Alice.2026";
|
|
||||||
this.menuBar = new MenuBar(this);
|
this.menuBar = new MenuBar(this);
|
||||||
this.plugins = new ArrayList<>();
|
this.plugins = new ArrayList<>();
|
||||||
this.initComponents();
|
this.initComponents();
|
||||||
|
|
@ -130,10 +127,6 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
return this.userName;
|
return this.userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIdentifier(){
|
|
||||||
return this.pitiupiIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMenu(JMenu menu) {
|
public void addMenu(JMenu menu) {
|
||||||
this.menuBar.addPlugin(menu);
|
this.menuBar.addPlugin(menu);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
package pitiupi.net;
|
package pitiupi.net;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mensagem utilizada para sinalizar a presença de um usuário na rede.
|
* Mensagem utilizada para sinalizar a presença de um usuário na rede.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,244 @@
|
||||||
|
package pitiupi.net;
|
||||||
|
|
||||||
|
import pitiupi.GUI.MainWindow;
|
||||||
|
import pitiupi.control.PeerInfo;
|
||||||
|
import pitiupi.control.PeerListener;
|
||||||
|
import pitiupi.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gerencia a comunicação TCP da aplicação.
|
||||||
|
*
|
||||||
|
* <p>Mantém conexões persistentes com os peers e reutiliza essas conexões
|
||||||
|
* para o envio de mensagens. As mensagens recebidas são encaminhadas aos
|
||||||
|
* plugins, que são responsáveis por interpretá-las.</p>
|
||||||
|
*
|
||||||
|
* <p>Novas conexões são aceitas pelo servidor e cada conexão possui uma
|
||||||
|
* tarefa própria para receber mensagens, permitindo que múltiplas conexões
|
||||||
|
* sejam mantidas simultaneamente.</p>
|
||||||
|
*
|
||||||
|
* <p>As conexões são associadas aos endereços dos peers e podem ser
|
||||||
|
* encerradas quando um peer deixa de estar ativo.</p>
|
||||||
|
*
|
||||||
|
* @author Gustavo
|
||||||
|
*/
|
||||||
|
public class SocketTCP extends Thread implements PeerListener {
|
||||||
|
private final ExecutorService connectionExecutor = Executors.newCachedThreadPool();
|
||||||
|
private final Map<InetAddress, TcpConnection> connections;
|
||||||
|
private ServerSocket serverSocket;
|
||||||
|
private InetAddress address;
|
||||||
|
|
||||||
|
private final MainWindow main;
|
||||||
|
|
||||||
|
private volatile boolean running = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
*
|
||||||
|
* @param main janela principal da aplicação.
|
||||||
|
*/
|
||||||
|
public SocketTCP(MainWindow main) {
|
||||||
|
this.main = main;
|
||||||
|
connections = new ConcurrentHashMap<>();
|
||||||
|
try {
|
||||||
|
serverSocket = new ServerSocket(main.getPort());
|
||||||
|
address = InetAddress.getByName(InetAddress.getLocalHost().getHostAddress());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.out.println("There is no socket connection. Sorry.");
|
||||||
|
System.out.println(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envia uma mensagem TCP para um peer.
|
||||||
|
*
|
||||||
|
* <p>Uma conexão existente com o peer é reutilizada. Caso não exista uma
|
||||||
|
* conexão válida, uma nova conexão é criada e adicionada ao conjunto de
|
||||||
|
* conexões ativas.</p>
|
||||||
|
*
|
||||||
|
* @param msg mensagem serializada a ser enviada.
|
||||||
|
* @param destinationAddress endereço IP do peer destinatário.
|
||||||
|
*/
|
||||||
|
public void send(byte[] msg, InetAddress destinationAddress) {
|
||||||
|
try {
|
||||||
|
if (destinationAddress.equals(address)) {
|
||||||
|
throw new IllegalArgumentException("Destination address cannot be the same as the original address.");
|
||||||
|
}
|
||||||
|
TcpConnection connec tion;
|
||||||
|
|
||||||
|
synchronized (connections) {
|
||||||
|
connection = connections.get(destinationAddress);
|
||||||
|
|
||||||
|
if (connection == null || connection.isClosed()) {
|
||||||
|
Socket socket = new Socket(destinationAddress, main.getPort());
|
||||||
|
|
||||||
|
connection = new TcpConnection(socket);
|
||||||
|
|
||||||
|
connections.put(destinationAddress, connection);
|
||||||
|
|
||||||
|
startReceiver(destinationAddress, connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.send(msg);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.out.println("Could not send TCP message.");
|
||||||
|
System.out.println(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
*/
|
||||||
|
private void receive() {
|
||||||
|
while (running) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Socket socket = serverSocket.accept();
|
||||||
|
|
||||||
|
InetAddress address = socket.getInetAddress();
|
||||||
|
|
||||||
|
TcpConnection connection = new TcpConnection(socket);
|
||||||
|
|
||||||
|
TcpConnection oldConnection = connections.put(address, connection);
|
||||||
|
|
||||||
|
if (oldConnection != null) {
|
||||||
|
oldConnection.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
startReceiver(address, connection);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
|
||||||
|
if (!running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Error accepting TCP connection.");
|
||||||
|
System.out.println(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicia a recepção de mensagens de uma conexão TCP.
|
||||||
|
*
|
||||||
|
* <p>A recepção é executada de forma independente para que uma conexão
|
||||||
|
* não impeça a aceitação ou o processamento de outras conexões.</p>
|
||||||
|
*
|
||||||
|
* <p>Cada mensagem recebida é encaminhada aos plugins da aplicação.</p>
|
||||||
|
*
|
||||||
|
* @param address endereço do peer associado à conexão.
|
||||||
|
* @param connection conexão TCP utilizada para a comunicação.
|
||||||
|
*/
|
||||||
|
private void startReceiver(InetAddress address, TcpConnection connection) {
|
||||||
|
connectionExecutor.submit(() -> {
|
||||||
|
try {
|
||||||
|
while (running) {
|
||||||
|
byte[] message = connection.receive();
|
||||||
|
|
||||||
|
for (Plugin plugin : main.getPlugins()) {
|
||||||
|
plugin.receiveMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.out.println(
|
||||||
|
"Connection with " + address.getHostAddress() + " closed."
|
||||||
|
);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
connections.remove(address, connection);
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicia o processo de recepção de conexões TCP.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encerra o servidor TCP e as conexões associadas.
|
||||||
|
*
|
||||||
|
* <p>As conexões existentes são encerradas e o servidor deixa
|
||||||
|
* de aceitar novas conexões.</p>
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
if (serverSocket != null) {
|
||||||
|
try {
|
||||||
|
serverSocket.close();
|
||||||
|
} catch (IOException _) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TcpConnection connection : connections.values()) {
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
} catch (IOException _) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connections.clear();
|
||||||
|
|
||||||
|
connectionExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
*
|
||||||
|
* @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());
|
||||||
|
|
||||||
|
for (InetAddress address : connections.keySet()) {
|
||||||
|
if (!activePeers.contains(address)) {
|
||||||
|
|
||||||
|
TcpConnection connection = connections.remove(address);
|
||||||
|
if (connection != null) {
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package pitiupi.net.udp;
|
package pitiupi.net;
|
||||||
|
|
||||||
import pitiupi.GUI.MainWindow;
|
import pitiupi.GUI.MainWindow;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
@ -13,9 +13,6 @@ import java.io.ObjectInputStream;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import pitiupi.net.HeartbeatMessage;
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
import pitiupi.plugin.Plugin;
|
import pitiupi.plugin.Plugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -148,15 +145,9 @@ public class SocketUDP extends Thread {
|
||||||
main.getHeartbeatManager().receiveHeartbeat(heartbeat, msgPacket.getAddress());
|
main.getHeartbeatManager().receiveHeartbeat(heartbeat, msgPacket.getAddress());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message message = tryParseMessage(msgPacket.getData());
|
|
||||||
if (message == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Plugin plugin : main.getPlugins()) {
|
for (Plugin plugin : main.getPlugins()) {
|
||||||
System.out.println(msgPacket.getAddress().toString());
|
System.out.println(msgPacket.getAddress().toString());
|
||||||
plugin.receiveMessage(message);
|
plugin.receiveMessage(msgPacket.getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,20 +170,6 @@ public class SocketUDP extends Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message tryParseMessage(byte[] data) {
|
|
||||||
try {
|
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(bis);
|
|
||||||
|
|
||||||
Object object = ois.readObject();
|
|
||||||
|
|
||||||
if (object instanceof Message message) return message;
|
|
||||||
return null;
|
|
||||||
} catch (IOException | ClassNotFoundException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inicia a recepção de mensagens UDP.
|
* Inicia a recepção de mensagens UDP.
|
||||||
*/
|
*/
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package pitiupi.net;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representa uma conexão TCP persistente com um peer.
|
||||||
|
*
|
||||||
|
* <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>
|
||||||
|
*
|
||||||
|
* @author Gustavo
|
||||||
|
*/
|
||||||
|
class TcpConnection {
|
||||||
|
|
||||||
|
private final Socket socket;
|
||||||
|
private final DataInputStream input;
|
||||||
|
private final DataOutputStream output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cria uma conexão a partir de um socket existente.
|
||||||
|
*
|
||||||
|
* @param socket socket TCP utilizado pela conexão.
|
||||||
|
* @throws IOException caso não seja possível obter os streams do socket.
|
||||||
|
*/
|
||||||
|
public TcpConnection(Socket socket) throws IOException {
|
||||||
|
this.socket = socket;
|
||||||
|
this.input = new DataInputStream(socket.getInputStream());
|
||||||
|
this.output = new DataOutputStream(socket.getOutputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envia uma mensagem pela conexão.
|
||||||
|
*
|
||||||
|
* <p>A mensagem é precedida por seu tamanho para permitir que o receptor
|
||||||
|
* identifique o limite entre mensagens transmitidas pela mesma conexão.</p>
|
||||||
|
*
|
||||||
|
* @param message mensagem serializada a ser enviada.
|
||||||
|
* @throws IOException caso ocorra um erro durante o envio.
|
||||||
|
*/
|
||||||
|
public void send(byte[] message) throws IOException {
|
||||||
|
synchronized (output) {
|
||||||
|
output.writeInt(message.length);
|
||||||
|
output.write(message);
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recebe uma mensagem completa da conexão.
|
||||||
|
*
|
||||||
|
* <p>O tamanho da mensagem é lido primeiro e, em seguida, os dados
|
||||||
|
* correspondentes são recebidos.</p>
|
||||||
|
*
|
||||||
|
* @return mensagem recebida.
|
||||||
|
* @throws IOException caso ocorra um erro durante a recepção.
|
||||||
|
*/
|
||||||
|
public byte[] receive() throws IOException {
|
||||||
|
int length = input.readInt();
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
throw new IOException("Invalid message length.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return input.readNBytes(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se o socket da conexão está fechado.
|
||||||
|
*
|
||||||
|
* @return {@code true} caso o socket esteja fechado.
|
||||||
|
*/
|
||||||
|
public boolean isClosed() {
|
||||||
|
return socket.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encerra a conexão TCP.
|
||||||
|
*
|
||||||
|
* @throws IOException caso ocorra um erro ao fechar o socket.
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class ConnectionFactory {
|
|
||||||
private static final int HANDSHAKE_TIMEOUT = 5000;
|
|
||||||
private static final int STANDARD_TIMEOUT = 300_000;
|
|
||||||
|
|
||||||
public static TcpConnection createConnection(InetAddress address, int port, String sender, String receiver, boolean isPrivate) {
|
|
||||||
try {
|
|
||||||
TcpConnection connection;
|
|
||||||
|
|
||||||
Socket socket = new Socket(address, port);
|
|
||||||
connection = new TcpConnection(socket);
|
|
||||||
connection.setSender(sender);
|
|
||||||
connection.setReceiver(receiver);
|
|
||||||
connection.setPrivate(isPrivate);
|
|
||||||
|
|
||||||
HandshakeMessage handshake = new HandshakeMessage(isPrivate, sender, receiver);
|
|
||||||
|
|
||||||
connection.send(handshake.toByteArray());
|
|
||||||
HandshakeMessage handshakeResponse = validateConnection(connection);
|
|
||||||
|
|
||||||
if (handshakeResponse == null) return null;
|
|
||||||
|
|
||||||
connection.setTimeout(STANDARD_TIMEOUT);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TcpConnection receiveConnection(Socket socket) {
|
|
||||||
try {
|
|
||||||
TcpConnection connection;
|
|
||||||
|
|
||||||
connection = new TcpConnection(socket);
|
|
||||||
|
|
||||||
HandshakeMessage handshake = validateConnection(connection);
|
|
||||||
|
|
||||||
if (handshake == null) return null;
|
|
||||||
|
|
||||||
connection.setSender(handshake.getSender());
|
|
||||||
connection.setReceiver(handshake.getReceiver());
|
|
||||||
connection.setPrivate(handshake.isPrivate());
|
|
||||||
|
|
||||||
connection.setTimeout(STANDARD_TIMEOUT);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HandshakeMessage validateConnection(TcpConnection connection) {
|
|
||||||
try {
|
|
||||||
connection.setTimeout(HANDSHAKE_TIMEOUT);
|
|
||||||
byte[] bytes = connection.receive();
|
|
||||||
|
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(bis);
|
|
||||||
|
|
||||||
Object object = ois.readObject();
|
|
||||||
|
|
||||||
if (object instanceof Message message) {
|
|
||||||
if (SysInfoMessage.parseSystemInfo(message) != null) {
|
|
||||||
//TODO: Tratar erros
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message instanceof HandshakeMessage handshake) {
|
|
||||||
return (validateHandshake(handshake, connection)) ? handshake : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.CONNECTION_REJECTED,
|
|
||||||
"The message was not from an allowed type."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (SocketTimeoutException ex) {
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.CONNECTION_REJECTED,
|
|
||||||
"No handshake was received within the allowed time."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException | IOException ex) {
|
|
||||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.INTERNAL_ERROR,
|
|
||||||
"The server could not process the handshake."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean validateHandshake(HandshakeMessage handshake, TcpConnection connection) {
|
|
||||||
String sender = handshake.getSender();
|
|
||||||
String receiver = handshake.getReceiver();
|
|
||||||
|
|
||||||
if (sender == null || sender.isBlank()) {
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.CONNECTION_REJECTED,
|
|
||||||
"Invalid Sender: " + sender + "."
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (receiver == null || receiver.isBlank()) {
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.CONNECTION_REJECTED,
|
|
||||||
"Invalid Receiver: " + receiver + "."
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
import pitiupi.control.PeerInfo;
|
|
||||||
import pitiupi.control.PeerListener;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class ConnectionManager implements PeerListener {
|
|
||||||
private final Logger logger;
|
|
||||||
private final Map<InetAddress, TcpConnection> publicConnections;
|
|
||||||
private final Map<InetAddress, List<TcpConnection>> privateConnections;
|
|
||||||
|
|
||||||
public ConnectionManager() {
|
|
||||||
publicConnections = new ConcurrentHashMap<>();
|
|
||||||
privateConnections = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
logger = Logger.getLogger(getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TcpConnection getPublicConnection(InetAddress address) {
|
|
||||||
return publicConnections.get(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPrivateConnection(InetAddress address, TcpConnection connection) {
|
|
||||||
List<TcpConnection> connections =
|
|
||||||
privateConnections.computeIfAbsent(connection.getAddress(), _ -> new ArrayList<>());
|
|
||||||
|
|
||||||
synchronized (connections) {
|
|
||||||
connections.add(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void addPublicConnection(InetAddress address, TcpConnection connection) {
|
|
||||||
TcpConnection oldConnection = publicConnections.put(connection.getAddress(), connection);
|
|
||||||
if (oldConnection != null && oldConnection != connection) {
|
|
||||||
closeConnection(oldConnection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeConnection(TcpConnection connection, String reason) {
|
|
||||||
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason);
|
|
||||||
closeConnection(connection);
|
|
||||||
}
|
|
||||||
public void closeConnection(TcpConnection connection) {
|
|
||||||
if (connection == null) return;
|
|
||||||
if (connection.isPrivate()) {
|
|
||||||
List<TcpConnection> connections = privateConnections.get(connection.getAddress());
|
|
||||||
|
|
||||||
if (connections != null) {
|
|
||||||
synchronized (connections) {
|
|
||||||
connections.remove(connection);
|
|
||||||
|
|
||||||
if (connections.isEmpty()) privateConnections.remove(connection.getAddress(), connections);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else publicConnections.remove(connection.getAddress(), connection);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!connection.isClosed()) {
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error closing TCP connection", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeByAdress(InetAddress address, String reason) {
|
|
||||||
List<TcpConnection> connections = privateConnections.get(address);
|
|
||||||
for (TcpConnection connection : connections) {
|
|
||||||
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason);
|
|
||||||
}
|
|
||||||
closeByAdress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeByAdress(InetAddress address) {
|
|
||||||
TcpConnection publicConnection = publicConnections.remove(address);
|
|
||||||
|
|
||||||
if (publicConnection != null) {
|
|
||||||
try {
|
|
||||||
publicConnection.close();
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error closing TCP connection", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TcpConnection> connections = privateConnections.remove(address);
|
|
||||||
|
|
||||||
if (connections != null) {
|
|
||||||
synchronized (connections) {
|
|
||||||
for (TcpConnection connection : connections) {
|
|
||||||
try {
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error closing TCP connection", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
connections.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeAllConnections(String reason) {
|
|
||||||
for (List<TcpConnection> connections : privateConnections.values()) {
|
|
||||||
for (TcpConnection connection : connections) {
|
|
||||||
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closeAllConnections();
|
|
||||||
}
|
|
||||||
public void closeAllConnections() {
|
|
||||||
for (TcpConnection connection : publicConnections.values()) {
|
|
||||||
closeConnection(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (privateConnections) {
|
|
||||||
for (List<TcpConnection> connectionsList : privateConnections.values()) {
|
|
||||||
for (TcpConnection connection : connectionsList) {
|
|
||||||
closeConnection(connection);
|
|
||||||
}
|
|
||||||
privateConnections.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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>
|
|
||||||
*
|
|
||||||
* @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());
|
|
||||||
|
|
||||||
for (InetAddress address : publicConnections.keySet()) {
|
|
||||||
if (!activePeers.contains(address)) {
|
|
||||||
closeByAdress(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (InetAddress address : privateConnections.keySet()) {
|
|
||||||
if (!activePeers.contains(address)) {
|
|
||||||
closeByAdress(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
|
|
||||||
class HandshakeMessage extends Message {
|
|
||||||
private final boolean isPrivate;
|
|
||||||
private final String sender;
|
|
||||||
private final String receiver;
|
|
||||||
|
|
||||||
|
|
||||||
public HandshakeMessage(boolean isPrivate, String sender, String receiver) {
|
|
||||||
this.isPrivate = isPrivate;
|
|
||||||
this.sender = sender;
|
|
||||||
this.receiver = receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPrivate() {
|
|
||||||
return isPrivate;
|
|
||||||
}
|
|
||||||
public String getSender() {
|
|
||||||
return sender;
|
|
||||||
}
|
|
||||||
public String getReceiver() {
|
|
||||||
return receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HandshakeMessage createResponse() {
|
|
||||||
return new HandshakeMessage(isPrivate, receiver, sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,344 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
import pitiupi.GUI.MainWindow;
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
import pitiupi.plugin.Plugin;
|
|
||||||
import pitiupi.plugin.PrivateConnectionPlugin;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gerencia a comunicação TCP da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Mantém conexões persistentes com os peers e reutiliza essas conexões
|
|
||||||
* para o envio de mensagens. As mensagens recebidas são encaminhadas aos
|
|
||||||
* plugins, que são responsáveis por interpretá-las.</p>
|
|
||||||
*
|
|
||||||
* <p>Novas conexões são aceitas pelo servidor e cada conexão possui uma
|
|
||||||
* tarefa própria para receber mensagens, permitindo que múltiplas conexões
|
|
||||||
* sejam mantidas simultaneamente.</p>
|
|
||||||
*
|
|
||||||
* <p>As conexões são associadas aos endereços dos peers e podem ser
|
|
||||||
* encerradas quando um peer deixa de estar ativo.</p>
|
|
||||||
*
|
|
||||||
* @author Gustavo
|
|
||||||
*/
|
|
||||||
public class SocketTCP extends Thread {
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
private final ExecutorService connectionExecutor = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
private final ConnectionManager connectionManager;
|
|
||||||
|
|
||||||
private ServerSocket serverSocket;
|
|
||||||
private InetAddress address;
|
|
||||||
|
|
||||||
private final MainWindow main;
|
|
||||||
|
|
||||||
private volatile boolean running = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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>
|
|
||||||
*
|
|
||||||
* @param main janela principal da aplicação.
|
|
||||||
*/
|
|
||||||
public SocketTCP(MainWindow main) {
|
|
||||||
this.main = main;
|
|
||||||
logger = Logger.getLogger(getClass().getName());
|
|
||||||
connectionManager = new ConnectionManager();
|
|
||||||
try {
|
|
||||||
serverSocket = new ServerSocket(main.getPort());
|
|
||||||
address = InetAddress.getByName(InetAddress.getLocalHost().getHostAddress());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("There is no socket connection. Sorry.");
|
|
||||||
System.out.println(ex.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(byte[] msg, InetAddress destinationAddress) {
|
|
||||||
try {
|
|
||||||
TcpConnection connection = getPublicConnection(destinationAddress, main.getIdentifier(), main.getIdentifier());
|
|
||||||
if (connection == null) {
|
|
||||||
System.out.println("Could not establish TCP connection.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connection.send(msg);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
System.out.println("Could not send TCP message.");
|
|
||||||
System.out.println(ex.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TcpConnection createPrivateConnection(InetAddress destinationAddress, String senderId, String receiverId) {
|
|
||||||
return establishOutgoingConnection(destinationAddress, senderId, receiverId, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized TcpConnection getPublicConnection(InetAddress destinationAddress, String senderId, String receiverId) {
|
|
||||||
TcpConnection connection;
|
|
||||||
|
|
||||||
connection = connectionManager.getPublicConnection(destinationAddress);
|
|
||||||
if (connection != null && !connection.isClosed()) return connection;
|
|
||||||
|
|
||||||
connection = establishOutgoingConnection(destinationAddress, senderId, receiverId, false);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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>
|
|
||||||
*/
|
|
||||||
private void receive() {
|
|
||||||
while (running) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
Socket socket = serverSocket.accept();
|
|
||||||
acceptIncomingConnection(socket);
|
|
||||||
|
|
||||||
} catch (IOException ex) {
|
|
||||||
if (!running) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Error accepting TCP connection.");
|
|
||||||
System.out.println(ex.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private TcpConnection establishOutgoingConnection(InetAddress destinationAddress, String sender, String receiver, boolean isPrivate) {
|
|
||||||
if (address.equals(destinationAddress)) {
|
|
||||||
System.out.println("Failed to get socket.");
|
|
||||||
throw new IllegalArgumentException("Connections to the local host are forbidden.");
|
|
||||||
}
|
|
||||||
|
|
||||||
TcpConnection connection = ConnectionFactory.createConnection(destinationAddress, main.getPort(), sender, receiver, isPrivate);
|
|
||||||
if (connection == null) return null;
|
|
||||||
|
|
||||||
saveConnection(connection);
|
|
||||||
|
|
||||||
connectionExecutor.submit(() -> {
|
|
||||||
startReceiver(connection, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void acceptIncomingConnection(Socket socket) {
|
|
||||||
connectionExecutor.submit(() -> {
|
|
||||||
TcpConnection connection = ConnectionFactory.receiveConnection(socket);
|
|
||||||
if (connection == null) return;
|
|
||||||
|
|
||||||
saveConnection(connection);
|
|
||||||
startReceiver(connection, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveConnection(TcpConnection connection) {
|
|
||||||
if (connection.isPrivate()) {
|
|
||||||
connectionManager.addPrivateConnection(connection.getAddress(), connection);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
connectionManager.addPublicConnection(connection.getAddress(), connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startReceiver(TcpConnection connection, boolean notify) {
|
|
||||||
try {
|
|
||||||
if (!connection.isPrivate()) {
|
|
||||||
publicConnectionReceiver(connection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection.getReceiver().equals(main.getIdentifier())) {
|
|
||||||
systemConnectionReceiver(connection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plugin receiver = findConnectionReceiver(connection);
|
|
||||||
if (receiver instanceof PrivateConnectionPlugin plugin) {
|
|
||||||
if (notify) {
|
|
||||||
plugin.receivePrivateConnection(connection);
|
|
||||||
}
|
|
||||||
privateConnectionReceiver(connection, plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Plugin findConnectionReceiver(TcpConnection connection) {
|
|
||||||
Plugin receiver = null;
|
|
||||||
for(Plugin plugin : main.getPlugins()) {
|
|
||||||
if (plugin.getIdentifier().equals(connection.getReceiver())) {
|
|
||||||
receiver = plugin;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receiver == null) {
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.RECEIVER_NOT_FOUND,
|
|
||||||
"This peer does not have the plugin: " + connection.getReceiver()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publicConnectionReceiver(TcpConnection connection) {
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
byte[] bytes = connection.receive();
|
|
||||||
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(bis)) {
|
|
||||||
|
|
||||||
Object object = ois.readObject();
|
|
||||||
if (object instanceof Message message) {
|
|
||||||
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
|
|
||||||
|
|
||||||
if (sysMessage == null) {
|
|
||||||
for (Plugin plugin : main.getPlugins()) {
|
|
||||||
plugin.receiveMessage(message);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (sysMessage.getType()) {
|
|
||||||
case CONNECTION_CLOSED, INTERNAL_ERROR -> {
|
|
||||||
connectionManager.closeConnection(connection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case MESSAGE_REJECTED -> {
|
|
||||||
logger.log(Level.SEVERE, "Somehow sent something that was not an Message");
|
|
||||||
}
|
|
||||||
default -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.MESSAGE_REJECTED,
|
|
||||||
"Public messages must be instances of Message"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
SysInfoMessage.sendSystemInfo(
|
|
||||||
connection,
|
|
||||||
SysInfoType.INTERNAL_ERROR,
|
|
||||||
"class Message not found."
|
|
||||||
);
|
|
||||||
logger.log(Level.SEVERE, null, ex);
|
|
||||||
connectionManager.closeConnection(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SocketTimeoutException ex) {
|
|
||||||
if (connection.hasTimedOut()) {
|
|
||||||
connectionManager.closeConnection(connection, "Connection timeout");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
connectionManager.closeConnection(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void privateConnectionReceiver(TcpConnection connection, Plugin plugin) {
|
|
||||||
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
byte[] message = connection.receive();
|
|
||||||
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
|
|
||||||
|
|
||||||
if (sysMessage == null) {
|
|
||||||
privatePlugin.receiveBytes(message);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (sysMessage.getType()) {
|
|
||||||
case CONNECTION_CLOSED, INTERNAL_ERROR -> {
|
|
||||||
privatePlugin.onSocketClosed(connection);
|
|
||||||
connectionManager.closeConnection(connection);
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SocketTimeoutException ex) {
|
|
||||||
if (connection.hasTimedOut()) {
|
|
||||||
connectionManager.closeConnection(connection, "Connection timeout");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
connectionManager.closeConnection(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void systemConnectionReceiver(TcpConnection connection) throws IOException {
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
byte[] message = connection.receive();
|
|
||||||
try (ByteArrayInputStream bis = new ByteArrayInputStream(message);
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(bis)) {
|
|
||||||
|
|
||||||
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
|
|
||||||
if (sysMessage != null) continue;
|
|
||||||
|
|
||||||
//TODO: Ainda não existem mensagens trocadas entre o sistema.
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (SocketTimeoutException ex) {
|
|
||||||
if (connection.hasTimedOut()) return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect(TcpConnection connection, String description) {
|
|
||||||
connectionManager.closeConnection(
|
|
||||||
connection,
|
|
||||||
Objects.requireNonNullElse(description, "No reason provided")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
receive();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
if (serverSocket != null) {
|
|
||||||
try {
|
|
||||||
serverSocket.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionManager.closeAllConnections("The peer must have closed the application or changed ports");
|
|
||||||
|
|
||||||
connectionExecutor.shutdownNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class SysInfoMessage extends Message {
|
|
||||||
|
|
||||||
private final SysInfoType type;
|
|
||||||
private final String message;
|
|
||||||
|
|
||||||
public SysInfoMessage(String message, SysInfoType type) {
|
|
||||||
this.message = message;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SysInfoType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendSystemInfo(TcpConnection connection, SysInfoType type, String description) {
|
|
||||||
try {
|
|
||||||
SysInfoMessage error = new SysInfoMessage(description, type);
|
|
||||||
connection.send(error.toByteArray());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.getLogger(SysInfoMessage.class.getName()).log(Level.WARNING, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SysInfoMessage parseSystemInfo(byte[] bytes) {
|
|
||||||
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
|
||||||
ObjectInputStream ois = new ObjectInputStream(bis)) {
|
|
||||||
|
|
||||||
Object object = ois.readObject();
|
|
||||||
if (object instanceof SysInfoMessage) {
|
|
||||||
return (SysInfoMessage) object;
|
|
||||||
}
|
|
||||||
} catch (IOException | ClassNotFoundException ex) {
|
|
||||||
Logger.getLogger(SysInfoMessage.class.getName()).log(Level.WARNING, null, ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SysInfoMessage parseSystemInfo(Message message) {
|
|
||||||
if (message instanceof SysInfoMessage) {
|
|
||||||
return (SysInfoMessage) message;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
public enum SysInfoType {
|
|
||||||
CONNECTION_REJECTED,
|
|
||||||
RECEIVER_NOT_FOUND,
|
|
||||||
MESSAGE_REJECTED,
|
|
||||||
INTERNAL_ERROR,
|
|
||||||
CONNECTION_CLOSED,
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
package pitiupi.net.tcp;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Representa uma conexão TCP persistente com um peer.
|
|
||||||
*
|
|
||||||
* <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>
|
|
||||||
*
|
|
||||||
* @author Gustavo
|
|
||||||
*/
|
|
||||||
public class TcpConnection {
|
|
||||||
private static final long TIMEOUT = 300_000; // 5 minutos
|
|
||||||
|
|
||||||
private final Socket socket;
|
|
||||||
private final DataInputStream input;
|
|
||||||
private final DataOutputStream output;
|
|
||||||
private final InetAddress address;
|
|
||||||
private String sender;
|
|
||||||
private String receiver;
|
|
||||||
|
|
||||||
private boolean isPrivate;
|
|
||||||
|
|
||||||
private volatile long lastActivity;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void send(byte[] message) throws IOException {
|
|
||||||
output.writeInt(message.length);
|
|
||||||
output.write(message);
|
|
||||||
output.flush();
|
|
||||||
updateLastActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] receive() throws IOException {
|
|
||||||
int length = input.readInt();
|
|
||||||
|
|
||||||
if (length < 0) throw new IOException("Invalid message length.");
|
|
||||||
|
|
||||||
byte[] message = input.readNBytes(length);
|
|
||||||
|
|
||||||
if (message.length != length) {
|
|
||||||
throw new IOException("Connection closed before receiving the complete message.");
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLastActivity();
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
|
||||||
return socket.isClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() throws IOException {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetAddress getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateLastActivity() {
|
|
||||||
lastActivity = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTimeout(int timeout) throws SocketException {
|
|
||||||
socket.setSoTimeout(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasTimedOut() {
|
|
||||||
return System.currentTimeMillis() - lastActivity >= TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getReceiver() {
|
|
||||||
return receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSender() {
|
|
||||||
return sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPrivate() {
|
|
||||||
return isPrivate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSender(String sender) {
|
|
||||||
this.sender = sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setReceiver(String receiver) {
|
|
||||||
this.receiver = receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPrivate(boolean aPrivate) {
|
|
||||||
isPrivate = aPrivate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
*/
|
*/
|
||||||
package pitiupi.plugin;
|
package pitiupi.plugin;
|
||||||
|
|
||||||
|
import javax.swing.JMenu;
|
||||||
import pitiupi.GUI.MainWindow;
|
import pitiupi.GUI.MainWindow;
|
||||||
import pitiupi.net.Message;
|
import pitiupi.net.Message;
|
||||||
import pitiupi.net.tcp.TcpConnection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface base para plugins da aplicação.
|
* Interface base para plugins da aplicação.
|
||||||
|
|
@ -21,7 +21,6 @@ public interface Plugin {
|
||||||
public String getName();
|
public String getName();
|
||||||
public String getAuthor();
|
public String getAuthor();
|
||||||
public String getVersion();
|
public String getVersion();
|
||||||
public String getIdentifier();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inicializa o plugin.
|
* Inicializa o plugin.
|
||||||
|
|
@ -47,6 +46,5 @@ public interface Plugin {
|
||||||
*
|
*
|
||||||
* @param message mensagem serializada recebida.
|
* @param message mensagem serializada recebida.
|
||||||
*/
|
*/
|
||||||
public void receiveMessage(Message message);
|
public void receiveMessage(byte[] message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package pitiupi.plugin;
|
|
||||||
|
|
||||||
import pitiupi.net.tcp.TcpConnection;
|
|
||||||
|
|
||||||
public interface PrivateConnectionPlugin extends Plugin {
|
|
||||||
public void receivePrivateConnection(TcpConnection connection);
|
|
||||||
public void receiveBytes(byte[] bytes);
|
|
||||||
public void onSocketClosed(TcpConnection connection);
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue