Esqueci de adicionar nos commits anteriores
This commit is contained in:
parent
4461772ebf
commit
b610ec20e1
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
package pitiupi.GUI;
|
||||
|
||||
import pitiupi.net.SocketTCP;
|
||||
import pitiupi.net.SocketUDP;
|
||||
import pitiupi.net.tcp.SocketTCP;
|
||||
import pitiupi.net.udp.SocketUDP;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.JMenu;
|
||||
|
|
@ -44,7 +44,9 @@ public class MainWindow extends javax.swing.JFrame {
|
|||
private SocketTCP socketTCP;
|
||||
|
||||
private String userName;
|
||||
private int port;
|
||||
private int port;
|
||||
|
||||
private String pitiupiIdentifier;
|
||||
|
||||
private PluginLoader pluginManager;
|
||||
private final List<Plugin> plugins;
|
||||
|
|
@ -58,6 +60,7 @@ public class MainWindow extends javax.swing.JFrame {
|
|||
public MainWindow(String userName, int port) {
|
||||
this.userName = userName;
|
||||
this.port = port;
|
||||
this.pitiupiIdentifier = "Pitiupi.Alice.2026";
|
||||
this.menuBar = new MenuBar(this);
|
||||
this.plugins = new ArrayList<>();
|
||||
this.initComponents();
|
||||
|
|
@ -126,6 +129,10 @@ public class MainWindow extends javax.swing.JFrame {
|
|||
public String getUserName(){
|
||||
return this.userName;
|
||||
}
|
||||
|
||||
public String getIdentifier(){
|
||||
return this.pitiupiIdentifier;
|
||||
}
|
||||
|
||||
public void addMenu(JMenu menu) {
|
||||
this.menuBar.addPlugin(menu);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
package pitiupi.net;
|
||||
|
||||
/**
|
||||
* Mensagem utilizada para sinalizar a presença de um usuário na rede.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,244 +0,0 @@
|
|||
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) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package pitiupi.net;
|
||||
package pitiupi.net.udp;
|
||||
|
||||
import pitiupi.GUI.MainWindow;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
|
@ -13,6 +13,8 @@ import java.io.ObjectInputStream;
|
|||
import java.net.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import pitiupi.net.HeartbeatMessage;
|
||||
import pitiupi.plugin.Plugin;
|
||||
|
||||
/**
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
*/
|
||||
package pitiupi.plugin;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import pitiupi.GUI.MainWindow;
|
||||
import pitiupi.net.Message;
|
||||
import pitiupi.net.tcp.TcpConnection;
|
||||
|
||||
/**
|
||||
* Interface base para plugins da aplicação.
|
||||
|
|
@ -21,6 +21,7 @@ public interface Plugin {
|
|||
public String getName();
|
||||
public String getAuthor();
|
||||
public String getVersion();
|
||||
public String getIdentifier();
|
||||
|
||||
/**
|
||||
* Inicializa o plugin.
|
||||
|
|
@ -46,5 +47,6 @@ public interface Plugin {
|
|||
*
|
||||
* @param message mensagem serializada recebida.
|
||||
*/
|
||||
public void receiveMessage(byte[] message);
|
||||
public void receiveMessage(Message message);
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue