Consolida documentação e plugins de referência ao núcleo atual.
Alinha mpChat e screenCast a ConnectionManager/GuiManager, adiciona docs em docs/ e README de plugins; remove artefatos de build do índice. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d92708a6e9
commit
f02acaf131
61 files changed
+1408
-758
No files matched your search
@@ -1,9 +1,13 @@
|
||||
/pitiupi/build/
|
||||
/pitiupi/dist/
|
||||
/pitiupi/plugins/*.jar
|
||||
/pitiupi.chat/build/
|
||||
/pitiupi.chat/dist/
|
||||
/pitiupi.chat/nbproject/private/
|
||||
/pitiupi.screenCast/build/
|
||||
/pitiupi.screenCast/dist/
|
||||
/pitiupi.mpChat/out/
|
||||
/pitiupi.mpChat/dist/
|
||||
**/built-jar.properties
|
||||
|
||||
.idea/
|
||||
@@ -1,29 +1,50 @@
|
||||
# pitiupi
|
||||
|
||||
Ferramenta p2p para práticas em rede
|
||||
Ferramenta **P2P** para práticas de Redes de Computadores: núcleo em Java com arquitetura de **plugins** (chat, compartilhamento de tela, exemplos multi-protocolo).
|
||||
|
||||
Este repositório tem 3 projetos Java que são independentes:
|
||||
- A ferramenta pitiupi (P2P)
|
||||
- Um plugin de Chat
|
||||
- Um plugin de ScreenCast
|
||||
**Documentação completa:** [docs/README.md](docs/README.md)
|
||||
|
||||
Cada um destes projetos deve ser compilado individualmente. Para isso, use a ferramenta ant no diretório do projeto.
|
||||
Ex:
|
||||
## Pré-requisitos
|
||||
|
||||
| Ferramenta | Uso |
|
||||
| --- | --- |
|
||||
| **JDK** com `javac` | Compilação (OpenJDK **25** recomendado; núcleo compila com target **17**) |
|
||||
| **Apache Ant** 1.10+ | `pitiupi/`, `pitiupi.chat/`, `pitiupi.screenCast/` |
|
||||
| **GNU Make** | `pitiupi.mpChat/` |
|
||||
|
||||
Exemplo Ubuntu / WSL:
|
||||
|
||||
```bash
|
||||
sudo apt install openjdk-25-jdk ant make
|
||||
javac -version # deve existir — JRE sozinho não basta
|
||||
```
|
||||
cd pitiupi.chat
|
||||
ant jar
|
||||
```
|
||||
|
||||
Feito isso, os arquivos jar dos plugins deverão ser copiados ou linkados na pasta plugin da ferramenta.
|
||||
No caso de a mesma ser linkada, ao recompilar os plugins não é necessário copiar o mesmo novamente para a pasta de plugins da ferramenta.
|
||||
Para criar links simbólicos no Linux, use o comando:
|
||||
## Módulos
|
||||
|
||||
```
|
||||
| Pasta | Descrição | Build |
|
||||
| --- | --- | --- |
|
||||
| `pitiupi/` | Núcleo (GUI, UDP multicast, TCP, heartbeat) | `ant jar` |
|
||||
| `pitiupi.chat/` | Plugin referência — chat | `ant jar` |
|
||||
| `pitiupi.screenCast/` | Plugin referência — screen cast | `ant jar` |
|
||||
| `pitiupi.mpChat/` | Plugin referência — chat multicast + TCP | `make` |
|
||||
|
||||
Compile cada módulo no seu diretório (núcleo primeiro). Instale os JARs dos plugins em `pitiupi/plugins/` — ver [pitiupi/plugins/README.md](pitiupi/plugins/README.md).
|
||||
|
||||
Exemplo de symlinks (Linux):
|
||||
|
||||
```bash
|
||||
cd pitiupi/plugins
|
||||
ln -s ../../pitiupi.chat/dist/*.jar .
|
||||
ln -s ../../pitiupi.screenCast/dist/*.jar .
|
||||
```
|
||||
Isso deve resolver!
|
||||
ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar
|
||||
ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar
|
||||
ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar
|
||||
```
|
||||
|
||||
Para criar um novo plugin:
|
||||
Execute o núcleo (a partir de `pitiupi/`):
|
||||
|
||||
```bash
|
||||
cd pitiupi
|
||||
java -jar dist/pitiupi.jar
|
||||
```
|
||||
|
||||
Quickstart: [docs/documentation/00_quickstart.md](docs/documentation/00_quickstart.md).
|
||||
Trabalho prático (plugin próprio): [docs/documentation/10_tutorial_plugin.md](docs/documentation/10_tutorial_plugin.md).
|
||||
@@ -0,0 +1,35 @@
|
||||
# Documentação — Pitiupi
|
||||
|
||||
Entrada na raiz do repositório: [README.md](../README.md).
|
||||
|
||||
## Pilares
|
||||
|
||||
| Pasta / arquivo | Papel |
|
||||
| --- | --- |
|
||||
| **[documentation/](documentation/README.md)** | Código, rede, plugins, operação e guia para alunos |
|
||||
| **[references/](references/README.md)** | Bibliografia e notas de conceitos (UDP, TCP, SPI) |
|
||||
| **[decision-register.md](decision-register.md)** | Decisões de desenho da plataforma |
|
||||
|
||||
## Público-alvo
|
||||
|
||||
| Leitor | Começar por |
|
||||
| --- | --- |
|
||||
| **Aluno (TP)** | [documentation/00_quickstart.md](documentation/00_quickstart.md) → [10_tutorial_plugin.md](documentation/10_tutorial_plugin.md) → [11_plugins_referencia.md](documentation/11_plugins_referencia.md) |
|
||||
| **Monitor / docência** | [documentation/01_visao_disciplina_e_tp.md](documentation/01_visao_disciplina_e_tp.md) → [15_testes_e_gate.md](documentation/15_testes_e_gate.md) |
|
||||
| **Desenvolvedor** | [documentation/13_guia_dev_e_agente.md](documentation/13_guia_dev_e_agente.md) → [14_arquitetura_e_manutencao.md](documentation/14_arquitetura_e_manutencao.md) |
|
||||
| **Professor / banca** | [documentation/16_fundamentacao_redes.md](documentation/16_fundamentacao_redes.md) → [references/README.md](references/README.md) |
|
||||
|
||||
**Idioma:** documentação em PT-BR; pacotes e classes Java como no código.
|
||||
|
||||
## Manutenção
|
||||
|
||||
| Tipo de mudança | Onde documentar |
|
||||
| --- | --- |
|
||||
| Protocolo UDP/TCP, multicast, portas | [documentation/05_udp_multicast.md](documentation/05_udp_multicast.md), [06_tcp_conexoes.md](documentation/06_tcp_conexoes.md), [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) |
|
||||
| Contrato de plugin / SPI | [documentation/09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md), [10_tutorial_plugin.md](documentation/10_tutorial_plugin.md) |
|
||||
| Novo plugin de referência | [documentation/11_plugins_referencia.md](documentation/11_plugins_referencia.md) |
|
||||
| Decisão de arquitetura | [decision-register.md](decision-register.md) |
|
||||
| Débito técnico | [documentation/engineering_backlog.md](documentation/engineering_backlog.md) |
|
||||
| Literatura de redes | [references/](references/) |
|
||||
|
||||
Documentação legada em `pitiupi/docs/` redireciona para esta árvore.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Registro de decisões — Pitiupi
|
||||
|
||||
| ID | Decisão | Motivação | Doc |
|
||||
| --- | --- | --- | --- |
|
||||
| D1 | Grupo multicast `224.0.0.3` | Endereço fixo para descoberta e broadcast na LAN da sala | [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) |
|
||||
| D2 | Mesma porta UDP e TCP por instância | Configurada no login; simplifica firewall em laboratório | [00_quickstart.md](documentation/00_quickstart.md) |
|
||||
| D3 | Heartbeat via UDP multicast | Presença sem conexão TCP permanente com todos os peers | [08_heartbeat_e_presenca.md](documentation/08_heartbeat_e_presenca.md) |
|
||||
| D4 | Timeout de peer ~15 s sem heartbeat | Remover usuários inativos da lista | [08_heartbeat_e_presenca.md](documentation/08_heartbeat_e_presenca.md) |
|
||||
| D5 | Framing TCP: `int` tamanho + bytes | Delimitar mensagens em stream orientado a bytes | [06_tcp_conexoes.md](documentation/06_tcp_conexoes.md) |
|
||||
| D6 | Mensagens `Serializable` em `byte[]` | Independência do transporte; plugins definem tipos | [07_mensagens_e_serializacao.md](documentation/07_mensagens_e_serializacao.md) |
|
||||
| D7 | Plugins via JAR + Java SPI | Carregamento dinâmico sem recompilar o núcleo | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||
| D8 | Classloader por JAR de plugin | Isolar dependências; desserialização com `PluginObjectInputStream` | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||
|
||||
Novas decisões: adicionar linha nesta tabela e detalhar no arquivo técnico correspondente.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Quickstart
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
- **JDK completo** (`java` e `javac`) — OpenJDK 25 recomendado; o núcleo usa `javac.source/target=17`, plugins Ant usam 25.
|
||||
- Apache Ant (`pitiupi`, `pitiupi.chat`, `pitiupi.screenCast`)
|
||||
- GNU Make (`pitiupi.mpChat`)
|
||||
|
||||
Ubuntu / WSL:
|
||||
|
||||
```bash
|
||||
sudo apt install openjdk-25-jdk ant make
|
||||
which javac ant make
|
||||
```
|
||||
|
||||
Se `ant jar` falhar com *Cannot run program "javac"*, você instalou apenas JRE — instale `openjdk-*-jdk`.
|
||||
|
||||
## Build
|
||||
|
||||
Na raiz do clone:
|
||||
|
||||
```bash
|
||||
cd pitiupi && ant jar
|
||||
cd ../pitiupi.chat && ant jar
|
||||
cd ../pitiupi.screenCast && ant jar
|
||||
cd ../pitiupi.mpChat && make
|
||||
```
|
||||
|
||||
O núcleo gera `pitiupi/dist/pitiupi.jar`. Cada plugin gera JAR em seu `dist/`.
|
||||
|
||||
## Instalar plugins
|
||||
|
||||
A aplicação lê JARs de `pitiupi/plugins/` (relativo ao diretório de trabalho ao executar o núcleo). Detalhes: [pitiupi/plugins/README.md](../../pitiupi/plugins/README.md).
|
||||
|
||||
Symlinks em desenvolvimento (Linux):
|
||||
|
||||
```bash
|
||||
cd pitiupi/plugins
|
||||
ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar
|
||||
ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar
|
||||
ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar
|
||||
```
|
||||
|
||||
## Executar
|
||||
|
||||
```bash
|
||||
cd pitiupi
|
||||
java -jar dist/pitiupi.jar
|
||||
```
|
||||
|
||||
No diálogo inicial informe **nome** e **porta** (UDP/TCP). Para duas instâncias no mesmo computador, use **portas diferentes** (ex.: 5000 e 5001).
|
||||
|
||||
## Verificar P2P
|
||||
|
||||
1. Abra duas instâncias (mesma rede ou mesmo host com portas distintas).
|
||||
2. Confirme que os nomes aparecem no painel de usuários online.
|
||||
3. Teste um plugin de referência (ex.: chat em `MenuCategory.Social`).
|
||||
|
||||
Problemas de multicast: [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md).
|
||||
|
||||
Gate de build (quatro módulos): [15_testes_e_gate.md](15_testes_e_gate.md).
|
||||
|
||||
Próximo passo para o TP: [10_tutorial_plugin.md](10_tutorial_plugin.md).
|
||||
@@ -0,0 +1,47 @@
|
||||
# Visão da disciplina e trabalho prático
|
||||
|
||||
## Papel do Pitiupi
|
||||
|
||||
O Pitiupi é uma **plataforma P2P com plugins** usada em Redes de Computadores para:
|
||||
|
||||
- Experimentar UDP (multicast/unicast) e TCP na mesma aplicação.
|
||||
- Observar **presença** de peers (heartbeat) sem servidor central de aplicação.
|
||||
- Desenvolver **trabalhos individuais ou em grupo** como novos plugins (jogos, ferramentas colaborativas, etc.).
|
||||
|
||||
O núcleo fornece rede e GUI; o aluno implementa a lógica no plugin.
|
||||
|
||||
## Trabalho típico (plugin)
|
||||
|
||||
Cada equipe/aluno entrega um **módulo Java separado** que:
|
||||
|
||||
1. Compila para JAR e carrega via `pitiupi/plugins/`.
|
||||
2. Define mensagens (`extends Message`) e o fluxo entre peers.
|
||||
3. Integra menus/janelas via `GuiManager`.
|
||||
4. Inclui README do módulo (build + protocolo).
|
||||
|
||||
Exemplos de tema: chat temático, tabuleiro (xadrez/damas), compartilhamento de arquivo, quiz síncrono.
|
||||
|
||||
## Plugins de referência no repositório
|
||||
|
||||
| Módulo | Ideia pedagógica |
|
||||
| --- | --- |
|
||||
| `pitiupi.chat` | Mensagens UDP + UI simples |
|
||||
| `pitiupi.screenCast` | Payload maior / streaming de imagem |
|
||||
| `pitiupi.mpChat` | Multicast + TCP privado no mesmo plugin |
|
||||
|
||||
Detalhes: [11_plugins_referencia.md](11_plugins_referencia.md).
|
||||
|
||||
## Critérios genéricos (ajustar na disciplina)
|
||||
|
||||
| Critério | Indicador |
|
||||
| --- | --- |
|
||||
| Funcionalidade | Comportamento correto entre dois+ peers na rede da sala |
|
||||
| Uso da plataforma | SPI, mensagens, sem “fork” desnecessário do núcleo |
|
||||
| Build | `ant jar` / `make` reproduzível |
|
||||
| Documentação | README do plugin + descrição das mensagens |
|
||||
|
||||
## Para monitores
|
||||
|
||||
- Roteiro de build: [00_quickstart.md](00_quickstart.md).
|
||||
- Checklist de entrega: [15_testes_e_gate.md](15_testes_e_gate.md).
|
||||
- Fundamentação para dúvidas de alunos: [16_fundamentacao_redes.md](16_fundamentacao_redes.md).
|
||||
@@ -0,0 +1,34 @@
|
||||
# Módulos do repositório
|
||||
|
||||
```
|
||||
pitiupi/ # clone (este repositório)
|
||||
├── pitiupi/ # núcleo P2P (executável)
|
||||
├── pitiupi.chat/ # plugin referência — chat
|
||||
├── pitiupi.screenCast/ # plugin referência — captura de tela
|
||||
├── pitiupi.mpChat/ # plugin referência — chat multi-protocolo
|
||||
└── docs/ # documentação canônica
|
||||
```
|
||||
|
||||
## Núcleo (`pitiupi/`)
|
||||
|
||||
- **Build:** Apache Ant (`build.xml`, `nbproject/`).
|
||||
- **Entrada:** `pitiupi.Main` → login → `MainWindow`.
|
||||
- **Pacotes principais:** `pitiupi.net`, `pitiupi.control`, `pitiupi.GUI`, `pitiupi.models`.
|
||||
|
||||
## Plugins de referência
|
||||
|
||||
| Pasta | Build | JAR típico | Classe SPI |
|
||||
| --- | --- | --- | --- |
|
||||
| `pitiupi.chat` | `ant jar` | `dist/pitiupi.chat.jar` | `pitiupi.chat.plugin.ChatPlugin` |
|
||||
| `pitiupi.screenCast` | `ant jar` | `dist/pitiupi.screenCast.jar` | `pitiupi.screenCast.plugin.ScreenCastPlugin` |
|
||||
| `pitiupi.mpChat` | `make` | `dist/mpChat.jar` | `plugin.MultiProtocolChat` |
|
||||
|
||||
Todos dependem do JAR do núcleo no classpath de compilação.
|
||||
|
||||
## O que o aluno cria
|
||||
|
||||
Novo diretório irmão, por exemplo `pitiupi.xadrez/`, copiando a estrutura de `pitiupi.chat` (Ant + `META-INF/services`).
|
||||
|
||||
Não é obrigatório commitar o plugin do aluno neste repositório base; o professor pode definir fork ou entrega em zip.
|
||||
|
||||
Ver [03_build_ant_netbeans.md](03_build_ant_netbeans.md) e [10_tutorial_plugin.md](10_tutorial_plugin.md).
|
||||
@@ -0,0 +1,49 @@
|
||||
# Build — Ant, NetBeans e Make
|
||||
|
||||
## Pré-requisitos e versões Java
|
||||
|
||||
| Módulo | Build | `javac.source` / `target` (NetBeans) |
|
||||
| --- | --- | --- |
|
||||
| `pitiupi` | Ant | 17 |
|
||||
| `pitiupi.chat` | Ant | 25 |
|
||||
| `pitiupi.screenCast` | Ant | 25 |
|
||||
| `pitiupi.mpChat` | Make | (sem `project.properties`; compila contra `pitiupi.jar`) |
|
||||
|
||||
Use um JDK que forneça `javac` (ex.: OpenJDK 25). Ant 1.10+ e Make no PATH.
|
||||
|
||||
## Núcleo e plugins NetBeans
|
||||
|
||||
Projetos com `nbproject/` e `build.xml`:
|
||||
|
||||
```bash
|
||||
cd pitiupi && ant jar
|
||||
cd pitiupi.chat && ant jar
|
||||
cd pitiupi.screenCast && ant jar
|
||||
```
|
||||
|
||||
Abrir no NetBeans: **File → Open Project** em cada pasta.
|
||||
|
||||
O target `jar` produz o executável do núcleo e os JARs dos plugins. O classpath de compilação dos plugins aponta para `../pitiupi/dist/pitiupi.jar` (ver `project.properties` de cada módulo).
|
||||
|
||||
## pitiupi.mpChat (Makefile)
|
||||
|
||||
```bash
|
||||
cd pitiupi.mpChat
|
||||
make
|
||||
```
|
||||
|
||||
O Makefile compila com `javac -cp ../pitiupi/dist/pitiupi.jar`, copia `META-INF` e gera `dist/mpChat.jar`. **Compile o núcleo antes.**
|
||||
|
||||
## Diretório `plugins/`
|
||||
|
||||
A pasta `pitiupi/plugins/` contém os JARs carregados em runtime. Apenas arquivos `.jar` são lidos. Ver [pitiupi/plugins/README.md](../../pitiupi/plugins/README.md).
|
||||
|
||||
## Ordem recomendada de build
|
||||
|
||||
1. `pitiupi` (núcleo)
|
||||
2. Plugins em qualquer ordem
|
||||
3. Symlink ou cópia para `pitiupi/plugins/`
|
||||
|
||||
## Gate automatizado (docentes)
|
||||
|
||||
Quem mantém o meta-workspace Cursor pode usar o script de gate que executa `ant jar` nos três módulos Ant e `make` no mpChat (requer Ant/Make no PATH). Detalhes em [15_testes_e_gate.md](15_testes_e_gate.md).
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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.
|
||||
|
||||
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.
|
||||
|
||||
Índice: [README.md](README.md).
|
||||
|
||||
# Arquitetura geral
|
||||
|
||||
A aplicação é dividida em três grupos principais:
|
||||
|
||||
- **Núcleo da aplicação**: a `MainWindow` coordena GUI, rede e plugins.
|
||||
- **Sistema de plugins**: extensões em JAR carregadas de `pitiupi/plugins/`.
|
||||
- **Comunicação de rede**: UDP (`SocketUDP`) e TCP (`SocketTCP`, `TcpConnection`).
|
||||
|
||||
## Inicialização da aplicação
|
||||
|
||||
1. A `MainWindow` é criada após o diálogo de login (nome e porta).
|
||||
2. O `SocketUDP` associa-se à porta configurada e ingressa no grupo multicast.
|
||||
3. O `SocketTCP` cria um `ServerSocket` na mesma porta para conexões TCP.
|
||||
4. Os mecanismos de comunicação iniciam threads de recepção.
|
||||
5. O `HeartbeatManager` envia heartbeats periodicamente.
|
||||
6. O painel de usuários online registra-se como listener do heartbeat.
|
||||
7. O `PluginManager` carrega os JARs em `plugins/` via SPI.
|
||||
|
||||
Ver também: [05_udp_multicast.md](05_udp_multicast.md), [09_sistema_de_plugins.md](09_sistema_de_plugins.md).
|
||||
|
||||
# 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 `ConnectionManager` e `GuiManager`
|
||||
no método `createPlugin()`.
|
||||
|
||||
A extensão usual é registrar menus por categoria (`MenuCategory`). Ver
|
||||
[12_menus_unicast_multicast.md](12_menus_unicast_multicast.md).
|
||||
|
||||
## Diagrama de comunicação
|
||||
|
||||
```
|
||||
+------------------+
|
||||
| MainWindow |
|
||||
+--------+---------+
|
||||
|
|
||||
+--------------------+--------------------+
|
||||
| | |
|
||||
v v v
|
||||
HeartbeatManager SocketUDP SocketTCP
|
||||
| | |
|
||||
v v v
|
||||
Heartbeat UDP Multicast TCP Connections
|
||||
| |
|
||||
+---------+----------+
|
||||
|
|
||||
v
|
||||
Plugins
|
||||
|
|
||||
v
|
||||
Message
|
||||
```
|
||||
|
||||
O UDP multicast sustenta presença e mensagens broadcast/unicast; o TCP sustenta
|
||||
canais diretos com framing de mensagens. Detalhes em [05](05_udp_multicast.md)
|
||||
e [06](06_tcp_conexoes.md).
|
||||
@@ -0,0 +1,85 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,192 @@
|
||||
# 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
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
# Mensagens
|
||||
|
||||
### 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[]`.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Heartbeat
|
||||
|
||||
### 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)
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
# Sistema de plugins
|
||||
|
||||
O sistema de plugins permite extensão modular sem alterar o núcleo. O contrato
|
||||
está em `pitiupi.models.Plugin` (classe abstrata).
|
||||
|
||||
## Interface Plugin
|
||||
|
||||
Métodos obrigatórios:
|
||||
|
||||
| Método | Função |
|
||||
| --- | --- |
|
||||
| `getName()`, `getAuthor()`, `getVersion()`, `getIdentifier()` | Identificação (o `identifier` distingue plugins em conexões TCP dedicadas) |
|
||||
| `createPlugin(ConnectionManager, GuiManager)` | Inicialização: menus, janelas, uso da rede |
|
||||
| `getMessages()` | Lista de classes `Message` que o plugin desserializa |
|
||||
| `receiveMessage(Message)` | Processamento após o `PluginManager` identificar o tipo |
|
||||
|
||||
Métodos opcionais (TCP avançado):
|
||||
|
||||
| Método | Função |
|
||||
| --- | --- |
|
||||
| `receiveAllTypes()` | Receber todas as mensagens públicas |
|
||||
| `receivePrivateConnection(TcpConnection)` | Aceitar conexão TCP direcionada ao plugin |
|
||||
| `receiveBytes(TcpConnection, byte[])` | Ler payload bruto na conexão privada |
|
||||
| `onSocketClosed(TcpConnection)` | Limpar estado ao fechar socket |
|
||||
|
||||
### 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.models.Plugin
|
||||
```
|
||||
|
||||
O arquivo `pitiupi.models.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
|
||||
|
||||
O `PluginManager.loadPlugins()` (chamado pela `MainWindow`):
|
||||
|
||||
1. Lista arquivos `.jar` em `pitiupi/plugins/` (diretório de trabalho = pasta do núcleo).
|
||||
2. Para cada JAR, cria um `URLClassLoader` e usa `ServiceLoader.load(Plugin.class, classLoader)`.
|
||||
3. Chama `createPlugin(connectionManager, guiManager)` em cada instância.
|
||||
4. Na recepção UDP/TCP, tenta desserializar com `PluginObjectInputStream` e despacha para plugins que declararam o tipo em `getMessages()`.
|
||||
|
||||
Implementação: `pitiupi/src/pitiupi/control/plugins/PluginManager.java`.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Tutorial — criar um plugin
|
||||
|
||||
Este guia complementa [09_sistema_de_plugins.md](09_sistema_de_plugins.md). Use `pitiupi.chat` como modelo.
|
||||
|
||||
## 1. Novo projeto
|
||||
|
||||
- Copie a pasta `pitiupi.chat` para `pitiupi.meuPlugin` (nome à escolha).
|
||||
- Ajuste `project.properties` / `build.xml` com o nome do JAR de saída.
|
||||
- Mantenha `javac.classpath` apontando para `../pitiupi/dist/pitiupi.jar`.
|
||||
|
||||
## 2. Classe principal
|
||||
|
||||
```java
|
||||
public class MeuPlugin extends Plugin {
|
||||
@Override
|
||||
public void createPlugin(ConnectionManager cm, GuiManager gui) {
|
||||
// registrar menus, abrir janelas
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<? extends Message>> getMessages() {
|
||||
return List.of(MinhaMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Message message) {
|
||||
if (message instanceof MinhaMessage m) { /* ... */ }
|
||||
}
|
||||
|
||||
// getName, getAuthor, getVersion, getIdentifier
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Mensagem
|
||||
|
||||
```java
|
||||
public class MinhaMessage extends Message implements Serializable {
|
||||
// campos + serialVersionUID recomendado
|
||||
}
|
||||
```
|
||||
|
||||
Envio típico via `ConnectionManager` (multicast ou unicast UDP, ou TCP conforme o caso).
|
||||
|
||||
## 4. Service Provider Interface
|
||||
|
||||
Arquivo `src/META-INF/services/pitiupi.models.Plugin`:
|
||||
|
||||
```
|
||||
pacote.MeuPlugin
|
||||
```
|
||||
|
||||
## 5. Build e instalação
|
||||
|
||||
```bash
|
||||
cd pitiupi.meuPlugin && ant jar
|
||||
ln -sf ../../pitiupi.meuPlugin/dist/*.jar ../pitiupi/plugins/
|
||||
```
|
||||
|
||||
Reinicie o Pitiupi após trocar JARs.
|
||||
|
||||
## 6. Teste
|
||||
|
||||
Duas instâncias, mesma rede, portas distintas se no mesmo host. Ver [00_quickstart.md](00_quickstart.md).
|
||||
|
||||
## TCP dedicado (avançado)
|
||||
|
||||
Se o plugin precisa de canal TCP com handshake por `getIdentifier()`, estude `pitiupi.mpChat` e os métodos opcionais em `Plugin` (`receivePrivateConnection`, `receiveBytes`). Ver [11_plugins_referencia.md](11_plugins_referencia.md).
|
||||
|
||||
## Erros frequentes
|
||||
|
||||
| Problema | Causa provável |
|
||||
| --- | --- |
|
||||
| Plugin não aparece | SPI ausente ou JAR fora de `plugins/` |
|
||||
| `ClassNotFoundException` na recepção | Classe da mensagem não no JAR do remetente/destino |
|
||||
| Peers não se veem | Multicast bloqueado — [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md) |
|
||||
@@ -0,0 +1,59 @@
|
||||
# Plugins de referência
|
||||
|
||||
Três exemplos no repositório para comparar estilos de integração. Todos devem ser empacotados em `pitiupi/plugins/`.
|
||||
|
||||
## Comparativo
|
||||
|
||||
| | pitiupi.chat | pitiupi.screenCast | pitiupi.mpChat |
|
||||
| --- | --- | --- | --- |
|
||||
| Build | Ant | Ant | Make |
|
||||
| Classe | `ChatPlugin` | `ScreenCastPlugin` | `MultiProtocolChat` |
|
||||
| Categoria menu | `Social` | (menu custom via `GuiManager`) | `Social` (multicast + unicast) |
|
||||
| Mensagem | `ChatMessage` | `ScreenCastMessage` | `MpChatMessage` |
|
||||
| Transporte principal | UDP (via `ConnectionManager`) | UDP / payloads maiores | UDP público + **TCP privado** |
|
||||
| TCP avançado | Não | Depende da versão do módulo | `receivePrivateConnection`, `receiveBytes` |
|
||||
|
||||
## pitiupi.chat
|
||||
|
||||
- **Referência canônica** alinhada ao contrato atual `Plugin` + `ConnectionManager` / `GuiManager`.
|
||||
- `createPlugin` registra `ChatMenu` em `MenuCategory.Social`.
|
||||
- `getMessages()` retorna `ChatMessage.class`; `receiveMessage` atualiza `ChatWindow`.
|
||||
|
||||
Caminhos: `pitiupi.chat/src/pitiupi/chat/plugin/ChatPlugin.java`.
|
||||
|
||||
## pitiupi.screenCast
|
||||
|
||||
- Demonstra compartilhamento de tela / imagens (`ScreenCastWindow`, `ScreenCastMessage`).
|
||||
- Útil para discutir **tamanho de datagramas**, compressão (`ImageTransformation`) e limites UDP.
|
||||
|
||||
Caminhos: `pitiupi.screenCast/src/pitiupi/screenCast/`. Alinhado ao contrato `Plugin` + `ConnectionManager` / `GuiManager` (como `ChatPlugin`).
|
||||
|
||||
## pitiupi.mpChat
|
||||
|
||||
- Chat **público** (multicast / unicast UDP) e **privado** (TCP).
|
||||
- `createPlugin(ConnectionManager, GuiManager)` como nos demais plugins; usa `ConnectionManager.connectPluginTcp` para abrir canal privado.
|
||||
- Sobrescreve hooks TCP do `Plugin` (`receivePrivateConnection`, `receiveBytes`, `onSocketClosed`).
|
||||
|
||||
Caminhos: `pitiupi.mpChat/src/plugin/MultiProtocolChat.java`.
|
||||
|
||||
## Fluxo UDP (todos os plugins)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as MainWindow
|
||||
participant UDP as SocketUDP
|
||||
participant HB as HeartbeatManager
|
||||
participant PM as PluginManager
|
||||
participant Peer as OutroPeer
|
||||
App->>UDP: join multicast
|
||||
UDP->>HB: HeartbeatMessage
|
||||
Peer->>UDP: Message plugin
|
||||
UDP->>PM: handleMessage bytes
|
||||
PM->>PM: deserialize + dispatch
|
||||
```
|
||||
|
||||
Heartbeat não passa pelos plugins — ver [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md).
|
||||
|
||||
## Próximo passo
|
||||
|
||||
Criar plugin próprio: [10_tutorial_plugin.md](10_tutorial_plugin.md).
|
||||
@@ -0,0 +1,35 @@
|
||||
# Menus — unicast e multicast
|
||||
|
||||
## MenuCategory
|
||||
|
||||
Enum em `pitiupi.models.Menus.MenuCategory`:
|
||||
|
||||
| Valor | Uso sugerido |
|
||||
| --- | --- |
|
||||
| `Jogos` | Plugins de jogos entre peers |
|
||||
| `Social` | Chat, presença, mensagens sociais |
|
||||
| `Streaming` | Screen cast, mídia |
|
||||
|
||||
## Registrar menu (padrão atual)
|
||||
|
||||
Via `GuiManager.createMenu(MenuCategory category, Menu menu)` — ver `ChatPlugin`:
|
||||
|
||||
```java
|
||||
guiManager.createMenu(MenuCategory.Social, new ChatMenu(chatWindow));
|
||||
```
|
||||
|
||||
A hierarquia de menus unicast/multicast do núcleo (`UnicastMenu`, `MulticastMenu`, etc.) permite ações por peer ou broadcast; plugins de referência encapsulam isso em classes `*Menu` próprias.
|
||||
|
||||
## Unicast vs multicast na aplicação
|
||||
|
||||
| Modo | API típica | Semântica |
|
||||
| --- | --- | --- |
|
||||
| Multicast | `ConnectionManager` → `SocketUDP.sendMulticast` | Todos no grupo na porta configurada |
|
||||
| Unicast UDP | envio para IP de um peer | Um destinatário, sem estabelecer TCP |
|
||||
| TCP | `ConnectionManager` / `SocketTCP` | Canal persistente, framing 4+N bytes |
|
||||
|
||||
Detalhes de rede: [05_udp_multicast.md](05_udp_multicast.md), [06_tcp_conexoes.md](06_tcp_conexoes.md).
|
||||
|
||||
## mpChat
|
||||
|
||||
`MulticastMpChatMenu` e `UnicastMpChatMenu` mostram como expor as duas modalidades no mesmo plugin — [11_plugins_referencia.md](11_plugins_referencia.md).
|
||||
@@ -0,0 +1,42 @@
|
||||
# Guia para desenvolvedor e agente
|
||||
|
||||
## Leitura mínima antes de mudar código
|
||||
|
||||
1. [00_quickstart.md](00_quickstart.md) — build e execução
|
||||
2. [04_inicializacao_e_gui.md](04_inicializacao_e_gui.md) — boot e diagrama
|
||||
3. [09_sistema_de_plugins.md](09_sistema_de_plugins.md) — contrato de extensão
|
||||
4. [14_arquitetura_e_manutencao.md](14_arquitetura_e_manutencao.md) — mapa de pacotes
|
||||
|
||||
## Por tipo de tarefa
|
||||
|
||||
| Tarefa | Ler primeiro |
|
||||
| --- | --- |
|
||||
| Novo plugin (aluno) | [10_tutorial_plugin.md](10_tutorial_plugin.md), [11_plugins_referencia.md](11_plugins_referencia.md) |
|
||||
| Bug UDP / multicast | [05_udp_multicast.md](05_udp_multicast.md), [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md) |
|
||||
| Bug TCP / conexão | [06_tcp_conexoes.md](06_tcp_conexoes.md) |
|
||||
| Presença / lista de usuários | [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md) |
|
||||
| Desserialização / mensagens | [07_mensagens_e_serializacao.md](07_mensagens_e_serializacao.md) |
|
||||
|
||||
## Gate de build
|
||||
|
||||
Compilar núcleo e os três plugins de referência:
|
||||
|
||||
```bash
|
||||
cd pitiupi && ant jar
|
||||
cd ../pitiupi.chat && ant jar
|
||||
cd ../pitiupi.screenCast && ant jar
|
||||
cd ../pitiupi.mpChat && make
|
||||
```
|
||||
|
||||
Requer JDK, Ant e Make no PATH. Teste manual em rede: [15_testes_e_gate.md](15_testes_e_gate.md).
|
||||
|
||||
## Convenções
|
||||
|
||||
- Código e identificadores em inglês (pacotes `pitiupi.*`).
|
||||
- Documentação em português (BR) em `docs/`.
|
||||
- Evitar alterar o núcleo para necessidades de um único plugin — preferir extensão via `Plugin`.
|
||||
|
||||
## Decisões e débitos
|
||||
|
||||
- [decision-register.md](../decision-register.md)
|
||||
- [engineering_backlog.md](engineering_backlog.md)
|
||||
@@ -0,0 +1,42 @@
|
||||
# Arquitetura e manutenção
|
||||
|
||||
## Onde editar
|
||||
|
||||
| Comportamento | Pacote / classe |
|
||||
| --- | --- |
|
||||
| Login, porta, nome | `pitiupi.GUI.Login` |
|
||||
| Orquestração GUI + plugins | `pitiupi.GUI.MainWindow` |
|
||||
| Barra de menus base | `pitiupi.GUI.MenuBar` |
|
||||
| Usuários online | `pitiupi.GUI.OnlineUsersPanel`, `HeartbeatManager` |
|
||||
| Facade rede para plugins | `pitiupi.control.ConnectionManager` |
|
||||
| Registro de menus | `pitiupi.control.GuiManager` |
|
||||
| UDP | `pitiupi.net.udp.SocketUDP` |
|
||||
| TCP servidor e mapa de conexões | `pitiupi.net.tcp.SocketTCP`, `TcpConnectionManager` |
|
||||
| Conexão TCP por peer | `pitiupi.net.tcp.TcpConnection` |
|
||||
| Handshake / fábrica TCP | `pitiupi.net.tcp.ConnectionFactory`, `HandshakeMessage` |
|
||||
| Heartbeat | `pitiupi.control.HeartbeatManager`, `HeartbeatMessage` |
|
||||
| Carregar JARs, despacho | `pitiupi.control.plugins.PluginManager` |
|
||||
| Contrato plugin | `pitiupi.models.Plugin`, `pitiupi.models.Messages.Message` |
|
||||
| Menus por categoria | `pitiupi.models.Menus.*` |
|
||||
|
||||
## Dependências entre módulos
|
||||
|
||||
```
|
||||
pitiupi (núcleo)
|
||||
↑ classpath
|
||||
pitiupi.chat | pitiupi.screenCast | pitiupi.mpChat
|
||||
```
|
||||
|
||||
Plugins **não** devem ser dependência do núcleo.
|
||||
|
||||
## Alterações arriscadas
|
||||
|
||||
- Mudar grupo multicast ou formato de heartbeat → quebra interoperabilidade na sala.
|
||||
- Mudar framing TCP → todos os plugins que usam TCP precisam acompanhar.
|
||||
- Alterar nome do arquivo SPI (`pitiupi.models.Plugin`) → todos os JARs de plugin.
|
||||
|
||||
Registrar decisão em [decision-register.md](../decision-register.md).
|
||||
|
||||
## Documentação viva
|
||||
|
||||
Ao mudar protocolo ou API pública do núcleo, atualizar o número correspondente em [documentation/README.md](README.md) e, se aplicável, [11_plugins_referencia.md](11_plugins_referencia.md).
|
||||
@@ -0,0 +1,38 @@
|
||||
# Testes e gate
|
||||
|
||||
## Estado atual
|
||||
|
||||
Não há suíte JUnit integrada ao repositório. A validação principal é:
|
||||
|
||||
1. **Build** de todos os módulos.
|
||||
2. **Teste manual** com duas instâncias na rede.
|
||||
|
||||
## Gate de build (recomendado)
|
||||
|
||||
Sequência:
|
||||
|
||||
```bash
|
||||
cd pitiupi && ant jar
|
||||
cd ../pitiupi.chat && ant jar
|
||||
cd ../pitiupi.screenCast && ant jar
|
||||
cd ../pitiupi.mpChat && make
|
||||
```
|
||||
|
||||
Falha se `java`, **`javac`**, Ant ou Make estiverem ausentes (instale `openjdk-*-jdk`, não só JRE).
|
||||
|
||||
## Checklist manual (rede)
|
||||
|
||||
- [ ] Duas instâncias com portas distintas (mesmo host) ou dois hosts na mesma LAN
|
||||
- [ ] Nomes visíveis no painel de usuários em até ~15 s
|
||||
- [ ] Plugin de referência troca mensagens entre os dois
|
||||
- [ ] Encerrar uma instância remove o peer da lista após timeout de heartbeat
|
||||
|
||||
## Checklist plugin de aluno
|
||||
|
||||
- [ ] JAR em `plugins/` + SPI correto
|
||||
- [ ] README do módulo com build e formato das mensagens
|
||||
- [ ] Demonstração mínima professor ↔ aluno ou aluno ↔ aluno
|
||||
|
||||
## Melhorias futuras
|
||||
|
||||
Ver [engineering_backlog.md](engineering_backlog.md) (testes automatizados, CI).
|
||||
@@ -0,0 +1,52 @@
|
||||
# Fundamentação — redes e código
|
||||
|
||||
Mapa entre conceitos de **Redes de Computadores** e o Pitiupi. Aprofundamento: [references/README.md](../references/README.md).
|
||||
|
||||
## Camada de aplicação P2P
|
||||
|
||||
| Conceito | No Pitiupi |
|
||||
| --- | --- |
|
||||
| Aplicação distribuída sem servidor dedicado | Cada peer executa o mesmo núcleo + plugins |
|
||||
| Identidade do participante | Nome no login + `HeartbeatMessage` (instância) |
|
||||
| Broadcast lógico | UDP multicast no grupo `224.0.0.3` |
|
||||
| Comunicação 1:1 | UDP unicast ou TCP com conexão persistente |
|
||||
|
||||
## UDP
|
||||
|
||||
- Sem conexão; mensagens delimitadas por datagrama.
|
||||
- Multicast economiza envios para todos os peers do grupo.
|
||||
- Limitações: perda, ordem, MTU — relevante para plugins com imagens (screenCast).
|
||||
|
||||
Doc: [05_udp_multicast.md](05_udp_multicast.md).
|
||||
|
||||
## TCP
|
||||
|
||||
- Conexão orientada a fluxo; o núcleo adiciona **framing** (tamanho + payload).
|
||||
- Uma conexão por endereço de peer reutilizada para várias mensagens.
|
||||
- Fechamento quando peer some do heartbeat.
|
||||
|
||||
Doc: [06_tcp_conexoes.md](06_tcp_conexoes.md).
|
||||
|
||||
## Presença
|
||||
|
||||
Heartbeats periódicos em UDP; timeout ~15 s. Analogia com “keepalive” de presença, não com camada de transporte confiável.
|
||||
|
||||
Doc: [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md).
|
||||
|
||||
## Serialização
|
||||
|
||||
Objetos Java `Serializable` em `byte[]` — acoplamento de versão de classes entre peers (mesmo plugin instalado).
|
||||
|
||||
Doc: [07_mensagens_e_serializacao.md](07_mensagens_e_serializacao.md).
|
||||
|
||||
## Extensibilidade
|
||||
|
||||
SPI e classloader por JAR — padrão de plug-in em aplicações Java.
|
||||
|
||||
Doc: [09_sistema_de_plugins.md](09_sistema_de_plugins.md).
|
||||
|
||||
## Leitura complementar
|
||||
|
||||
- [references/notas/udp_vs_tcp.md](../references/notas/udp_vs_tcp.md)
|
||||
- [references/notas/multicast_lan.md](../references/notas/multicast_lan.md)
|
||||
- [references/notas/java_spi_plugins.md](../references/notas/java_spi_plugins.md)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Documentação técnica — Pitiupi
|
||||
|
||||
Referência para **código Java**, **rede P2P** e **plugins**. Índice geral: [docs/README.md](../README.md).
|
||||
|
||||
## Ordem sugerida
|
||||
|
||||
| # | Arquivo | Conteúdo |
|
||||
| --- | --- | --- |
|
||||
| 0 | [00_quickstart.md](00_quickstart.md) | Build, plugins, duas instâncias |
|
||||
| 1 | [01_visao_disciplina_e_tp.md](01_visao_disciplina_e_tp.md) | Uso na disciplina e trabalho prático |
|
||||
| 2 | [02_modulos_do_repositorio.md](02_modulos_do_repositorio.md) | Núcleo e três plugins de referência |
|
||||
| 3 | [03_build_ant_netbeans.md](03_build_ant_netbeans.md) | Ant, NetBeans, Makefile mpChat |
|
||||
| 4 | [04_inicializacao_e_gui.md](04_inicializacao_e_gui.md) | Boot, MainWindow, diagrama |
|
||||
| 5 | [05_udp_multicast.md](05_udp_multicast.md) | SocketUDP |
|
||||
| 6 | [06_tcp_conexoes.md](06_tcp_conexoes.md) | SocketTCP, TcpConnection, framing |
|
||||
| 7 | [07_mensagens_e_serializacao.md](07_mensagens_e_serializacao.md) | Classe Message |
|
||||
| 8 | [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md) | HeartbeatManager |
|
||||
| 9 | [09_sistema_de_plugins.md](09_sistema_de_plugins.md) | Plugin, PluginManager, SPI |
|
||||
| 10 | [10_tutorial_plugin.md](10_tutorial_plugin.md) | Passo a passo novo plugin |
|
||||
| 11 | [11_plugins_referencia.md](11_plugins_referencia.md) | chat, screenCast, mpChat |
|
||||
| 12 | [12_menus_unicast_multicast.md](12_menus_unicast_multicast.md) | MenuCategory e ações |
|
||||
| 13 | [13_guia_dev_e_agente.md](13_guia_dev_e_agente.md) | Leitura para dev/agente |
|
||||
| 14 | [14_arquitetura_e_manutencao.md](14_arquitetura_e_manutencao.md) | Onde editar cada comportamento |
|
||||
| 15 | [15_testes_e_gate.md](15_testes_e_gate.md) | Build gate e testes manuais |
|
||||
| 16 | [16_fundamentacao_redes.md](16_fundamentacao_redes.md) | Teoria ↔ código |
|
||||
| — | [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md) | Portas, multicast, WSL |
|
||||
| — | [engineering_backlog.md](engineering_backlog.md) | Débitos técnicos |
|
||||
@@ -0,0 +1,38 @@
|
||||
# Apêndice — portas e multicast
|
||||
|
||||
## Configuração
|
||||
|
||||
- **Nome** e **porta** definidos no diálogo de login (`Login`).
|
||||
- A mesma porta é usada para UDP (multicast socket) e TCP (`ServerSocket`).
|
||||
- Preferências persistidas localmente (`java.util.prefs`).
|
||||
|
||||
## Multicast
|
||||
|
||||
- Grupo: **`224.0.0.3`** (fixo no código do núcleo).
|
||||
- Buffer UDP: **256000** bytes (configuração em `SocketUDP`).
|
||||
|
||||
Todos os peers na mesma LAN devem usar a **mesma porta** para participar do mesmo “grupo lógico” da aula (portas diferentes = grupos isolados).
|
||||
|
||||
## Mesmo computador
|
||||
|
||||
Execute duas instâncias com **portas diferentes** (ex.: 5000 e 5001). Mesmo nome de usuário em duas instâncias pode confundir a lista — use nomes distintos.
|
||||
|
||||
## Firewall e Wi‑Fi
|
||||
|
||||
- Bloqueio de multicast em redes de visitantes ou isolamento AP→AP impede descoberta.
|
||||
- Em laboratório, preferir cabo ou Wi‑Fi com multicast habilitado.
|
||||
|
||||
## WSL2
|
||||
|
||||
Executar o Pitiupi no WSL e peers no Windows (ou vice-versa) pode falhar em multicast. Preferir:
|
||||
|
||||
- todos os peers no mesmo ambiente (Linux nativo ou VMs), ou
|
||||
- dois PCs físicos na mesma sub-rede.
|
||||
|
||||
## Sintomas
|
||||
|
||||
| Sintoma | Verificar |
|
||||
| --- | --- |
|
||||
| Lista de usuários vazia | Porta igual entre peers? Mesma sub-rede? Firewall? |
|
||||
| Plugin não recebe | Desserialização / SPI / JAR em `plugins/` |
|
||||
| TCP falha | Peer ativo no heartbeat? IP correto? |
|
||||
@@ -0,0 +1,11 @@
|
||||
# Engineering backlog
|
||||
|
||||
| Item | Prioridade | Notas |
|
||||
| --- | --- | --- |
|
||||
| Completar tutorial legado em `pitiupi/docs/PluginTutorial.md` | Média | Substituído por [10_tutorial_plugin.md](10_tutorial_plugin.md) |
|
||||
| Testes JUnit para desserialização e framing TCP | Média | Sem rede real |
|
||||
| CI (GitHub Actions) com `ant jar` + `make` | Baixa | Requer imagem com Ant |
|
||||
|
||||
Itens concluídos recentemente: alinhamento de `pitiupi.mpChat` a `ConnectionManager` / `GuiManager`; documentação de JDK mínimo no [README](../../README.md) e [00_quickstart.md](00_quickstart.md).
|
||||
|
||||
Ao fechar um item, atualizar esta tabela e o doc técnico relacionado.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Referências — Pitiupi
|
||||
|
||||
Bibliografia e notas curtas para apoiar a disciplina de Redes de Computadores.
|
||||
|
||||
| Recurso | Descrição |
|
||||
| --- | --- |
|
||||
| [bibliografia.bib](bibliografia.bib) | Entradas BibTeX (livro-texto, RFCs, documentação Java) |
|
||||
| [notas/](notas/) | Resumos em PT-BR ligados ao código |
|
||||
| [pdfs/](pdfs/) | PDFs locais (não versionados se grandes; ver `.gitkeep`) |
|
||||
|
||||
## Notas disponíveis
|
||||
|
||||
- [udp_vs_tcp.md](notas/udp_vs_tcp.md)
|
||||
- [multicast_lan.md](notas/multicast_lan.md)
|
||||
- [java_serializacao.md](notas/java_serializacao.md)
|
||||
- [java_spi_plugins.md](notas/java_spi_plugins.md)
|
||||
|
||||
Voltar ao índice: [docs/README.md](../README.md).
|
||||
@@ -0,0 +1,24 @@
|
||||
@book{tanenbaum2011,
|
||||
title = {Computer Networks},
|
||||
author = {Tanenbaum, Andrew S. and Wetherall, David J.},
|
||||
edition = {5},
|
||||
year = {2011},
|
||||
publisher = {Prentice Hall},
|
||||
note = {UDP, TCP, multicast — conceitos gerais}
|
||||
}
|
||||
|
||||
@misc{rfc1112,
|
||||
title = {Host Extensions for IP Multicasting},
|
||||
author = {Deering, S.},
|
||||
howpublished = {RFC 1112},
|
||||
year = {1989},
|
||||
url = {https://www.rfc-editor.org/rfc/rfc1112}
|
||||
}
|
||||
|
||||
@book{oracle_networking,
|
||||
title = {Java Platform, Standard Edition Documentation: Networking},
|
||||
author = {{Oracle}},
|
||||
year = {2024},
|
||||
url = {https://docs.oracle.com/en/java/javase/},
|
||||
note = {MulticastSocket, Socket, serialization}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Serialização Java (nota)
|
||||
|
||||
O Pitiupi serializa objetos `Message` (Java `Serializable`) para `byte[]` antes do envio.
|
||||
|
||||
Implicações:
|
||||
|
||||
- Mesma versão de classe nos peers (mesmo JAR de plugin).
|
||||
- `serialVersionUID` explícito evita incompatibilidade silenciosa.
|
||||
- Desserialização no núcleo usa `PluginObjectInputStream` com classloader do plugin.
|
||||
|
||||
Doc: [07_mensagens_e_serializacao.md](../../documentation/07_mensagens_e_serializacao.md).
|
||||
@@ -0,0 +1,9 @@
|
||||
# Java SPI — plugins (nota)
|
||||
|
||||
O núcleo usa `ServiceLoader` para localizar implementações de `pitiupi.models.Plugin` declaradas em:
|
||||
|
||||
`META-INF/services/pitiupi.models.Plugin`
|
||||
|
||||
Uma linha por classe concreta (FQCN). O JAR deve estar em `pitiupi/plugins/`.
|
||||
|
||||
Doc: [09_sistema_de_plugins.md](../../documentation/09_sistema_de_plugins.md).
|
||||
@@ -0,0 +1,12 @@
|
||||
# Multicast em LAN (nota)
|
||||
|
||||
IP multicast envia um datagrama que roteadores/switches encaminham aos assinantes do grupo (ex.: `224.0.0.3`).
|
||||
|
||||
Na sala de aula:
|
||||
|
||||
- Todos os peers devem estar na mesma sub-rede e permitir IGMP/multicast.
|
||||
- Wi‑Fi com “client isolation” impede descoberta.
|
||||
|
||||
Operação no código: [appendix_portas_e_multicast.md](../../documentation/appendix_portas_e_multicast.md).
|
||||
|
||||
RFC introdutória: ver entrada `rfc1112` em [bibliografia.bib](../bibliografia.bib).
|
||||
@@ -0,0 +1,10 @@
|
||||
# UDP vs TCP (nota)
|
||||
|
||||
| | UDP | TCP |
|
||||
| --- | --- | --- |
|
||||
| Conexão | Não | Sim |
|
||||
| Confiabilidade | Melhor esforço | Entrega ordenada na conexão |
|
||||
| Delimitação de mensagem | Por datagrama | Fluxo de bytes — Pitiupi usa framing 4+N |
|
||||
| Uso no Pitiupi | Heartbeat, multicast, plugins | Canais diretos peer-a-peer |
|
||||
|
||||
Ver [05_udp_multicast.md](../../documentation/05_udp_multicast.md) e [06_tcp_conexoes.md](../../documentation/06_tcp_conexoes.md).
|
||||
Whitespace-only changes.
@@ -1,6 +0,0 @@
|
||||
#Fri, 02 Oct 2026 20:11:21 -0300
|
||||
|
||||
|
||||
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.chat=
|
||||
|
||||
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi=
|
||||
@@ -1 +0,0 @@
|
||||
pitiupi.chat.plugin.ChatPlugin
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
package GUI;
|
||||
|
||||
import net.MpChatMessage;
|
||||
import pitiupi.control.ConnectionManager;
|
||||
import pitiupi.net.tcp.TcpConnection;
|
||||
import pitiupi.plugin.Pitiupi;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -12,17 +12,17 @@ import java.util.logging.Logger;
|
||||
|
||||
public class PrivateChatWindow extends JFrame {
|
||||
|
||||
private final Pitiupi pitiupi;
|
||||
private final ConnectionManager connectionManager;
|
||||
private final TcpConnection connection;
|
||||
|
||||
private final JTextArea receivedMessagesArea;
|
||||
private final JTextField messageField;
|
||||
|
||||
public PrivateChatWindow(
|
||||
Pitiupi pitiupi,
|
||||
ConnectionManager connectionManager,
|
||||
TcpConnection connection
|
||||
) {
|
||||
this.pitiupi = pitiupi;
|
||||
this.connectionManager = connectionManager;
|
||||
this.connection = connection;
|
||||
|
||||
setTitle("Chat privado");
|
||||
@@ -81,7 +81,7 @@ public class PrivateChatWindow extends JFrame {
|
||||
|
||||
private void sendTCP() {
|
||||
try {
|
||||
String nome = pitiupi.getUserName();
|
||||
String nome = connectionManager.getUserName();
|
||||
|
||||
MpChatMessage message = new MpChatMessage(
|
||||
nome,
|
||||
|
||||
@@ -2,7 +2,6 @@ package GUI;
|
||||
|
||||
import net.MpChatMessage;
|
||||
import pitiupi.control.ConnectionManager;
|
||||
import pitiupi.plugin.Pitiupi;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.BadLocationException;
|
||||
@@ -15,14 +14,14 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class PublicChatWindow extends JFrame {
|
||||
private final Pitiupi pitiupi;
|
||||
private final ConnectionManager connectionManager;
|
||||
|
||||
private JTextPane receivedMessagesArea;
|
||||
private JTextField messageField;
|
||||
private JTextField ipField;
|
||||
|
||||
public PublicChatWindow(Pitiupi pitiupi) {
|
||||
this.pitiupi = pitiupi;
|
||||
public PublicChatWindow(ConnectionManager connectionManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
|
||||
initComponents();
|
||||
}
|
||||
@@ -120,7 +119,7 @@ public class PublicChatWindow extends JFrame {
|
||||
|
||||
private MpChatMessage createMessage(String tipoMensagem) {
|
||||
try {
|
||||
String nome = pitiupi.getUserName();
|
||||
String nome = connectionManager.getUserName();
|
||||
String ip = InetAddress.getLocalHost().getHostAddress();
|
||||
String message = messageField.getText();
|
||||
|
||||
@@ -177,7 +176,7 @@ public class PublicChatWindow extends JFrame {
|
||||
|
||||
InetAddress ip = InetAddress.getByName(ipField.getText());
|
||||
|
||||
pitiupi.sendTcp(msg, ip);
|
||||
connectionManager.sendTcp(msg, ip);
|
||||
receiveMessage(msg);
|
||||
|
||||
} catch (Exception ex) {
|
||||
@@ -193,7 +192,7 @@ public class PublicChatWindow extends JFrame {
|
||||
|
||||
InetAddress ip = InetAddress.getByName(ipField.getText());
|
||||
|
||||
pitiupi.sendUdpUnicast(msg, ip);
|
||||
connectionManager.sendUdpUnicast(msg, ip);
|
||||
receiveMessage(msg);
|
||||
|
||||
} catch (Exception ex) {
|
||||
@@ -207,7 +206,7 @@ public class PublicChatWindow extends JFrame {
|
||||
try {
|
||||
MpChatMessage msg = createMessage("UDP Multicast");
|
||||
|
||||
pitiupi.sendUdpMulticast(msg);
|
||||
connectionManager.sendUdpMulticast(msg);
|
||||
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package control;
|
||||
|
||||
import GUI.ChatMenu;
|
||||
import GUI.ChatWindow;
|
||||
import GUI.PrivateChatWindow;
|
||||
import GUI.PublicChatWindow;
|
||||
import net.MpChatMessage;
|
||||
import pitiupi.control.ConnectionManager;
|
||||
import pitiupi.control.PeerListener;
|
||||
import pitiupi.models.User;
|
||||
import pitiupi.net.tcp.TcpConnection;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ChatController implements PeerListener {
|
||||
ConnectionManager connectionManager;
|
||||
ChatMenu chatMenu;
|
||||
|
||||
PublicChatWindow publicChatWindow;
|
||||
|
||||
private final HashMap<TcpConnection, ChatWindow> chatWindows;
|
||||
|
||||
public ChatController(ConnectionManager connectionManager, ChatMenu chatMenu) {
|
||||
this.connectionManager = connectionManager;
|
||||
publicChatWindow = new PublicChatWindow(connectionManager);
|
||||
|
||||
chatMenus = new HashMap<>();
|
||||
chatWindows = new HashMap<>();
|
||||
|
||||
this.chatMenu = chatMenu;
|
||||
|
||||
this.chatMenu.newMenuItem(new JMenuItem("Show global chat"), publicChatWindow);
|
||||
}
|
||||
|
||||
public void addChatWindow(TcpConnection tcpConnection) {
|
||||
if (chatWindows.containsKey(tcpConnection)) return;
|
||||
|
||||
PrivateChatWindow newChat = new PrivateChatWindow(connectionManager, tcpConnection);
|
||||
chatWindows.put(tcpConnection, newChat);
|
||||
JMenuItem newMenuItem = new JMenuItem();
|
||||
chatMenus.put(tcpConnection, newMenuItem);
|
||||
newMenuItem.setText(tcpConnection.getAddress().toString());
|
||||
chatMenu.newMenuItem(newMenuItem, newChat);
|
||||
}
|
||||
|
||||
public void removeChatWindow(TcpConnection tcpConnection) {
|
||||
ChatWindow removedWindow = chatWindows.remove(tcpConnection);
|
||||
JMenuItem removedMenu = chatMenus.remove(tcpConnection);
|
||||
|
||||
chatMenu.removeMenuItem(removedMenu);
|
||||
removedWindow.setVisible(false);
|
||||
removedWindow.dispose();
|
||||
}
|
||||
|
||||
public void receivePublic(MpChatMessage message) {
|
||||
publicChatWindow.receiveMessage(message);
|
||||
}
|
||||
public void receivePrivate(TcpConnection tcpConnection, MpChatMessage message) {
|
||||
chatWindows.get(tcpConnection).receiveMessage(message);
|
||||
}
|
||||
|
||||
public void openChatWindow(InetAddress address) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeersChanged(List<User> list) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package control;
|
||||
|
||||
import GUI.PrivateChatWindow;
|
||||
import net.MpChatMessage;
|
||||
import pitiupi.control.ConnectionManager;
|
||||
import pitiupi.models.User;
|
||||
import pitiupi.net.tcp.TcpConnection;
|
||||
import pitiupi.plugin.Pitiupi;
|
||||
import plugin.MultiProtocolChat;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -14,7 +14,7 @@ import java.util.Set;
|
||||
|
||||
public class PrivateChatController {
|
||||
|
||||
private final Pitiupi pitiupi;
|
||||
private final ConnectionManager connectionManager;
|
||||
private final MultiProtocolChat multiProtocolChat;
|
||||
|
||||
/*
|
||||
@@ -29,16 +29,20 @@ public class PrivateChatController {
|
||||
private final Set<String> pendingConnections;
|
||||
|
||||
public PrivateChatController(
|
||||
Pitiupi pitiupi,
|
||||
ConnectionManager connectionManager,
|
||||
MultiProtocolChat multiProtocolChat
|
||||
) {
|
||||
this.pitiupi = pitiupi;
|
||||
this.connectionManager = connectionManager;
|
||||
this.multiProtocolChat = multiProtocolChat;
|
||||
|
||||
this.windows = new HashMap<>();
|
||||
this.pendingConnections = new HashSet<>();
|
||||
}
|
||||
|
||||
private static String addressKey(TcpConnection connection) {
|
||||
return connection.getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
public void openChat(User user) {
|
||||
String address = user.getAddress().getHostAddress();
|
||||
|
||||
@@ -70,7 +74,7 @@ public class PrivateChatController {
|
||||
}
|
||||
|
||||
public void receiveMessage(TcpConnection connection, MpChatMessage message) {
|
||||
String address = connection.getAddress().toString();
|
||||
String address = addressKey(connection);
|
||||
|
||||
PrivateChatWindow window = windows.get(address);
|
||||
|
||||
@@ -85,12 +89,12 @@ public class PrivateChatController {
|
||||
* Deve ser chamado quando uma nova conexão privada chegar.
|
||||
*/
|
||||
public void connectionReceived(TcpConnection connection) {
|
||||
String address = connection.getAddress().toString();
|
||||
String address = addressKey(connection);
|
||||
|
||||
pendingConnections.remove(address);
|
||||
|
||||
PrivateChatWindow window = new PrivateChatWindow(
|
||||
pitiupi,
|
||||
connectionManager,
|
||||
connection
|
||||
);
|
||||
|
||||
@@ -105,7 +109,7 @@ public class PrivateChatController {
|
||||
* Fecha a janela e remove o relacionamento.
|
||||
*/
|
||||
public void closeChat(TcpConnection connection) {
|
||||
String address = connection.getAddress().toString();
|
||||
String address = addressKey(connection);
|
||||
|
||||
PrivateChatWindow window = windows.remove(address);
|
||||
|
||||
@@ -113,4 +117,4 @@ public class PrivateChatController {
|
||||
window.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package plugin;
|
||||
|
||||
import GUI.MulticastMpChatMenu;
|
||||
import GUI.PrivateChatWindow;
|
||||
import GUI.PublicChatWindow;
|
||||
import GUI.UnicastMpChatMenu;
|
||||
import control.PrivateChatController;
|
||||
import net.MpChatMessage;
|
||||
import pitiupi.control.ConnectionManager;
|
||||
import pitiupi.control.GuiManager;
|
||||
import pitiupi.models.Menus.MenuCategory;
|
||||
import pitiupi.models.Messages.Message;
|
||||
import pitiupi.net.tcp.TcpConnection;
|
||||
import pitiupi.models.Plugin;
|
||||
import pitiupi.plugin.Pitiupi;
|
||||
import pitiupi.net.tcp.TcpConnection;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -20,7 +20,7 @@ import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiProtocolChat extends Plugin {
|
||||
Pitiupi pitiupi;
|
||||
private ConnectionManager connectionManager;
|
||||
|
||||
PrivateChatController privateChatController;
|
||||
PublicChatWindow publicChatWindow;
|
||||
@@ -46,18 +46,18 @@ public class MultiProtocolChat extends Plugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createPlugin(Pitiupi pitiupi) {
|
||||
this.pitiupi = pitiupi;
|
||||
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
|
||||
privateChatController = new PrivateChatController(pitiupi, this);
|
||||
privateChatController = new PrivateChatController(connectionManager, this);
|
||||
|
||||
publicChatWindow = new PublicChatWindow(pitiupi);
|
||||
publicChatWindow = new PublicChatWindow(connectionManager);
|
||||
|
||||
MulticastMpChatMenu multicastMpChatMenu = new MulticastMpChatMenu(publicChatWindow);
|
||||
UnicastMpChatMenu unicastMpChatMenu = new UnicastMpChatMenu(privateChatController);
|
||||
|
||||
pitiupi.createMenu(MenuCategory.Social, multicastMpChatMenu);
|
||||
pitiupi.createMenu(MenuCategory.Social, unicastMpChatMenu);
|
||||
guiManager.createMenu(MenuCategory.Social, multicastMpChatMenu);
|
||||
guiManager.createMenu(MenuCategory.Social, unicastMpChatMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,7 +102,11 @@ public class MultiProtocolChat extends Plugin {
|
||||
|
||||
public void openConnection(String ip) {
|
||||
try {
|
||||
pitiupi.connectTcp(InetAddress.getByName(ip), getIdentifier(), getIdentifier());
|
||||
connectionManager.connectPluginTcp(
|
||||
InetAddress.getByName(ip),
|
||||
getIdentifier(),
|
||||
getIdentifier()
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("Error opening connection: " + e.getMessage());
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#Tue, 29 Sep 2026 11:04:58 -0300
|
||||
|
||||
|
||||
C\:\\Users\\Gustavo\\OneDrive\\Documentos\\Faculdade\\2026_2\\Monitoria\\pitiupi\\pitiupi.screenCast=
|
||||
|
||||
C\:\\Users\\Gustavo\\OneDrive\\Documentos\\Faculdade\\2026_2\\Monitoria\\pitiupi\\pitiupi=
|
||||
@@ -1 +0,0 @@
|
||||
pitiupi.screenCast.plugin.ScreenCastPlugin
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
========================
|
||||
BUILD OUTPUT DESCRIPTION
|
||||
========================
|
||||
|
||||
When you build an Java application project that has a main class, the IDE
|
||||
automatically copies all of the JAR
|
||||
files on the projects classpath to your projects dist/lib folder. The IDE
|
||||
also adds each of the JAR files to the Class-Path element in the application
|
||||
JAR files manifest file (MANIFEST.MF).
|
||||
|
||||
To run the project from the command line, go to the dist folder and
|
||||
type the following:
|
||||
|
||||
java -jar "pitiupi.screenCast.jar"
|
||||
|
||||
To distribute this project, zip up the dist folder (including the lib folder)
|
||||
and distribute the ZIP file.
|
||||
|
||||
Notes:
|
||||
|
||||
* If two JAR files on the project classpath have the same name, only the first
|
||||
JAR file is copied to the lib folder.
|
||||
* Only JAR files are copied to the lib folder.
|
||||
If the classpath contains other types of files or folders, these files (folders)
|
||||
are not copied.
|
||||
* If a library on the projects classpath also has a Class-Path element
|
||||
specified in the manifest,the content of the Class-Path element has to be on
|
||||
the projects runtime path.
|
||||
* To set a main class in a standard Java project, right-click the project node
|
||||
in the Projects window and choose Properties. Then click Run and enter the
|
||||
class name in the Main Class field. Alternatively, you can manually type the
|
||||
class name in the manifest Main-Class element.
|
||||
@@ -4,28 +4,49 @@
|
||||
*/
|
||||
package pitiupi.screenCast.GUI;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.JMenu;
|
||||
import java.util.List;
|
||||
|
||||
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;
|
||||
import pitiupi.models.Menus.MulticastMenus.MulticastMenu;
|
||||
import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author flavio
|
||||
*/
|
||||
public class ScreenCastMenu extends JMenu {
|
||||
public class ScreenCastMenu extends MulticastSubMenu {
|
||||
|
||||
private final ScreenCastWindow screenCastWindow;
|
||||
private final javax.swing.JMenuItem menuItem;
|
||||
private final ScreenCastShowMenu showMenu;
|
||||
|
||||
public ScreenCastMenu(ScreenCastWindow screenCastWindow) {
|
||||
this.screenCastWindow = screenCastWindow;
|
||||
this.setText("Screen Cast");
|
||||
menuItem = new javax.swing.JMenuItem();
|
||||
menuItem.setText("ScreenCast Window");
|
||||
menuItem.addActionListener(this::chatMenuActionPerformed);
|
||||
this.add(menuItem);
|
||||
this.showMenu = new ScreenCastShowMenu(screenCastWindow);
|
||||
}
|
||||
|
||||
private void chatMenuActionPerformed(ActionEvent ae) {
|
||||
this.screenCastWindow.setVisible(true);
|
||||
@Override
|
||||
public String getText() {
|
||||
return "Screen Cast";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MulticastMenu> getMenus() {
|
||||
return List.of(showMenu);
|
||||
}
|
||||
|
||||
private static final class ScreenCastShowMenu extends MulticastActionMenu {
|
||||
private final ScreenCastWindow screenCastWindow;
|
||||
|
||||
ScreenCastShowMenu(ScreenCastWindow screenCastWindow) {
|
||||
this.screenCastWindow = screenCastWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "ScreenCast Window";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
screenCastWindow.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,6 @@ import java.util.logging.Logger;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import pitiupi.GUI.MainWindow;
|
||||
import pitiupi.net.Message;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
package pitiupi.screenCast.net;
|
||||
|
||||
import pitiupi.net.Message;
|
||||
import pitiupi.models.Messages.Message;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -46,7 +46,7 @@ public class ScreenCastMessage extends Message {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imageByte the image to set
|
||||
* @param imageByte the imageByte to set
|
||||
*/
|
||||
public void setImageByte(byte[] imageByte) {
|
||||
this.imageByte = imageByte;
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
*/
|
||||
package pitiupi.screenCast.plugin;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import pitiupi.GUI.MainWindow;
|
||||
import java.util.List;
|
||||
|
||||
import pitiupi.control.ConnectionManager;
|
||||
import pitiupi.control.GuiManager;
|
||||
import pitiupi.net.Message;
|
||||
import pitiupi.plugin.Plugin;
|
||||
import pitiupi.models.Menus.MenuCategory;
|
||||
import pitiupi.models.Messages.Message;
|
||||
import pitiupi.models.Plugin;
|
||||
import pitiupi.screenCast.GUI.ScreenCastMenu;
|
||||
import pitiupi.screenCast.GUI.ScreenCastWindow;
|
||||
import pitiupi.screenCast.net.ScreenCastMessage;
|
||||
@@ -20,7 +19,7 @@ import pitiupi.screenCast.net.ScreenCastMessage;
|
||||
*
|
||||
* @author flavio
|
||||
*/
|
||||
public class ScreenCastPlugin implements Plugin {
|
||||
public class ScreenCastPlugin extends Plugin {
|
||||
|
||||
private ScreenCastWindow screenCastWindow;
|
||||
|
||||
@@ -47,26 +46,18 @@ public class ScreenCastPlugin implements Plugin {
|
||||
@Override
|
||||
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
|
||||
this.screenCastWindow = new ScreenCastWindow(connectionManager, guiManager);
|
||||
guiManager.addMenu(new ScreenCastMenu(screenCastWindow));
|
||||
guiManager.createMenu(MenuCategory.Social, new ScreenCastMenu(screenCastWindow));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(byte[] message) {
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(message);
|
||||
ObjectInputStream ois;
|
||||
try {
|
||||
ois = new ObjectInputStream(bis);
|
||||
if (ois.readObject() instanceof ScreenCastMessage msg) {
|
||||
this.screenCastWindow.receiveScreenCastMessage(msg);
|
||||
}
|
||||
} catch (IOException | ClassNotFoundException ex) {
|
||||
System.out.println(ex.toString());
|
||||
public List<Class<? extends Message>> getMessages() {
|
||||
return List.of(ScreenCastMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Message message) {
|
||||
if (message instanceof ScreenCastMessage msg) {
|
||||
this.screenCastWindow.receiveScreenCastMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getMessage() {
|
||||
return new ScreenCastMessage();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,547 +1,5 @@
|
||||
# Visão geral
|
||||
# Documentação (legado)
|
||||
|
||||
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.
|
||||
Este arquivo foi substituído pela árvore em **[docs/](../../docs/README.md)**.
|
||||
|
||||
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.models.Plugin
|
||||
```
|
||||
|
||||
O arquivo `pitiupi.models.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.
|
||||
Conteúdo técnico fatiado em [docs/documentation/](../../docs/documentation/README.md).
|
||||
@@ -1,2 +1,5 @@
|
||||
# Como criar plugins para o Share my Sheet
|
||||
# Tutorial de plugin (legado)
|
||||
|
||||
Use o tutorial atualizado:
|
||||
|
||||
**[docs/documentation/10_tutorial_plugin.md](../../docs/documentation/10_tutorial_plugin.md)**
|
||||
@@ -0,0 +1,10 @@
|
||||
# Documentação movida
|
||||
|
||||
A documentação canônica do Pitiupi está na raiz do repositório:
|
||||
|
||||
**[../../docs/README.md](../../docs/README.md)**
|
||||
|
||||
Arquivos legados nesta pasta:
|
||||
|
||||
- [Documentation.md](Documentation.md) — redirecionamento
|
||||
- [PluginTutorial.md](PluginTutorial.md) — redirecionamento
|
||||
@@ -0,0 +1,18 @@
|
||||
# Plugins instalados
|
||||
|
||||
O núcleo carrega arquivos `.jar` desta pasta ao iniciar (relativo ao diretório de trabalho — execute `java -jar dist/pitiupi.jar` a partir de `pitiupi/`).
|
||||
|
||||
## Desenvolvimento
|
||||
|
||||
Após compilar os módulos de referência, use symlinks (Linux):
|
||||
|
||||
```bash
|
||||
cd pitiupi/plugins
|
||||
ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar
|
||||
ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar
|
||||
ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar
|
||||
```
|
||||
|
||||
Ordem de build: núcleo (`pitiupi`) primeiro, depois cada plugin. Ver [docs/documentation/00_quickstart.md](../../docs/documentation/00_quickstart.md).
|
||||
|
||||
Os JARs gerados não devem ser commitados — apenas este README e symlinks locais.
|
||||
Reference in new issue
Block a user