modificar o socketTCP para servir de interface com o restante da aplicação, enquanto usa a factory e o manager para criar conexões conforme necessário
This commit is contained in:
parent
0d5a4dbe75
commit
4461772ebf
|
|
@ -0,0 +1,344 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue