Compare commits
No commits in common. "feature/unicast" and "main" have entirely different histories.
feature/un
...
main
|
|
@ -5,5 +5,3 @@
|
||||||
/pitiupi.chat/nbproject/private/
|
/pitiupi.chat/nbproject/private/
|
||||||
/pitiupi.screenCast/build/
|
/pitiupi.screenCast/build/
|
||||||
/pitiupi.screenCast/dist/
|
/pitiupi.screenCast/dist/
|
||||||
|
|
||||||
.idea/
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<component name="libraryTable">
|
|
||||||
<library name="pitiupi.chat">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/dist/pitiupi.chat.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/pitiupi.chat.iml" filepath="$PROJECT_DIR$/pitiupi.chat.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#Mon, 17 Aug 2026 14:26:26 -0300
|
#Thu, 16 Jul 2026 15:21:36 -0300
|
||||||
|
|
||||||
|
|
||||||
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.chat=
|
/home/flavio/NetBeansProjects/pitiupi.chat=
|
||||||
|
|
||||||
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi=
|
/home/flavio/NetBeansProjects/pitiupi=
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="JAVA_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
<orderEntry type="module-library">
|
|
||||||
<library>
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$MODULE_DIR$/../pitiupi/dist/pitiupi.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</orderEntry>
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
|
|
@ -91,7 +91,7 @@ public class ChatWindow extends javax.swing.JDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage() {
|
private void sendMessage() {
|
||||||
if (jTextArea1.getText().trim().isEmpty()) {
|
if (jTextArea1.getText().trim().length() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ChatMessage message = new ChatMessage(mainWindow.getUserName(), jTextArea1.getText());
|
ChatMessage message = new ChatMessage(mainWindow.getUserName(), jTextArea1.getText());
|
||||||
|
|
@ -101,7 +101,7 @@ public class ChatWindow extends javax.swing.JDialog {
|
||||||
|
|
||||||
public void sendChatMessage(Message message) {
|
public void sendChatMessage(Message message) {
|
||||||
try {
|
try {
|
||||||
mainWindow.getSocketUDP().sendMulticast(message.toByteArray());
|
mainWindow.getSocket().send(message.toByteArray());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AntConfiguration">
|
|
||||||
<buildFile url="file://$PROJECT_DIR$/build.xml" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK" />
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AutoImportSettings">
|
|
||||||
<option name="autoReloadType" value="SELECTIVE" />
|
|
||||||
</component>
|
|
||||||
<component name="ChangeListManager">
|
|
||||||
<list default="true" id="083e72ce-fa9b-4ddf-a0ef-24105aa3eabb" name="Changes" comment="">
|
|
||||||
<change beforePath="$PROJECT_DIR$/../.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/misc.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../.idea/vcs.xml" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/workspace.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/.idea/misc.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/build/built-jar.properties" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/build/built-jar.properties" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/dist/pitiupi.chat.jar" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/pitiupi.chat.iml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/pitiupi.chat.iml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/build/built-jar.properties" beforeDir="false" afterPath="$PROJECT_DIR$/build/built-jar.properties" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class" beforeDir="false" afterPath="$PROJECT_DIR$/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/dist/pitiupi.screenCast.jar" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pitiupi/screenCast/GUI/ScreenCastWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/pitiupi/screenCast/GUI/ScreenCastWindow.java" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.chat.jar" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.chat.jar" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.screenCast.jar" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.screenCast.jar" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/GUI/MainWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/GUI/MainWindow.java" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/control/HeartbeatManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/control/HeartbeatManager.java" afterDir="false" />
|
|
||||||
</list>
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
||||||
</component>
|
|
||||||
<component name="Git.Settings">
|
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectColorInfo">{
|
|
||||||
"associatedIndex": 7
|
|
||||||
}</component>
|
|
||||||
<component name="ProjectId" id="3Gs0wFaRD4HyhP9VXLUWBeq0qTL" />
|
|
||||||
<component name="ProjectViewState">
|
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
|
||||||
<option name="showLibraryContents" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="PropertiesComponent"><![CDATA[{
|
|
||||||
"keyToString": {
|
|
||||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
|
||||||
"git-widget-placeholder": "feature/unicast",
|
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
|
||||||
"kotlin-language-version-configured": "true",
|
|
||||||
"last_opened_file_path": "C:/Users/extre/OneDrive/Documentos/Faculdade/2026_02/Monitoria/Pitiupi/pitiupi.screenCast",
|
|
||||||
"project.structure.last.edited": "Modules",
|
|
||||||
"project.structure.proportion": "0.15",
|
|
||||||
"project.structure.side.proportion": "0.2",
|
|
||||||
"settings.editor.selected.configurable": "preferences.keymap"
|
|
||||||
}
|
|
||||||
}]]></component>
|
|
||||||
<component name="RunManager">
|
|
||||||
<configuration default="true" type="JetRunConfigurationType">
|
|
||||||
<module name="pitiupi.screenCast" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
|
|
||||||
<module name="pitiupi.screenCast" />
|
|
||||||
<option name="filePath" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration default="true" type="PythonConfigurationType" factoryName="Python">
|
|
||||||
<module name="pitiupi.screenCast" />
|
|
||||||
<option name="ENV_FILES" value="" />
|
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
|
||||||
<option name="PARENT_ENVS" value="true" />
|
|
||||||
<envs>
|
|
||||||
<env name="PYTHONUNBUFFERED" value="1" />
|
|
||||||
</envs>
|
|
||||||
<option name="SDK_HOME" value="" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
|
||||||
<option name="IS_MODULE_SDK" value="false" />
|
|
||||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
||||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
||||||
<option name="SCRIPT_NAME" value="" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
|
||||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
||||||
<option name="EMULATE_TERMINAL" value="false" />
|
|
||||||
<option name="MODULE_MODE" value="false" />
|
|
||||||
<option name="REDIRECT_INPUT" value="false" />
|
|
||||||
<option name="INPUT_FILE" value="" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration default="true" type="Tox" factoryName="Tox">
|
|
||||||
<module name="pitiupi.screenCast" />
|
|
||||||
<option name="ENV_FILES" value="" />
|
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
|
||||||
<option name="PARENT_ENVS" value="true" />
|
|
||||||
<option name="SDK_HOME" value="" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
|
||||||
<option name="IS_MODULE_SDK" value="false" />
|
|
||||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
||||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
<component name="SharedIndexes">
|
|
||||||
<attachedChunks>
|
|
||||||
<set>
|
|
||||||
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.28539.54" />
|
|
||||||
</set>
|
|
||||||
</attachedChunks>
|
|
||||||
</component>
|
|
||||||
<component name="TaskManager">
|
|
||||||
<task active="true" id="Default" summary="Default task">
|
|
||||||
<changelist id="083e72ce-fa9b-4ddf-a0ef-24105aa3eabb" name="Changes" comment="" />
|
|
||||||
<created>1784745206049</created>
|
|
||||||
<option name="number" value="Default" />
|
|
||||||
<option name="presentableId" value="Default" />
|
|
||||||
<updated>1784745206049</updated>
|
|
||||||
</task>
|
|
||||||
<servers />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#Mon, 17 Aug 2026 14:35:40 -0300
|
#Thu, 16 Jul 2026 15:21:39 -0300
|
||||||
|
|
||||||
|
|
||||||
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.screenCast=
|
/home/flavio/NetBeansProjects/pitiupi.screenCast=
|
||||||
|
|
||||||
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi=
|
/home/flavio/NetBeansProjects/pitiupi=
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -98,7 +98,7 @@ public class ScreenCastWindow extends javax.swing.JFrame {
|
||||||
}
|
}
|
||||||
if (msg != null) {
|
if (msg != null) {
|
||||||
try {
|
try {
|
||||||
this.mainWindow.getSocketUDP().sendMulticast(msg);
|
this.mainWindow.getSocket().send(msg);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
System.out.println(ex.toString());
|
System.out.println(ex.toString());
|
||||||
this.compressRatio -= 0.1f;
|
this.compressRatio -= 0.1f;
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1 +0,0 @@
|
||||||
plugin.SocketTest
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,21 +0,0 @@
|
||||||
package GUI;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
|
|
||||||
public class SocketTestMenu extends JMenu {
|
|
||||||
private final SocketTestWindow socketTestWindow;
|
|
||||||
|
|
||||||
public SocketTestMenu(SocketTestWindow socketTestWindow) {
|
|
||||||
this.socketTestWindow = socketTestWindow;
|
|
||||||
this.setText("Chat");
|
|
||||||
JMenuItem menuItem = new JMenuItem();
|
|
||||||
menuItem.setText("Show Chat Window");
|
|
||||||
menuItem.addActionListener(this::showTestWindow);
|
|
||||||
this.add(menuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showTestWindow(ActionEvent ae) {
|
|
||||||
this.socketTestWindow.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
package GUI;
|
|
||||||
|
|
||||||
import net.TestMessage;
|
|
||||||
import pitiupi.GUI.MainWindow;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class SocketTestWindow extends JFrame {
|
|
||||||
private MainWindow mainWindow;
|
|
||||||
private JTextArea receivedMessagesArea;
|
|
||||||
|
|
||||||
private JTextField ipField;
|
|
||||||
private JTextField messageField;
|
|
||||||
|
|
||||||
private JButton tcpButton;
|
|
||||||
private JButton udpButton;
|
|
||||||
private JButton multicastButton;
|
|
||||||
|
|
||||||
public SocketTestWindow(MainWindow mainWindow) {
|
|
||||||
this.mainWindow = mainWindow;
|
|
||||||
initComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initComponents() {
|
|
||||||
setTitle("Socket Test");
|
|
||||||
setSize(700, 500);
|
|
||||||
setLocationRelativeTo(null);
|
|
||||||
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
|
||||||
|
|
||||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
|
||||||
|
|
||||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
|
||||||
|
|
||||||
initReceivedMessagesPanel(mainPanel);
|
|
||||||
initBottomPanel(mainPanel);
|
|
||||||
|
|
||||||
setContentPane(mainPanel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initReceivedMessagesPanel(JPanel mainPanel) {
|
|
||||||
receivedMessagesArea = new JTextArea();
|
|
||||||
receivedMessagesArea.setEditable(false);
|
|
||||||
receivedMessagesArea.setLineWrap(true);
|
|
||||||
receivedMessagesArea.setWrapStyleWord(true);
|
|
||||||
|
|
||||||
JScrollPane messagesScrollPane = new JScrollPane(receivedMessagesArea);
|
|
||||||
|
|
||||||
messagesScrollPane.setBorder(BorderFactory.createTitledBorder("Mensagens recebidas"));
|
|
||||||
|
|
||||||
mainPanel.add(messagesScrollPane, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initBottomPanel(JPanel mainPanel) {
|
|
||||||
ipField = new JTextField();
|
|
||||||
ipField.setPreferredSize(new Dimension(130, 30));
|
|
||||||
|
|
||||||
JPanel ipPanel = new JPanel(new BorderLayout(5, 5));
|
|
||||||
ipPanel.add(new JLabel("IP destino:"), BorderLayout.WEST);
|
|
||||||
ipPanel.add(ipField, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
messageField = new JTextField();
|
|
||||||
|
|
||||||
JPanel bottomPanel = new JPanel(new BorderLayout(10, 10));
|
|
||||||
|
|
||||||
bottomPanel.add(ipPanel, BorderLayout.WEST);
|
|
||||||
bottomPanel.add(messageField, BorderLayout.CENTER);
|
|
||||||
initButtons(bottomPanel);
|
|
||||||
|
|
||||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initButtons(JPanel bottomPanel) {
|
|
||||||
tcpButton = new JButton("TCP");
|
|
||||||
udpButton = new JButton("UDP");
|
|
||||||
multicastButton = new JButton("Multicast");
|
|
||||||
|
|
||||||
tcpButton.addActionListener(e -> sendTCP());
|
|
||||||
udpButton.addActionListener(e -> sendUDP());
|
|
||||||
multicastButton.addActionListener(e -> sendMulticast());
|
|
||||||
|
|
||||||
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
|
|
||||||
|
|
||||||
buttonsPanel.add(tcpButton);
|
|
||||||
buttonsPanel.add(udpButton);
|
|
||||||
buttonsPanel.add(multicastButton);
|
|
||||||
|
|
||||||
bottomPanel.add(buttonsPanel, BorderLayout.EAST);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendTCP() {
|
|
||||||
try {
|
|
||||||
TestMessage msg = createMessage("TCP Unicast");
|
|
||||||
InetAddress ip = InetAddress.getByName(ipField.getText());
|
|
||||||
mainWindow.getSocketTCP().send(msg.toByteArray(), ip);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
public void sendUDP() {
|
|
||||||
try {
|
|
||||||
TestMessage msg = createMessage("UDP Unicast");
|
|
||||||
InetAddress ip = InetAddress.getByName(ipField.getText());
|
|
||||||
mainWindow.getSocketUDP().sendUnicast(msg.toByteArray(), ip);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void sendMulticast() {
|
|
||||||
try {
|
|
||||||
TestMessage msg = createMessage("UDP Multicast");
|
|
||||||
mainWindow.getSocketUDP().sendMulticast(msg.toByteArray());
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private TestMessage createMessage(String tipoMensagem) {
|
|
||||||
try {
|
|
||||||
String nome = mainWindow.getUserName();
|
|
||||||
String ip = InetAddress.getLocalHost().getHostAddress();
|
|
||||||
String message = messageField.getText();
|
|
||||||
return new TestMessage(nome, ip, tipoMensagem, message);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void receiveMessage(TestMessage msg) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
String mensagem = String.format(
|
|
||||||
"[%s] %s (%s): %s%n",
|
|
||||||
msg.getTipoMensagem(),
|
|
||||||
msg.getNome(),
|
|
||||||
msg.getIp(),
|
|
||||||
msg.getMessage()
|
|
||||||
);
|
|
||||||
|
|
||||||
receivedMessagesArea.append(mensagem);
|
|
||||||
|
|
||||||
receivedMessagesArea.setCaretPosition(
|
|
||||||
receivedMessagesArea.getDocument().getLength()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
plugin.SocketTest
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
package net;
|
|
||||||
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
|
|
||||||
public class TestMessage extends Message {
|
|
||||||
private String nome;
|
|
||||||
private String ip;
|
|
||||||
private String tipoMensagem;
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
public TestMessage(String nome, String ip, String tipoMensagem, String message) {
|
|
||||||
this.nome = nome;
|
|
||||||
this.ip = ip;
|
|
||||||
this.tipoMensagem = tipoMensagem;
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestMessage(){}
|
|
||||||
|
|
||||||
public String getNome() {
|
|
||||||
return nome;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIp() {
|
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTipoMensagem() {
|
|
||||||
return tipoMensagem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
package plugin;
|
|
||||||
|
|
||||||
import GUI.SocketTestMenu;
|
|
||||||
import GUI.SocketTestWindow;
|
|
||||||
import net.TestMessage;
|
|
||||||
import pitiupi.GUI.MainWindow;
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
import pitiupi.plugin.Plugin;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
|
|
||||||
public class SocketTest implements Plugin{
|
|
||||||
SocketTestWindow socketTestWindow;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Test Socket";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAuthor() {
|
|
||||||
return "Gustavo Henrique";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getVersion() {
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createPlugin(MainWindow mainWindow) {
|
|
||||||
this.socketTestWindow = new SocketTestWindow(mainWindow);
|
|
||||||
mainWindow.addMenu(new SocketTestMenu(socketTestWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Message getMessage() {
|
|
||||||
return new TestMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receiveMessage(byte[] bytes) {
|
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
|
||||||
ObjectInputStream ois;
|
|
||||||
try {
|
|
||||||
ois = new ObjectInputStream(bis);
|
|
||||||
if (ois.readObject() instanceof TestMessage msg) {
|
|
||||||
this.socketTestWindow.receiveMessage(msg);
|
|
||||||
}
|
|
||||||
} catch (IOException | ClassNotFoundException ex) {
|
|
||||||
System.out.println(ex.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,5 +8,3 @@
|
||||||
|
|
||||||
# JARs gerados
|
# JARs gerados
|
||||||
*.jar
|
*.jar
|
||||||
|
|
||||||
.idea/
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<component name="libraryTable">
|
|
||||||
<library name="pitiupi">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/dist/pitiupi.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<component name="libraryTable">
|
|
||||||
<library name="plugins">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/plugins/pitiupi.chat.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/plugins/pitiupi.screenCast.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/pitiupi.iml" filepath="$PROJECT_DIR$/pitiupi.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,547 +0,0 @@
|
||||||
# Visão geral
|
|
||||||
|
|
||||||
O `Pitiupi` é uma plataforma P2P baseada em uma arquitetura de plugins,
|
|
||||||
desenvolvida para permitir a criação de aplicações distribuídas sobre uma
|
|
||||||
infraestrutura comum de comunicação em rede.
|
|
||||||
|
|
||||||
A aplicação fornece mecanismos de descoberta de usuários, comunicação entre
|
|
||||||
peers, troca de mensagens serializadas e integração de extensões por meio de
|
|
||||||
plugins. A comunicação de rede utiliza dois protocolos de transporte: UDP,
|
|
||||||
empregado principalmente na descoberta e comunicação multicast entre as
|
|
||||||
instâncias, e TCP, utilizado para comunicação direta e persistente entre peers.
|
|
||||||
|
|
||||||
Dessa forma, novos comportamentos podem ser adicionados por meio de plugins
|
|
||||||
sem modificar o núcleo da aplicação.
|
|
||||||
|
|
||||||
O projeto foi desenvolvido como suporte às práticas da disciplina de Redes de
|
|
||||||
Computadores, servindo também como base para experimentação de aplicações
|
|
||||||
distribuídas e desenvolvimento de projetos acadêmicos.
|
|
||||||
|
|
||||||
# Arquitetura geral
|
|
||||||
|
|
||||||
A aplicação é dividida em três grupos principais:
|
|
||||||
|
|
||||||
- **Núcleo da aplicação**: A `MainWindow` atua como ponto central da aplicação,
|
|
||||||
sendo responsável por inicializar e integrar esses componentes durante a
|
|
||||||
execução.
|
|
||||||
- **Sistema de plugins**: responsável por adicionar funcionalidades sem
|
|
||||||
alterar o núcleo da aplicação.
|
|
||||||
- **Comunicação de rede**: responsável pela troca de mensagens entre
|
|
||||||
instâncias da aplicação.
|
|
||||||
|
|
||||||
## Inicialização da aplicação
|
|
||||||
|
|
||||||
1. A MainWindow é criada.
|
|
||||||
2. O SocketUDP é inicializado, associa-se à porta configurada e ingressa no
|
|
||||||
grupo multicast.
|
|
||||||
3. O SocketTCP é inicializado e cria um ServerSocket na porta da aplicação
|
|
||||||
para aceitar conexões TCP.
|
|
||||||
4. Os mecanismos de comunicação iniciam seus respectivos processos de recepção.
|
|
||||||
5. O HeartbeatManager é criado e inicia o envio periódico de heartbeats.
|
|
||||||
6. O painel de usuários online é registrado como listener do HeartbeatManager.
|
|
||||||
7. O PluginLoader procura e carrega os plugins disponíveis.
|
|
||||||
|
|
||||||
# Sistema de comunicação
|
|
||||||
|
|
||||||
A comunicação entre as instâncias da aplicação é realizada por meio de dois
|
|
||||||
mecanismos complementares:
|
|
||||||
|
|
||||||
- **UDP multicast/unicast**, implementado por `SocketUDP`
|
|
||||||
- **TCP**, implementado por `SocketTCP` e `TcpConnection`
|
|
||||||
|
|
||||||
Ambos os mecanismos utilizam mensagens serializadas em `byte[]`, permitindo que
|
|
||||||
os plugins permaneçam independentes dos detalhes do protocolo de transporte.
|
|
||||||
|
|
||||||
## Comunicação UDP
|
|
||||||
|
|
||||||
A classe `SocketUDP` é responsável pela comunicação UDP da aplicação.
|
|
||||||
|
|
||||||
Ela utiliza um `MulticastSocket` associado à porta configurada pela aplicação e
|
|
||||||
ingressa no grupo multicast `224.0.0.3`. Por ser baseada em UDP, a comunicação não
|
|
||||||
estabelece uma conexão permanente entre os peers.
|
|
||||||
|
|
||||||
A classe estende `Thread`, mantendo um processo contínuo de recepção de datagramas
|
|
||||||
enquanto a aplicação está em execução.
|
|
||||||
|
|
||||||
O SocketUDP suporta dois modos de envio:
|
|
||||||
|
|
||||||
- **multicast**, destinado ao grupo de peers
|
|
||||||
- **unicast**, destinado diretamente ao endereço IP de um peer específico
|
|
||||||
|
|
||||||
### Inicialização
|
|
||||||
|
|
||||||
Durante sua inicialização, o `SocketUDP`:
|
|
||||||
|
|
||||||
1. obtém o endereço do grupo multicast;
|
|
||||||
2. cria um `MulticastSocket` utilizando a porta configurada na `MainWindow`;
|
|
||||||
3. configura os buffers de envio e recepção;
|
|
||||||
4. habilita o reuso do endereço;
|
|
||||||
5. ingressa no grupo multicast.
|
|
||||||
|
|
||||||
O tamanho dos buffers de comunicação é configurado para `256000` bytes.
|
|
||||||
|
|
||||||
### Envio de mensagens UDP
|
|
||||||
|
|
||||||
O `SocketUDP` suporta dois modos de envio de mensagens: multicast e unicast, ambos
|
|
||||||
baseados no mesmo mecanismo interno de criação e envio de DatagramPacket.
|
|
||||||
|
|
||||||
O método `sendMulticast()` envia uma mensagem para o grupo multicast da aplicação.
|
|
||||||
Nesse caso, o `DatagramPacket` é direcionado ao endereço multicast configurado,
|
|
||||||
permitindo que todas as instâncias participantes do grupo recebam a mensagem
|
|
||||||
simultaneamente.
|
|
||||||
|
|
||||||
Já o método `sendUnicast()` permite enviar uma mensagem diretamente para um peer
|
|
||||||
específico. Nesse caso, o `DatagramPacket` é direcionado ao endereço IP informado pelo
|
|
||||||
chamador, mantendo a mesma porta utilizada pela aplicação.
|
|
||||||
|
|
||||||
Ambos os métodos são, na prática, abstrações de um único mecanismo interno de envio,
|
|
||||||
que utiliza o mesmo processo de criação e envio de `DatagramPacket` por baixo dos
|
|
||||||
panos. A diferença entre eles está apenas no endereço de destino: enquanto o multicast
|
|
||||||
utiliza o grupo compartilhado, o unicast direciona o pacote a um IP específico.
|
|
||||||
|
|
||||||
### Recepção de mensagens
|
|
||||||
|
|
||||||
O método `receive()` mantém um loop aguardando novos datagramas.
|
|
||||||
|
|
||||||
Ao receber uma mensagem, o `SocketUDP` inicialmente tenta identificar se os dados
|
|
||||||
correspondem a uma `HeartbeatMessage`.
|
|
||||||
|
|
||||||
O processamento segue o seguinte fluxo:
|
|
||||||
|
|
||||||
1. O datagrama é recebido pelo `MulticastSocket`.
|
|
||||||
2. Os dados recebidos são analisados pelo método `tryParseHeartbeat()`.
|
|
||||||
3. Caso seja identificada uma `HeartbeatMessage`, ela é encaminhada ao
|
|
||||||
`HeartbeatManager`, juntamente com o endereço IP de origem.
|
|
||||||
4. Caso não seja um heartbeat, a mensagem é encaminhada para todos os plugins
|
|
||||||
carregados.
|
|
||||||
5. Cada plugin decide se a mensagem recebida pertence à sua funcionalidade.
|
|
||||||
|
|
||||||
Essa separação mantém as mensagens de infraestrutura, como heartbeat, sob
|
|
||||||
responsabilidade do núcleo da aplicação, enquanto as mensagens específicas das
|
|
||||||
funcionalidades são processadas pelos plugins.
|
|
||||||
|
|
||||||
### Identificação de mensagens de heartbeat
|
|
||||||
|
|
||||||
O método `tryParseHeartbeat()` tenta desserializar os dados recebidos e verificar se o
|
|
||||||
objeto resultante é uma instância de `HeartbeatMessage`.
|
|
||||||
|
|
||||||
Caso a mensagem não possa ser desserializada ou não corresponda ao tipo esperado, o
|
|
||||||
método retorna `null`. Nesse caso, a mensagem é tratada como uma mensagem destinada aos
|
|
||||||
plugins.
|
|
||||||
|
|
||||||
### Encerramento
|
|
||||||
|
|
||||||
O método `close()` interrompe a execução do processo de recepção, remove a aplicação
|
|
||||||
do grupo multicast e fecha o `MulticastSocket`.
|
|
||||||
|
|
||||||
A variável `running` é utilizada para sinalizar que o processo de recepção deve ser
|
|
||||||
encerrado.
|
|
||||||
|
|
||||||
## Comunicação TCP
|
|
||||||
|
|
||||||
A comunicação TCP é implementada pela classe `SocketTCP`.
|
|
||||||
|
|
||||||
Diferentemente do UDP, o TCP estabelece conexões entre dois peers. Essas conexões
|
|
||||||
são mantidas enquanto forem necessárias e podem ser reutilizadas para o envio de
|
|
||||||
múltiplas mensagens.
|
|
||||||
|
|
||||||
A arquitetura utiliza uma conexão TCP persistente por endereço de peer.
|
|
||||||
|
|
||||||
O SocketTCP possui um mapa de conexões:
|
|
||||||
|
|
||||||
`InetAddress` -> `TcpConnection`
|
|
||||||
|
|
||||||
Esse mapa permite localizar rapidamente uma conexão existente para determinado peer e
|
|
||||||
reutilizá-la durante novos envios.
|
|
||||||
|
|
||||||
### SocketTCP
|
|
||||||
|
|
||||||
A classe `SocketTCP` estende `Thread` e implementa `PeerListener`.
|
|
||||||
|
|
||||||
Ao ser criada, ela inicializa um `ServerSocket` utilizando a porta configurada na
|
|
||||||
aplicação.
|
|
||||||
|
|
||||||
O servidor TCP permanece aguardando novas conexões enquanto a aplicação estiver
|
|
||||||
em execução.
|
|
||||||
|
|
||||||
### Envio de mensagens TCP
|
|
||||||
|
|
||||||
O método `send()` recebe uma mensagem serializada e o endereço IP do peer destinatário.
|
|
||||||
|
|
||||||
Antes de enviar a mensagem, o `SocketTCP` verifica se já existe uma conexão válida com
|
|
||||||
o peer.
|
|
||||||
|
|
||||||
O comportamento é:
|
|
||||||
|
|
||||||
1. Procurar uma conexão existente no mapa de conexões.
|
|
||||||
2. Verificar se a conexão está fechada.
|
|
||||||
3. Caso não exista uma conexão válida, criar um novo Socket TCP para o endereço do peer.
|
|
||||||
4. Encapsular o socket em uma `TcpConnection`.
|
|
||||||
5. Registrar a conexão no mapa.
|
|
||||||
6. Iniciar uma tarefa de recepção para essa conexão.
|
|
||||||
7. Enviar a mensagem pela conexão.
|
|
||||||
|
|
||||||
Assim, uma conexão TCP existente pode ser reutilizada para várias mensagens, evitando
|
|
||||||
a criação de um novo socket para cada envio.
|
|
||||||
|
|
||||||
### Recepção de conexões
|
|
||||||
|
|
||||||
O método `receive()` permanece aguardando novas conexões através de
|
|
||||||
`ServerSocket.accept()`.
|
|
||||||
|
|
||||||
Quando uma conexão é aceita:
|
|
||||||
|
|
||||||
O endereço IP do peer é obtido.
|
|
||||||
Um objeto `TcpConnection` é criado para encapsular o socket.
|
|
||||||
A conexão é registrada no mapa de conexões.
|
|
||||||
Caso já exista uma conexão associada ao mesmo endereço, a conexão anterior é encerrada.
|
|
||||||
Uma tarefa independente de recepção é criada para a nova conexão.
|
|
||||||
|
|
||||||
Esse modelo permite que o servidor aceite múltiplas conexões sem bloquear o
|
|
||||||
processamento das conexões já existentes.
|
|
||||||
|
|
||||||
### Recepção concorrente
|
|
||||||
|
|
||||||
A recepção das mensagens TCP é realizada de maneira concorrente por meio de um
|
|
||||||
`ExecutorService`.
|
|
||||||
|
|
||||||
Cada conexão possui uma tarefa própria responsável por receber continuamente as
|
|
||||||
mensagens.
|
|
||||||
|
|
||||||
O fluxo de recepção é:
|
|
||||||
|
|
||||||
```
|
|
||||||
SocketTCP
|
|
||||||
|
|
|
||||||
+-- Peer A → TcpConnection → tarefa de recepção
|
|
||||||
|
|
|
||||||
+-- Peer B → TcpConnection → tarefa de recepção
|
|
||||||
|
|
|
||||||
+-- Peer C → TcpConnection → tarefa de recepção
|
|
||||||
```
|
|
||||||
|
|
||||||
Dessa forma, uma conexão bloqueada ou aguardando dados não impede que outras conexões
|
|
||||||
continuem sendo processadas.
|
|
||||||
|
|
||||||
Quando uma mensagem é recebida por uma `TcpConnection`, ela é encaminhada para todos os
|
|
||||||
plugins registrados na `MainWindow`.
|
|
||||||
|
|
||||||
O plugin responsável pela funcionalidade deve identificar o tipo da mensagem e
|
|
||||||
realizar o processamento correspondente.
|
|
||||||
|
|
||||||
### TcpConnection
|
|
||||||
|
|
||||||
A classe TcpConnection encapsula uma conexão TCP individual entre dois peers.
|
|
||||||
|
|
||||||
Ela mantém:
|
|
||||||
|
|
||||||
o Socket utilizado na conexão;
|
|
||||||
um DataInputStream para recepção;
|
|
||||||
um DataOutputStream para envio.
|
|
||||||
|
|
||||||
Além de encapsular esses recursos, a classe é responsável pelo framing das mensagens.
|
|
||||||
|
|
||||||
### Framing das mensagens TCP
|
|
||||||
|
|
||||||
A classe `TcpConnection` encapsula uma conexão TCP individual entre dois peers.
|
|
||||||
|
|
||||||
Ela mantém:
|
|
||||||
|
|
||||||
1. o `Socket` utilizado na conexão;
|
|
||||||
2. um `DataInputStream` para recepção;
|
|
||||||
3. um `DataOutputStream` para envio.
|
|
||||||
|
|
||||||
Além de encapsular esses recursos, a classe é responsável pelo framing das mensagens.
|
|
||||||
|
|
||||||
Framing das mensagens TCP
|
|
||||||
|
|
||||||
O protocolo TCP fornece um fluxo contínuo de bytes e não preserva os limites das
|
|
||||||
mensagens enviadas pela aplicação.
|
|
||||||
|
|
||||||
Por esse motivo, é adicionado o tamanho da mensagem antes do conteúdo.
|
|
||||||
|
|
||||||
O formato utilizado é:
|
|
||||||
|
|
||||||
```
|
|
||||||
+----------------------+----------------------+
|
|
||||||
| tamanho da mensagem | dados da mensagem |
|
|
||||||
+----------------------+----------------------+
|
|
||||||
4 bytes N bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
O tamanho é enviado como um `int`, seguido pelos bytes da mensagem.
|
|
||||||
|
|
||||||
No envio, o método `TcpConnection.send()`:
|
|
||||||
|
|
||||||
1. obtém o tamanho do vetor de bytes;
|
|
||||||
2. escreve o tamanho no `DataOutputStream`;
|
|
||||||
3. escreve os dados da mensagem;
|
|
||||||
4. realiza `flush()` no stream.
|
|
||||||
|
|
||||||
O acesso ao stream de saída é sincronizado para evitar que envios concorrentes
|
|
||||||
misturem seus dados.
|
|
||||||
|
|
||||||
Na recepção, o método `receive()`:
|
|
||||||
|
|
||||||
1. lê o tamanho da mensagem;
|
|
||||||
2. verifica se o tamanho é válido;
|
|
||||||
3. lê a quantidade correspondente de bytes;
|
|
||||||
4. retorna o vetor de bytes completo.
|
|
||||||
|
|
||||||
Esse mecanismo permite que várias mensagens sejam transmitidas pela mesma conexão
|
|
||||||
TCP sem que o receptor perca a delimitação entre elas.
|
|
||||||
|
|
||||||
### Integração entre TCP e gerenciamento de peers
|
|
||||||
|
|
||||||
O `SocketTCP` implementa a interface PeerListener para acompanhar as alterações na
|
|
||||||
lista de peers ativos.
|
|
||||||
|
|
||||||
O `HeartbeatManager` mantém a lista de peers atualmente detectados na rede. Quando
|
|
||||||
essa lista é alterada, o SocketTCP recebe a notificação por meio do método
|
|
||||||
`onPeersChanged()`.
|
|
||||||
|
|
||||||
O método obtém os endereços IP dos peers atualmente ativos e compara esses endereços
|
|
||||||
com as conexões TCP existentes.
|
|
||||||
|
|
||||||
Quando uma conexão pertence a um peer que não está mais ativo:
|
|
||||||
|
|
||||||
a conexão é removida do mapa;
|
|
||||||
a conexão TCP é encerrada;
|
|
||||||
os recursos associados são liberados.
|
|
||||||
|
|
||||||
Esse mecanismo impede que conexões TCP permaneçam abertas indefinidamente para peers
|
|
||||||
que já deixaram de participar da rede.
|
|
||||||
|
|
||||||
O fluxo de integração pode ser representado da seguinte forma:
|
|
||||||
```
|
|
||||||
HeartbeatManager
|
|
||||||
|
|
|
||||||
| peers ativos alterados
|
|
||||||
|
|
|
||||||
v
|
|
||||||
SocketTCP.onPeersChanged()
|
|
||||||
|
|
|
||||||
| compara peers ativos
|
|
||||||
|
|
|
||||||
v
|
|
||||||
+---- peer ativo ------> mantém conexão
|
|
||||||
|
|
|
||||||
+---- peer inativo ----> fecha conexão
|
|
||||||
```
|
|
||||||
|
|
||||||
### Message
|
|
||||||
|
|
||||||
A classe `Message` representa a estrutura base das mensagens trocadas pela aplicação.
|
|
||||||
|
|
||||||
Todas as mensagens utilizadas pelo sistema devem herdar dessa classe. Ela implementa
|
|
||||||
`Serializable`, permitindo que objetos de mensagem sejam convertidos em vetores de
|
|
||||||
bytes para transmissão pela rede.
|
|
||||||
|
|
||||||
A serialização é realizada pelo método `toByteArray()`, que transforma uma instância
|
|
||||||
da mensagem em uma representação binária enviada pelos mecanismos de comunicação.
|
|
||||||
|
|
||||||
As mensagens específicas devem estender essa classe, adicionando os atributos e
|
|
||||||
comportamentos necessários para cada funcionalidade.
|
|
||||||
|
|
||||||
A camada de transporte não depende do conteúdo específico das mensagens. Tanto o
|
|
||||||
UDP quanto o TCP recebem mensagens serializadas como `byte[]`.
|
|
||||||
|
|
||||||
### Heartbeat
|
|
||||||
|
|
||||||
O mecanismo de heartbeat é utilizado para identificar quais usuários estão ativos
|
|
||||||
na rede.
|
|
||||||
|
|
||||||
A aplicação envia periodicamente uma `HeartbeatMessage` contendo informações do usuário,
|
|
||||||
um identificador único da instância da aplicação e um timestamp.
|
|
||||||
|
|
||||||
Essas mensagens são transmitidas utilizando a comunicação UDP multicast.
|
|
||||||
|
|
||||||
Quando outra instância recebe uma mensagem de heartbeat, o `SocketUDP` identifica
|
|
||||||
o tipo da mensagem e encaminha o objeto ao `HeartbeatManager`, juntamente com o endereço
|
|
||||||
IP de origem.
|
|
||||||
|
|
||||||
O `HeartbeatManager` registra ou atualiza o peer correspondente, utilizando o
|
|
||||||
identificador da instância para diferenciá-lo das demais.
|
|
||||||
|
|
||||||
Um peer é considerado inativo quando permanece mais de 15 segundos sem receber um novo
|
|
||||||
heartbeat. O `HeartbeatManager` então remove o peer da lista e notifica todos os
|
|
||||||
componentes registrados como listeners de mudanças de peers.
|
|
||||||
|
|
||||||
Esses listeners podem incluir diferentes partes da aplicação, como componentes
|
|
||||||
da interface gráfica, mecanismos de comunicação (como o `SocketTCP`) e também plugins
|
|
||||||
que desejem reagir à presença ou ausência de usuários na rede.
|
|
||||||
|
|
||||||
Dessa forma, o sistema permite que tanto o núcleo da aplicação quanto extensões
|
|
||||||
externas sejam notificados sobre alterações no estado da rede, mantendo a arquitetura
|
|
||||||
flexível e extensível.
|
|
||||||
|
|
||||||
O fluxo completo é:
|
|
||||||
|
|
||||||
```
|
|
||||||
HeartbeatManager
|
|
||||||
|
|
|
||||||
| cria HeartbeatMessage
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Message.toByteArray()
|
|
||||||
|
|
|
||||||
v
|
|
||||||
SocketUDP.sendMulticast()
|
|
||||||
|
|
|
||||||
v
|
|
||||||
UDP Multicast
|
|
||||||
|
|
|
||||||
v
|
|
||||||
SocketUDP.receive()
|
|
||||||
|
|
|
||||||
v
|
|
||||||
HeartbeatManager.receiveHeartbeat()
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Lista de peers ativos
|
|
||||||
|
|
|
||||||
+----> Interface de usuários online
|
|
||||||
|
|
|
||||||
+----> SocketTCP (PeerListener)
|
|
||||||
|
|
|
||||||
+----> Plugins (PeerListener)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sistema de plugins
|
|
||||||
|
|
||||||
O sistema de plugins é responsável por permitir a extensão das funcionalidades da
|
|
||||||
aplicação de forma modular, sem a necessidade de alterações no núcleo do sistema.
|
|
||||||
Ele define um contrato comum que deve ser seguido por todas as extensões, garantindo
|
|
||||||
que possam ser carregadas dinamicamente e integradas à comunicação e à interface da
|
|
||||||
aplicação.
|
|
||||||
|
|
||||||
### Interface Plugin
|
|
||||||
|
|
||||||
A interface `Plugin` define o contrato que deve ser implementado por qualquer
|
|
||||||
extensão da aplicação.
|
|
||||||
|
|
||||||
Um plugin deve fornecer informações básicas de identificação (`name`,
|
|
||||||
`author` e `version`), inicializar seu estado através de `createPlugin()` e
|
|
||||||
participar do sistema de comunicação utilizando mensagens derivadas de
|
|
||||||
`Message`.
|
|
||||||
|
|
||||||
Os principais métodos são:
|
|
||||||
|
|
||||||
#### Identificação
|
|
||||||
|
|
||||||
Os métodos:
|
|
||||||
- `getName()`
|
|
||||||
- `getAuthor()`
|
|
||||||
- `getVersion()`
|
|
||||||
|
|
||||||
fornecem informações utilizadas pela aplicação para identificar o plugin.
|
|
||||||
|
|
||||||
#### Inicialização
|
|
||||||
|
|
||||||
`createPlugin(MainWindow window)`
|
|
||||||
|
|
||||||
É chamado após o carregamento do plugin e permite que ele registre seus
|
|
||||||
componentes na aplicação, como menus ou elementos de interface.
|
|
||||||
|
|
||||||
#### Comunicação
|
|
||||||
|
|
||||||
`getMessage()`
|
|
||||||
|
|
||||||
Deve retornar uma mensagem utilizada pelo plugin para comunicação com outras
|
|
||||||
instâncias da aplicação.
|
|
||||||
|
|
||||||
`receiveMessage(byte[] message)`
|
|
||||||
|
|
||||||
É chamado pela aplicação sempre que uma mensagem é recebida. Cada plugin deve
|
|
||||||
verificar se a mensagem pertence ao seu tipo e realizar o processamento
|
|
||||||
correspondente.
|
|
||||||
|
|
||||||
### Descoberta de plugins
|
|
||||||
|
|
||||||
Para serem reconhecidos, os plugins devem possuir os arquivos de configuração
|
|
||||||
do Java Service Provider Interface (SPI):
|
|
||||||
|
|
||||||
```
|
|
||||||
MeusPlugins
|
|
||||||
|
|
|
||||||
+---META-INF
|
|
||||||
|
|
|
||||||
+---services
|
|
||||||
|
|
|
||||||
+---pitiupi.plugin.Plugin
|
|
||||||
```
|
|
||||||
|
|
||||||
O arquivo `pitiupi.plugin.Plugin` deve conter o nome completo das classes que
|
|
||||||
implementam a interface Plugin:
|
|
||||||
|
|
||||||
```
|
|
||||||
MeusPlugins.plugins.NomeDaClasse
|
|
||||||
```
|
|
||||||
|
|
||||||
Para serem carregados, os plugins devem estar empacotados como arquivos JAR
|
|
||||||
e colocados no diretório plugins da aplicação.
|
|
||||||
|
|
||||||
### Carregamento de plugins
|
|
||||||
|
|
||||||
Ao iniciar a aplicação, o `PluginLoader` executa os seguintes passos:
|
|
||||||
|
|
||||||
1. Procura arquivos `.jar` dentro do diretório `plugins`.
|
|
||||||
2. Cria um `URLClassLoader` contendo os JARs encontrados.
|
|
||||||
3. Utiliza `ServiceLoader` para localizar classes que implementam
|
|
||||||
`Plugin`.
|
|
||||||
4. Instancia cada plugin encontrado.
|
|
||||||
5. Chama o método `createPlugin(MainWindow)`.
|
|
||||||
6. Registra o plugin na lista de plugins ativos da aplicação.
|
|
||||||
|
|
||||||
## Interface gráfica
|
|
||||||
|
|
||||||
A interface gráfica da aplicação é baseada em Swing e é centralizada na
|
|
||||||
classe `MainWindow`.
|
|
||||||
|
|
||||||
Durante a inicialização, cada plugin recebe uma referência para a
|
|
||||||
`MainWindow` através do método `createPlugin(MainWindow)`.
|
|
||||||
|
|
||||||
Essa referência permite integrar componentes gráficos à aplicação, sendo a
|
|
||||||
principal forma de extensão a adição de menus à barra de menus:
|
|
||||||
|
|
||||||
```
|
|
||||||
JMenu menu = new JMenu("Meu Plugin");
|
|
||||||
window.addMenu(menu);
|
|
||||||
```
|
|
||||||
|
|
||||||
Visão geral da comunicação
|
|
||||||
|
|
||||||
A arquitetura de comunicação pode ser resumida da seguinte maneira:
|
|
||||||
```
|
|
||||||
+------------------+
|
|
||||||
| MainWindow |
|
|
||||||
+--------+---------+
|
|
||||||
|
|
|
||||||
+--------------------+--------------------+
|
|
||||||
| | |
|
|
||||||
v v v
|
|
||||||
HeartbeatManager SocketUDP SocketTCP
|
|
||||||
| | |
|
|
||||||
| | |
|
|
||||||
v v v
|
|
||||||
Heartbeat UDP Multicast TCP Connections
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
+---------+----------+
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Plugins
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Message
|
|
||||||
```
|
|
||||||
|
|
||||||
O UDP multicast fornece a infraestrutura de descoberta e manutenção da presença dos
|
|
||||||
peers, além de permitir comunicação multicast e unicast.
|
|
||||||
|
|
||||||
O TCP fornece canais diretos e persistentes entre peers, com uma conexão reutilizável
|
|
||||||
por endereço.
|
|
||||||
|
|
||||||
Os dois mecanismos encaminham as mensagens destinadas às funcionalidades da aplicação
|
|
||||||
para os plugins, mantendo separadas as responsabilidades de transporte, infraestrutura
|
|
||||||
e lógica específica de cada extensão.
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# Como criar plugins para o Share my Sheet
|
|
||||||
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="JAVA_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,72 +0,0 @@
|
||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package pitiupi.GUI;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Janela que exibe informações sobre a aplicação, incluindo versão,
|
|
||||||
* descrição e autores.
|
|
||||||
*
|
|
||||||
* @author gustavo
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class About extends javax.swing.JFrame {
|
|
||||||
public About () {
|
|
||||||
this.setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE);
|
|
||||||
initComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initComponents() {
|
|
||||||
this.setTitle("About");
|
|
||||||
this.setBounds(new java.awt.Rectangle(0, 0, 800, 600));
|
|
||||||
this.getContentPane().setLayout(new java.awt.BorderLayout(5, 5));
|
|
||||||
this.setPreferredSize(new java.awt.Dimension(800, 600));
|
|
||||||
|
|
||||||
JScrollPane scroll = getJScrollPane();
|
|
||||||
|
|
||||||
this.add(scroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JScrollPane getJScrollPane() {
|
|
||||||
JEditorPane texto = new JEditorPane();
|
|
||||||
texto.setContentType("text/html");
|
|
||||||
texto.setEditable(false);
|
|
||||||
|
|
||||||
texto.setText("""
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1>Share My Sheet</h1>
|
|
||||||
\s
|
|
||||||
<p>
|
|
||||||
<b>Versão:</b> 1.0
|
|
||||||
</p>
|
|
||||||
\s
|
|
||||||
<p>
|
|
||||||
Plataforma P2P baseada em plugins para o desenvolvimento de aplicações
|
|
||||||
distribuídas. Criada para apoiar as práticas da disciplina de Redes de
|
|
||||||
Computadores e servir como base para projetos acadêmicos e TCCs.
|
|
||||||
\s
|
|
||||||
</p>
|
|
||||||
\s
|
|
||||||
<p>
|
|
||||||
<b>Autores:</b><br>
|
|
||||||
Flávio<br>
|
|
||||||
Tony<br>
|
|
||||||
Gustavo Henrique
|
|
||||||
</p>
|
|
||||||
\s
|
|
||||||
<p>
|
|
||||||
Universidade Federal de São João del-Rei (UFSJ)
|
|
||||||
</p>
|
|
||||||
\s
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
\s""");
|
|
||||||
|
|
||||||
return new JScrollPane(texto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -16,10 +16,6 @@ import javax.swing.JSpinner;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Janela de preferencias da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Permite ao usuário informar o nome utilizado na rede e a porta
|
|
||||||
* multicast que será utilizada pela comunicação.</p>
|
|
||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
* @author tony
|
* @author tony
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package pitiupi.GUI;
|
package pitiupi.GUI;
|
||||||
|
|
||||||
import pitiupi.net.SocketTCP;
|
import pitiupi.net.Socket;
|
||||||
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;
|
||||||
|
|
@ -15,18 +14,9 @@ import pitiupi.plugin.Plugin;
|
||||||
import pitiupi.control.HeartbeatManager;
|
import pitiupi.control.HeartbeatManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Janela principal da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Responsável por inicializar a interface gráfica e coordenar os
|
|
||||||
* principais componentes da aplicação, incluindo comunicação de rede,
|
|
||||||
* gerenciamento de heartbeat e carregamento de plugins.</p>
|
|
||||||
*
|
|
||||||
* <p>A instância desta classe é compartilhada com os plugins para permitir
|
|
||||||
* integração com a aplicação principal.</p>
|
|
||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
* @author tony
|
* @author tony
|
||||||
* @author Gustavo
|
|
||||||
*/
|
*/
|
||||||
public class MainWindow extends javax.swing.JFrame {
|
public class MainWindow extends javax.swing.JFrame {
|
||||||
|
|
||||||
|
|
@ -40,8 +30,7 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
private OnlineUsersPanel onlineUsersPanel;
|
private OnlineUsersPanel onlineUsersPanel;
|
||||||
|
|
||||||
|
|
||||||
private SocketUDP socketUDP;
|
private Socket socket;
|
||||||
private SocketTCP socketTCP;
|
|
||||||
|
|
||||||
private String userName;
|
private String userName;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
@ -49,12 +38,7 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
private PluginLoader pluginManager;
|
private PluginLoader pluginManager;
|
||||||
private final List<Plugin> plugins;
|
private final List<Plugin> plugins;
|
||||||
|
|
||||||
/**
|
|
||||||
* Cria a janela principal da aplicação.
|
|
||||||
*
|
|
||||||
* @param userName nome utilizado para identificação na rede.
|
|
||||||
* @param port porta utilizada na comunicação multicast.
|
|
||||||
*/
|
|
||||||
public MainWindow(String userName, int port) {
|
public MainWindow(String userName, int port) {
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
|
@ -64,14 +48,6 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
initGUI();
|
|
||||||
initSocket();
|
|
||||||
initHeartbeat();
|
|
||||||
initPlugins();
|
|
||||||
this.setSize(new java.awt.Dimension(1024, 768));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI() {
|
|
||||||
this.setJMenuBar(menuBar);
|
this.setJMenuBar(menuBar);
|
||||||
this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
||||||
this.setTitle("Share My Sheet");
|
this.setTitle("Share My Sheet");
|
||||||
|
|
@ -90,29 +66,22 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
this.getContentPane().add(statusPanel, java.awt.BorderLayout.PAGE_END);
|
this.getContentPane().add(statusPanel, java.awt.BorderLayout.PAGE_END);
|
||||||
|
|
||||||
this.pack();
|
this.pack();
|
||||||
}
|
|
||||||
|
|
||||||
private void initSocket() {
|
this.socket = new Socket(this);
|
||||||
this.socketUDP = new SocketUDP(this);
|
this.socket.start();
|
||||||
this.socketTCP = new SocketTCP(this);
|
|
||||||
|
|
||||||
this.socketUDP.start();
|
|
||||||
this.socketTCP.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initHeartbeat() {
|
|
||||||
this.heartbeatManager = new HeartbeatManager(this);
|
this.heartbeatManager = new HeartbeatManager(this);
|
||||||
this.onlineUsersPanel = new OnlineUsersPanel();
|
this.onlineUsersPanel = new OnlineUsersPanel();
|
||||||
this.heartbeatManager.addPeerListener(this.onlineUsersPanel);
|
this.heartbeatManager.addPeerListener(this.onlineUsersPanel);
|
||||||
this.getContentPane().add(this.onlineUsersPanel, java.awt.BorderLayout.LINE_END);
|
this.getContentPane().add(this.onlineUsersPanel, java.awt.BorderLayout.LINE_END);
|
||||||
this.heartbeatManager.start();
|
this.heartbeatManager.start();
|
||||||
}
|
|
||||||
|
|
||||||
private void initPlugins() {
|
this.setSize(new java.awt.Dimension(1024, 768));
|
||||||
this.pluginManager = new PluginLoader(this);
|
this.pluginManager = new PluginLoader(this);
|
||||||
this.pluginManager.loadPlugins();
|
this.pluginManager.loadPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void exit() {
|
public void exit() {
|
||||||
this.setVisible(false);
|
this.setVisible(false);
|
||||||
this.dispose();
|
this.dispose();
|
||||||
|
|
@ -135,12 +104,8 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
return this.port;
|
return this.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocketUDP getSocketUDP() {
|
public Socket getSocket() {
|
||||||
return this.socketUDP;
|
return this.socket;
|
||||||
}
|
|
||||||
|
|
||||||
public SocketTCP getSocketTCP() {
|
|
||||||
return this.socketTCP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Plugin> getPlugins() {
|
public List<Plugin> getPlugins() {
|
||||||
|
|
@ -163,17 +128,11 @@ public class MainWindow extends javax.swing.JFrame {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconnect() {
|
private void reconnect() {
|
||||||
if (this.socketUDP != null) {
|
if (this.socket != null) {
|
||||||
this.socketUDP.close();
|
this.socket.close();
|
||||||
}
|
}
|
||||||
if (this.socketTCP != null) {
|
this.socket = new Socket(this);
|
||||||
this.socketTCP.close();
|
this.socket.start();
|
||||||
}
|
|
||||||
|
|
||||||
this.socketUDP = new SocketUDP(this);
|
|
||||||
this.socketTCP = new SocketTCP(this);
|
|
||||||
|
|
||||||
this.socketUDP.start();
|
|
||||||
this.socketTCP.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,31 +9,20 @@ import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Barra de menus principal da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Gerencia os menus padrões da interface e disponibiliza um espaço
|
|
||||||
* para que plugins adicionem seus próprios menus.</p>
|
|
||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
* @author Gustavo
|
|
||||||
* @author tony
|
* @author tony
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class MenuBar extends JMenuBar {
|
public class MenuBar extends JMenuBar {
|
||||||
|
|
||||||
private javax.swing.JMenuItem setupMenu;
|
private javax.swing.JMenuItem setupMenu;
|
||||||
private javax.swing.JMenuItem aboutMenu;
|
|
||||||
private javax.swing.JMenu fileMenu;
|
private javax.swing.JMenu fileMenu;
|
||||||
private javax.swing.JMenu helpMenu;
|
private javax.swing.JMenu helpMenu;
|
||||||
private javax.swing.JMenu pluginMenu;
|
private javax.swing.JMenu pluginMenu;
|
||||||
private javax.swing.JMenuItem exitMenu;
|
private javax.swing.JMenuItem exitMenu;
|
||||||
private final MainWindow mainWindow;
|
private final MainWindow mainWindow;
|
||||||
|
|
||||||
/**
|
|
||||||
* Cria a barra de menus principal da aplicação.
|
|
||||||
*
|
|
||||||
* @param mainWindow mainWindow janela principal da aplicação utilizada para executar
|
|
||||||
*/
|
|
||||||
public MenuBar(MainWindow mainWindow) {
|
public MenuBar(MainWindow mainWindow) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.initComponents();
|
this.initComponents();
|
||||||
|
|
@ -45,7 +34,6 @@ public class MenuBar extends JMenuBar {
|
||||||
helpMenu = new javax.swing.JMenu();
|
helpMenu = new javax.swing.JMenu();
|
||||||
pluginMenu = new javax.swing.JMenu();
|
pluginMenu = new javax.swing.JMenu();
|
||||||
setupMenu = new javax.swing.JMenuItem();
|
setupMenu = new javax.swing.JMenuItem();
|
||||||
aboutMenu = new javax.swing.JMenuItem();
|
|
||||||
|
|
||||||
fileMenu.setText("File");
|
fileMenu.setText("File");
|
||||||
exitMenu.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_MASK));
|
exitMenu.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_MASK));
|
||||||
|
|
@ -58,18 +46,14 @@ public class MenuBar extends JMenuBar {
|
||||||
this.add(pluginMenu);
|
this.add(pluginMenu);
|
||||||
|
|
||||||
helpMenu.setText("Help");
|
helpMenu.setText("Help");
|
||||||
|
|
||||||
setupMenu.setText("Preferences");
|
setupMenu.setText("Preferences");
|
||||||
setupMenu.addActionListener(this::setupMenuActionPerformed);
|
setupMenu.addActionListener(this::setupMenuActionPerformed);
|
||||||
helpMenu.add(setupMenu);
|
helpMenu.add(setupMenu);
|
||||||
|
|
||||||
aboutMenu.setText("About");
|
|
||||||
aboutMenu.addActionListener(this::aboutMenuActionPerformed);
|
|
||||||
helpMenu.add(aboutMenu);
|
|
||||||
|
|
||||||
this.add(helpMenu);
|
this.add(helpMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void exitMenuActionPerformed(java.awt.event.ActionEvent evt) {
|
private void exitMenuActionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
mainWindow.exit();
|
mainWindow.exit();
|
||||||
}
|
}
|
||||||
|
|
@ -85,17 +69,6 @@ public class MenuBar extends JMenuBar {
|
||||||
login.dispose();
|
login.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void aboutMenuActionPerformed(java.awt.event.ActionEvent evt) {
|
|
||||||
About about = new About();
|
|
||||||
about.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adiciona um menu fornecido por um plugin à seção de plugins da barra
|
|
||||||
* de menus.
|
|
||||||
*
|
|
||||||
* @param menu menu criado pelo plugin.
|
|
||||||
*/
|
|
||||||
public void addPlugin(JMenu menu){
|
public void addPlugin(JMenu menu){
|
||||||
this.pluginMenu.add(menu);
|
this.pluginMenu.add(menu);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,7 @@ import javax.swing.JScrollPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import pitiupi.control.PeerInfo;
|
import pitiupi.control.PeerInfo;
|
||||||
import pitiupi.control.PeerListener;
|
import pitiupi.control.PeerListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Painel da interface gráfica responsável por exibir os usuários ativos
|
|
||||||
* na rede.
|
|
||||||
*
|
*
|
||||||
* @author tony
|
* @author tony
|
||||||
*/
|
*/
|
||||||
|
|
@ -33,11 +30,6 @@ public class OnlineUsersPanel extends JPanel implements PeerListener {
|
||||||
add(new JScrollPane(userList), BorderLayout.CENTER);
|
add(new JScrollPane(userList), BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Atualiza a lista de usuários exibida no painel.
|
|
||||||
*
|
|
||||||
* @param peers lista atualizada de usuários ativos na rede.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onPeersChanged(List<PeerInfo> peers) {
|
public void onPeersChanged(List<PeerInfo> peers) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import java.util.logging.Logger;
|
||||||
* @author tony
|
* @author tony
|
||||||
*/
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param args the command line arguments
|
* @param args the command line arguments
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,6 @@ import pitiupi.GUI.MainWindow;
|
||||||
import pitiupi.net.HeartbeatMessage;
|
import pitiupi.net.HeartbeatMessage;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
/**
|
/**
|
||||||
* Gerencia o mecanismo de heartbeat da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Responsável por anunciar a presença da aplicação na rede, receber
|
|
||||||
* heartbeats de outros usuários, controlar o tempo desde a última atividade
|
|
||||||
* dos peers e notificar listeners quando a lista de usuários ativos muda.</p>
|
|
||||||
*
|
*
|
||||||
* @author tony
|
* @author tony
|
||||||
*/
|
*/
|
||||||
|
|
@ -35,28 +30,16 @@ public class HeartbeatManager {
|
||||||
private ScheduledExecutorService scheduler;
|
private ScheduledExecutorService scheduler;
|
||||||
private final String instanceId = UUID.randomUUID().toString();
|
private final String instanceId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
/**
|
|
||||||
* Cria um gerenciador de heartbeat associado à janela principal da aplicação.
|
|
||||||
*
|
|
||||||
* @param mainWindow janela principal utilizada para obter o nome do usuário
|
|
||||||
* e o socket de comunicação.
|
|
||||||
*/
|
|
||||||
public HeartbeatManager(MainWindow mainWindow) {
|
public HeartbeatManager(MainWindow mainWindow) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Inicia o mecanismo de heartbeat.
|
|
||||||
*/
|
|
||||||
public void start() {
|
public void start() {
|
||||||
scheduler = Executors.newScheduledThreadPool(1);
|
scheduler = Executors.newScheduledThreadPool(1);
|
||||||
scheduler.scheduleAtFixedRate(this::announce, 0, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
scheduler.scheduleAtFixedRate(this::announce, 0, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
||||||
scheduler.scheduleAtFixedRate(this::checkTimeouts, HEARTBEAT_INTERVAL_MS, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
scheduler.scheduleAtFixedRate(this::checkTimeouts, HEARTBEAT_INTERVAL_MS, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Para o mecanismo de heartbeat e encerra as tarefas agendadas.
|
|
||||||
*/
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (scheduler != null) {
|
if (scheduler != null) {
|
||||||
scheduler.shutdownNow();
|
scheduler.shutdownNow();
|
||||||
|
|
@ -66,24 +49,12 @@ public class HeartbeatManager {
|
||||||
private void announce() {
|
private void announce() {
|
||||||
try {
|
try {
|
||||||
HeartbeatMessage msg = new HeartbeatMessage(mainWindow.getUserName(), instanceId);
|
HeartbeatMessage msg = new HeartbeatMessage(mainWindow.getUserName(), instanceId);
|
||||||
mainWindow.getSocketUDP().sendMulticast(msg.toByteArray());
|
mainWindow.getSocket().send(msg.toByteArray());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.getLogger(HeartbeatManager.class.getName()).log(Level.WARNING, "Falha ao enviar heartbeat", ex);
|
Logger.getLogger(HeartbeatManager.class.getName()).log(Level.WARNING, "Falha ao enviar heartbeat", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chamado pelo Socket quando um pacote é identificado como heartbeat
|
|
||||||
/**
|
|
||||||
* Processa uma mensagem de heartbeat recebida de outro usuário.
|
|
||||||
*
|
|
||||||
* <p>Atualiza o momento da última atividade do peer correspondente e notifica os listeners
|
|
||||||
* caso outra instância da aplicação seja identificada.</p>
|
|
||||||
*
|
|
||||||
* <p>Mensagens de heartbeat enviadas pela própria aplicação são ignoradas.</p>
|
|
||||||
*
|
|
||||||
* @param msg mensagem de heartbeat recebida.
|
|
||||||
* @param from endereço de rede do usuário que enviou a mensagem.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void receiveHeartbeat(HeartbeatMessage msg, InetAddress from) {
|
public void receiveHeartbeat(HeartbeatMessage msg, InetAddress from) {
|
||||||
// ignora o próprio heartbeat
|
// ignora o próprio heartbeat
|
||||||
|
|
@ -117,33 +88,15 @@ public class HeartbeatManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adiciona um listener para receber atualizações sobre os peers conhecidos.
|
|
||||||
*
|
|
||||||
* <p>O listener recebe imediatamente o estado atual dos peers ao ser
|
|
||||||
* registrado.</p>
|
|
||||||
*
|
|
||||||
* @param l listener que será notificado sobre alterações.
|
|
||||||
*/
|
|
||||||
public void addPeerListener(PeerListener l) {
|
public void addPeerListener(PeerListener l) {
|
||||||
listeners.add(l);
|
listeners.add(l);
|
||||||
l.onPeersChanged(new ArrayList<>(peers.values()));
|
l.onPeersChanged(new ArrayList<>(peers.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove um listener previamente registrado.
|
|
||||||
*
|
|
||||||
* @param l listener que deixará de receber atualizações.
|
|
||||||
*/
|
|
||||||
public void removePeerListener(PeerListener l) {
|
public void removePeerListener(PeerListener l) {
|
||||||
listeners.remove(l);
|
listeners.remove(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retorna uma cópia da lista atual de peers conhecidos.
|
|
||||||
*
|
|
||||||
* @return lista contendo os peers atualmente conhecidos.
|
|
||||||
*/
|
|
||||||
public List<PeerInfo> getPeers() {
|
public List<PeerInfo> getPeers() {
|
||||||
return new ArrayList<>(peers.values());
|
return new ArrayList<>(peers.values());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ package pitiupi.control;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
/**
|
/**
|
||||||
* Representa um usuário identificado na rede e as informações necessárias
|
|
||||||
* para acompanhar sua presença através de mensagens de heartbeat.
|
|
||||||
*
|
|
||||||
* <p>Armazena o nome do usuário, seu endereço de rede e o instante da
|
|
||||||
* última mensagem recebida.</p>
|
|
||||||
*
|
*
|
||||||
* @author tony
|
* @author tony
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,11 +29,6 @@ public class PeerInfo {
|
||||||
return lastSeen;
|
return lastSeen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Atualiza o instante da última atividade do usuário.
|
|
||||||
*
|
|
||||||
* @param timestamp novo instante de atividade.
|
|
||||||
*/
|
|
||||||
public void touch(long timestamp) {
|
public void touch(long timestamp) {
|
||||||
this.lastSeen = timestamp;
|
this.lastSeen = timestamp;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,10 @@
|
||||||
package pitiupi.control;
|
package pitiupi.control;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface para receber notificações sobre alterações nos usuários
|
|
||||||
* conhecidos na rede.
|
|
||||||
*
|
|
||||||
* <p>Implementações desta interface são notificadas sempre que a lista
|
|
||||||
* de usuários ativos é modificada.</p>
|
|
||||||
*
|
*
|
||||||
* @author tony
|
* @author tony
|
||||||
*/
|
*/
|
||||||
public interface PeerListener {
|
public interface PeerListener {
|
||||||
/**
|
|
||||||
* Chamado quando a lista de usuários conhecidos na rede é alterada.
|
|
||||||
*
|
|
||||||
* @param peers lista atualizada de usuários ativos.
|
|
||||||
*/
|
|
||||||
void onPeersChanged(List<PeerInfo> peers);
|
void onPeersChanged(List<PeerInfo> peers);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,7 @@ import pitiupi.Main;
|
||||||
import pitiupi.plugin.Plugin;
|
import pitiupi.plugin.Plugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsável por localizar, carregar e registrar os plugins
|
|
||||||
* presentes no diretório {@code plugins}.
|
|
||||||
*
|
*
|
||||||
* <p>Os plugins são descobertos utilizando {@link java.util.ServiceLoader}
|
|
||||||
* e devem estar empacotados como arquivos JAR contendo a configuração
|
|
||||||
* {@code META-INF/services} correspondente.</p>
|
|
||||||
* @author flavio
|
* @author flavio
|
||||||
*/
|
*/
|
||||||
public class PluginLoader {
|
public class PluginLoader {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
package pitiupi.net;
|
package pitiupi.net;
|
||||||
/**
|
/**
|
||||||
* Mensagem utilizada para sinalizar a presença de um usuário na rede.
|
|
||||||
*
|
|
||||||
* <p>É enviada periodicamente para permitir que a aplicação identifique
|
|
||||||
* usuários ativos e mantenha o controle dos participantes conectados.</p>
|
|
||||||
*
|
*
|
||||||
* @author tony
|
* @author tony
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,11 @@ import java.io.ObjectOutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classe base para as mensagens trocadas entre a aplicação e os plugins.
|
|
||||||
*
|
|
||||||
* <p>As mensagens devem estender esta classe para utilizar o sistema de
|
|
||||||
* comunicação da aplicação. Subclasses podem adicionar os dados e a lógica
|
|
||||||
* necessários para representar diferentes tipos de mensagens.</p>
|
|
||||||
*
|
|
||||||
* <p>Por implementar {@link Serializable}, objetos derivados desta classe
|
|
||||||
* podem ser convertidos em bytes para transmissão pela rede.</p>
|
|
||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
*/
|
*/
|
||||||
public abstract class Message implements Serializable{
|
public abstract class Message implements Serializable{
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializa a mensagem para um vetor de bytes.
|
|
||||||
*
|
|
||||||
* @return representação serializada da mensagem.
|
|
||||||
* @throws IOException caso ocorra um erro durante a serialização.
|
|
||||||
*/
|
|
||||||
public byte[] toByteArray() throws IOException {
|
public byte[] toByteArray() throws IOException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ObjectOutput out = new ObjectOutputStream(baos);
|
ObjectOutput out = new ObjectOutputStream(baos);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package pitiupi.net;
|
||||||
|
|
||||||
|
import pitiupi.GUI.MainWindow;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.MulticastSocket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import pitiupi.plugin.Plugin;
|
||||||
|
import pitiupi.net.HeartbeatMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author flavio
|
||||||
|
* @author tony
|
||||||
|
*/
|
||||||
|
public class Socket extends Thread {
|
||||||
|
|
||||||
|
private MulticastSocket multicastSocket;
|
||||||
|
private InetAddress address;
|
||||||
|
private MainWindow main;
|
||||||
|
private volatile boolean running = true;
|
||||||
|
public final static String INET_ADDR = "224.0.0.3";
|
||||||
|
|
||||||
|
public Socket(MainWindow main) {
|
||||||
|
this.main = main;
|
||||||
|
try {
|
||||||
|
this.address = InetAddress.getByName(Socket.INET_ADDR);
|
||||||
|
} catch (UnknownHostException ex) {
|
||||||
|
System.getLogger(Socket.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
multicastSocket = new MulticastSocket(this.main.getPort());
|
||||||
|
multicastSocket.setSendBufferSize(256000);
|
||||||
|
multicastSocket.setReceiveBufferSize(256000);
|
||||||
|
multicastSocket.setReuseAddress(true);
|
||||||
|
multicastSocket.joinGroup(address);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.out.println("There is no socket connection. Sorry.");
|
||||||
|
System.out.println(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(byte[] msg) throws IOException {
|
||||||
|
DatagramPacket msgPacket;
|
||||||
|
msgPacket = new DatagramPacket(msg, msg.length, this.address, this.main.getPort());
|
||||||
|
multicastSocket.send(msgPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
running = false;
|
||||||
|
if (multicastSocket != null) {
|
||||||
|
try {
|
||||||
|
multicastSocket.leaveGroup(address);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
multicastSocket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receive() throws UnknownHostException, IOException, ClassNotFoundException {
|
||||||
|
byte[] buf = new byte[256000];
|
||||||
|
while (running) {
|
||||||
|
DatagramPacket msgPacket = new DatagramPacket(buf, buf.length);
|
||||||
|
try {
|
||||||
|
multicastSocket.receive(msgPacket);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (!running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
HeartbeatMessage heartbeat = tryParseHeartbeat(msgPacket.getData());
|
||||||
|
if (heartbeat != null) {
|
||||||
|
main.getHeartbeatManager().receiveHeartbeat(heartbeat, msgPacket.getAddress());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Plugin plugin : main.getPlugins()) {
|
||||||
|
System.out.println(msgPacket.getAddress().toString());
|
||||||
|
plugin.receiveMessage(msgPacket.getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private HeartbeatMessage tryParseHeartbeat(byte[] data) {
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||||
|
ObjectInput in = new ObjectInputStream(bais);
|
||||||
|
Object obj = in.readObject();
|
||||||
|
return (obj instanceof HeartbeatMessage) ? (HeartbeatMessage) obj : null;
|
||||||
|
} catch (IOException | ClassNotFoundException | ClassCastException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
this.receive();
|
||||||
|
} catch (IOException | ClassNotFoundException ex) {
|
||||||
|
Logger.getLogger(Socket.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,184 +0,0 @@
|
||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package pitiupi.net;
|
|
||||||
|
|
||||||
import pitiupi.GUI.MainWindow;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import pitiupi.plugin.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gerencia a comunicação UDP multicast e unicast da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Mantém a conexão com o grupo multicast da aplicação e permite o envio
|
|
||||||
* de mensagens tanto para o grupo quanto diretamente para um peer.</p>
|
|
||||||
*
|
|
||||||
* <p>As mensagens recebidas são verificadas para identificar mensagens de
|
|
||||||
* heartbeat. As demais mensagens são encaminhadas aos plugins, que são
|
|
||||||
* responsáveis por interpretá-las.</p>
|
|
||||||
*
|
|
||||||
* @author flavio
|
|
||||||
* @author tony
|
|
||||||
* @author Gustavo
|
|
||||||
*/
|
|
||||||
public class SocketUDP extends Thread {
|
|
||||||
|
|
||||||
private MulticastSocket multicastSocket;
|
|
||||||
|
|
||||||
private InetAddress address;
|
|
||||||
private MainWindow main;
|
|
||||||
private volatile boolean running = true;
|
|
||||||
public final static String INET_ADDR = "224.0.0.3";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cria e inicializa o socket UDP utilizado pela aplicação.
|
|
||||||
*
|
|
||||||
* <p>O socket é associado à porta da aplicação e ingressa no grupo
|
|
||||||
* multicast configurado.</p>
|
|
||||||
*
|
|
||||||
* @param main janela principal da aplicação.
|
|
||||||
*/
|
|
||||||
public SocketUDP(MainWindow main) {
|
|
||||||
this.main = main;
|
|
||||||
try {
|
|
||||||
this.address = InetAddress.getByName(SocketUDP.INET_ADDR);
|
|
||||||
} catch (UnknownHostException ex) {
|
|
||||||
System.getLogger(SocketUDP.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
multicastSocket = new MulticastSocket(this.main.getPort());
|
|
||||||
multicastSocket.setSendBufferSize(256000);
|
|
||||||
multicastSocket.setReceiveBufferSize(256000);
|
|
||||||
multicastSocket.setReuseAddress(true);
|
|
||||||
multicastSocket.joinGroup(address);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println("There is no socket connection. Sorry.");
|
|
||||||
System.out.println(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Envia uma mensagem para o grupo multicast da aplicação.
|
|
||||||
*
|
|
||||||
* @param msg mensagem serializada a ser enviada.
|
|
||||||
* @throws IOException caso ocorra um erro durante o envio.
|
|
||||||
*/
|
|
||||||
public void sendMulticast(byte[] msg) throws IOException {
|
|
||||||
send(msg, this.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Envia uma mensagem diretamente para um peer utilizando UDP.
|
|
||||||
*
|
|
||||||
* @param msg mensagem serializada a ser enviada.
|
|
||||||
* @param destinationAddress endereço IP do peer destinatário.
|
|
||||||
* @throws IOException caso ocorra um erro durante o envio.
|
|
||||||
*/
|
|
||||||
public void sendUnicast(byte[] msg, InetAddress destinationAddress) throws IOException {
|
|
||||||
send(msg, destinationAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Envia uma mensagem UDP para o endereço especificado.
|
|
||||||
*
|
|
||||||
* @param msg mensagem serializada a ser enviada.
|
|
||||||
* @param destinationAddress endereço de destino.
|
|
||||||
* @throws IOException caso ocorra um erro durante o envio.
|
|
||||||
*/
|
|
||||||
private void send(byte[] msg, InetAddress destinationAddress) throws IOException {
|
|
||||||
DatagramPacket msgPacket;
|
|
||||||
msgPacket = new DatagramPacket(msg, msg.length, destinationAddress, this.main.getPort());
|
|
||||||
multicastSocket.send(msgPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encerra a comunicação multicast e interrompe a recepção de mensagens.
|
|
||||||
*/
|
|
||||||
public void close() {
|
|
||||||
running = false;
|
|
||||||
if (multicastSocket != null) {
|
|
||||||
try {
|
|
||||||
multicastSocket.leaveGroup(address);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.out.println(ex);
|
|
||||||
}
|
|
||||||
multicastSocket.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recebe e processa mensagens UDP.
|
|
||||||
*
|
|
||||||
* <p>Mensagens de heartbeat são encaminhadas ao gerenciador de heartbeat.
|
|
||||||
* As demais mensagens são encaminhadas aos plugins carregados pela
|
|
||||||
* aplicação.</p>
|
|
||||||
*
|
|
||||||
* @throws UnknownHostException caso não seja possível resolver o endereço
|
|
||||||
* utilizado pela comunicação.
|
|
||||||
* @throws IOException caso ocorra um erro durante a recepção.
|
|
||||||
* @throws ClassNotFoundException caso ocorra um erro ao verificar uma
|
|
||||||
* mensagem de heartbeat.
|
|
||||||
*/
|
|
||||||
public void receive() throws UnknownHostException, IOException, ClassNotFoundException {
|
|
||||||
byte[] buf = new byte[256000];
|
|
||||||
while (running) {
|
|
||||||
DatagramPacket msgPacket = new DatagramPacket(buf, buf.length);
|
|
||||||
try {
|
|
||||||
multicastSocket.receive(msgPacket);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
if (!running) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
HeartbeatMessage heartbeat = tryParseHeartbeat(msgPacket.getData());
|
|
||||||
if (heartbeat != null) {
|
|
||||||
main.getHeartbeatManager().receiveHeartbeat(heartbeat, msgPacket.getAddress());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (Plugin plugin : main.getPlugins()) {
|
|
||||||
System.out.println(msgPacket.getAddress().toString());
|
|
||||||
plugin.receiveMessage(msgPacket.getData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tenta identificar os dados recebidos como uma mensagem de heartbeat.
|
|
||||||
*
|
|
||||||
* @param data dados serializados recebidos.
|
|
||||||
* @return a mensagem de heartbeat caso os dados correspondam a uma;
|
|
||||||
* {@code null} caso contrário.
|
|
||||||
*/
|
|
||||||
private HeartbeatMessage tryParseHeartbeat(byte[] data) {
|
|
||||||
try {
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
|
||||||
ObjectInput in = new ObjectInputStream(bais);
|
|
||||||
Object obj = in.readObject();
|
|
||||||
return (obj instanceof HeartbeatMessage) ? (HeartbeatMessage) obj : null;
|
|
||||||
} catch (IOException | ClassNotFoundException | ClassCastException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inicia a recepção de mensagens UDP.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
this.receive();
|
|
||||||
} catch (IOException | ClassNotFoundException ex) {
|
|
||||||
Logger.getLogger(SocketUDP.class.getName()).log(Level.SEVERE, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,10 +9,6 @@ import pitiupi.GUI.MainWindow;
|
||||||
import pitiupi.net.Message;
|
import pitiupi.net.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface base para plugins da aplicação.
|
|
||||||
*
|
|
||||||
* <p>Cada plugin deve implementar esta interface para ser carregado
|
|
||||||
* pela aplicação e participar do sistema de comunicação por mensagens.</p>
|
|
||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
*/
|
*/
|
||||||
|
|
@ -21,30 +17,7 @@ public interface Plugin {
|
||||||
public String getName();
|
public String getName();
|
||||||
public String getAuthor();
|
public String getAuthor();
|
||||||
public String getVersion();
|
public String getVersion();
|
||||||
|
|
||||||
/**
|
|
||||||
* Inicializa o plugin.
|
|
||||||
*
|
|
||||||
* <p>Este método é chamado pela aplicação após o carregamento do plugin.</p>
|
|
||||||
*
|
|
||||||
* @param window janela principal da aplicação.
|
|
||||||
*/
|
|
||||||
public void createPlugin(MainWindow window);
|
public void createPlugin(MainWindow window);
|
||||||
|
|
||||||
/**
|
|
||||||
* Retorna uma mensagem utilizada pelo plugin.
|
|
||||||
*
|
|
||||||
* @return mensagem criada pelo plugin.
|
|
||||||
*/
|
|
||||||
public Message getMessage();
|
public Message getMessage();
|
||||||
|
|
||||||
/**
|
|
||||||
* Recebe uma mensagem enviada pela aplicação.
|
|
||||||
*
|
|
||||||
* <p>A mensagem é recebida em formato serializado. O plugin deve verificar
|
|
||||||
* se a mensagem pertence ao seu tipo e realizar o processamento necessário.</p>
|
|
||||||
*
|
|
||||||
* @param message mensagem serializada recebida.
|
|
||||||
*/
|
|
||||||
public void receiveMessage(byte[] message);
|
public void receiveMessage(byte[] message);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue