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
+1406
-756
No files matched your search
@@ -1,9 +1,13 @@
|
|||||||
/pitiupi/build/
|
/pitiupi/build/
|
||||||
/pitiupi/dist/
|
/pitiupi/dist/
|
||||||
|
/pitiupi/plugins/*.jar
|
||||||
/pitiupi.chat/build/
|
/pitiupi.chat/build/
|
||||||
/pitiupi.chat/dist/
|
/pitiupi.chat/dist/
|
||||||
/pitiupi.chat/nbproject/private/
|
/pitiupi.chat/nbproject/private/
|
||||||
/pitiupi.screenCast/build/
|
/pitiupi.screenCast/build/
|
||||||
/pitiupi.screenCast/dist/
|
/pitiupi.screenCast/dist/
|
||||||
|
/pitiupi.mpChat/out/
|
||||||
|
/pitiupi.mpChat/dist/
|
||||||
|
**/built-jar.properties
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
@@ -1,29 +1,50 @@
|
|||||||
# pitiupi
|
# 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:
|
**Documentação completa:** [docs/README.md](docs/README.md)
|
||||||
- A ferramenta pitiupi (P2P)
|
|
||||||
- Um plugin de Chat
|
|
||||||
- Um plugin de ScreenCast
|
|
||||||
|
|
||||||
Cada um destes projetos deve ser compilado individualmente. Para isso, use a ferramenta ant no diretório do projeto.
|
## Pré-requisitos
|
||||||
Ex:
|
|
||||||
```
|
| Ferramenta | Uso |
|
||||||
cd pitiupi.chat
|
| --- | --- |
|
||||||
ant jar
|
| **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
|
||||||
```
|
```
|
||||||
|
|
||||||
Feito isso, os arquivos jar dos plugins deverão ser copiados ou linkados na pasta plugin da ferramenta.
|
## Módulos
|
||||||
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:
|
|
||||||
|
|
||||||
```
|
| 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
|
cd pitiupi/plugins
|
||||||
ln -s ../../pitiupi.chat/dist/*.jar .
|
ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar
|
||||||
ln -s ../../pitiupi.screenCast/dist/*.jar .
|
ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar
|
||||||
|
ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar
|
||||||
```
|
```
|
||||||
Isso deve resolver!
|
|
||||||
|
|
||||||
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;
|
package GUI;
|
||||||
|
|
||||||
import net.MpChatMessage;
|
import net.MpChatMessage;
|
||||||
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.net.tcp.TcpConnection;
|
import pitiupi.net.tcp.TcpConnection;
|
||||||
import pitiupi.plugin.Pitiupi;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -12,17 +12,17 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
public class PrivateChatWindow extends JFrame {
|
public class PrivateChatWindow extends JFrame {
|
||||||
|
|
||||||
private final Pitiupi pitiupi;
|
private final ConnectionManager connectionManager;
|
||||||
private final TcpConnection connection;
|
private final TcpConnection connection;
|
||||||
|
|
||||||
private final JTextArea receivedMessagesArea;
|
private final JTextArea receivedMessagesArea;
|
||||||
private final JTextField messageField;
|
private final JTextField messageField;
|
||||||
|
|
||||||
public PrivateChatWindow(
|
public PrivateChatWindow(
|
||||||
Pitiupi pitiupi,
|
ConnectionManager connectionManager,
|
||||||
TcpConnection connection
|
TcpConnection connection
|
||||||
) {
|
) {
|
||||||
this.pitiupi = pitiupi;
|
this.connectionManager = connectionManager;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
|
||||||
setTitle("Chat privado");
|
setTitle("Chat privado");
|
||||||
@@ -81,7 +81,7 @@ public class PrivateChatWindow extends JFrame {
|
|||||||
|
|
||||||
private void sendTCP() {
|
private void sendTCP() {
|
||||||
try {
|
try {
|
||||||
String nome = pitiupi.getUserName();
|
String nome = connectionManager.getUserName();
|
||||||
|
|
||||||
MpChatMessage message = new MpChatMessage(
|
MpChatMessage message = new MpChatMessage(
|
||||||
nome,
|
nome,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package GUI;
|
|||||||
|
|
||||||
import net.MpChatMessage;
|
import net.MpChatMessage;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.plugin.Pitiupi;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.text.BadLocationException;
|
import javax.swing.text.BadLocationException;
|
||||||
@@ -15,14 +14,14 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class PublicChatWindow extends JFrame {
|
public class PublicChatWindow extends JFrame {
|
||||||
private final Pitiupi pitiupi;
|
private final ConnectionManager connectionManager;
|
||||||
|
|
||||||
private JTextPane receivedMessagesArea;
|
private JTextPane receivedMessagesArea;
|
||||||
private JTextField messageField;
|
private JTextField messageField;
|
||||||
private JTextField ipField;
|
private JTextField ipField;
|
||||||
|
|
||||||
public PublicChatWindow(Pitiupi pitiupi) {
|
public PublicChatWindow(ConnectionManager connectionManager) {
|
||||||
this.pitiupi = pitiupi;
|
this.connectionManager = connectionManager;
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
}
|
}
|
||||||
@@ -120,7 +119,7 @@ public class PublicChatWindow extends JFrame {
|
|||||||
|
|
||||||
private MpChatMessage createMessage(String tipoMensagem) {
|
private MpChatMessage createMessage(String tipoMensagem) {
|
||||||
try {
|
try {
|
||||||
String nome = pitiupi.getUserName();
|
String nome = connectionManager.getUserName();
|
||||||
String ip = InetAddress.getLocalHost().getHostAddress();
|
String ip = InetAddress.getLocalHost().getHostAddress();
|
||||||
String message = messageField.getText();
|
String message = messageField.getText();
|
||||||
|
|
||||||
@@ -177,7 +176,7 @@ public class PublicChatWindow extends JFrame {
|
|||||||
|
|
||||||
InetAddress ip = InetAddress.getByName(ipField.getText());
|
InetAddress ip = InetAddress.getByName(ipField.getText());
|
||||||
|
|
||||||
pitiupi.sendTcp(msg, ip);
|
connectionManager.sendTcp(msg, ip);
|
||||||
receiveMessage(msg);
|
receiveMessage(msg);
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -193,7 +192,7 @@ public class PublicChatWindow extends JFrame {
|
|||||||
|
|
||||||
InetAddress ip = InetAddress.getByName(ipField.getText());
|
InetAddress ip = InetAddress.getByName(ipField.getText());
|
||||||
|
|
||||||
pitiupi.sendUdpUnicast(msg, ip);
|
connectionManager.sendUdpUnicast(msg, ip);
|
||||||
receiveMessage(msg);
|
receiveMessage(msg);
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -207,7 +206,7 @@ public class PublicChatWindow extends JFrame {
|
|||||||
try {
|
try {
|
||||||
MpChatMessage msg = createMessage("UDP Multicast");
|
MpChatMessage msg = createMessage("UDP Multicast");
|
||||||
|
|
||||||
pitiupi.sendUdpMulticast(msg);
|
connectionManager.sendUdpMulticast(msg);
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.getLogger(
|
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 GUI.PrivateChatWindow;
|
||||||
import net.MpChatMessage;
|
import net.MpChatMessage;
|
||||||
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.models.User;
|
import pitiupi.models.User;
|
||||||
import pitiupi.net.tcp.TcpConnection;
|
import pitiupi.net.tcp.TcpConnection;
|
||||||
import pitiupi.plugin.Pitiupi;
|
|
||||||
import plugin.MultiProtocolChat;
|
import plugin.MultiProtocolChat;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -14,7 +14,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class PrivateChatController {
|
public class PrivateChatController {
|
||||||
|
|
||||||
private final Pitiupi pitiupi;
|
private final ConnectionManager connectionManager;
|
||||||
private final MultiProtocolChat multiProtocolChat;
|
private final MultiProtocolChat multiProtocolChat;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -29,16 +29,20 @@ public class PrivateChatController {
|
|||||||
private final Set<String> pendingConnections;
|
private final Set<String> pendingConnections;
|
||||||
|
|
||||||
public PrivateChatController(
|
public PrivateChatController(
|
||||||
Pitiupi pitiupi,
|
ConnectionManager connectionManager,
|
||||||
MultiProtocolChat multiProtocolChat
|
MultiProtocolChat multiProtocolChat
|
||||||
) {
|
) {
|
||||||
this.pitiupi = pitiupi;
|
this.connectionManager = connectionManager;
|
||||||
this.multiProtocolChat = multiProtocolChat;
|
this.multiProtocolChat = multiProtocolChat;
|
||||||
|
|
||||||
this.windows = new HashMap<>();
|
this.windows = new HashMap<>();
|
||||||
this.pendingConnections = new HashSet<>();
|
this.pendingConnections = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String addressKey(TcpConnection connection) {
|
||||||
|
return connection.getAddress().getHostAddress();
|
||||||
|
}
|
||||||
|
|
||||||
public void openChat(User user) {
|
public void openChat(User user) {
|
||||||
String address = user.getAddress().getHostAddress();
|
String address = user.getAddress().getHostAddress();
|
||||||
|
|
||||||
@@ -70,7 +74,7 @@ public class PrivateChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void receiveMessage(TcpConnection connection, MpChatMessage message) {
|
public void receiveMessage(TcpConnection connection, MpChatMessage message) {
|
||||||
String address = connection.getAddress().toString();
|
String address = addressKey(connection);
|
||||||
|
|
||||||
PrivateChatWindow window = windows.get(address);
|
PrivateChatWindow window = windows.get(address);
|
||||||
|
|
||||||
@@ -85,12 +89,12 @@ public class PrivateChatController {
|
|||||||
* Deve ser chamado quando uma nova conexão privada chegar.
|
* Deve ser chamado quando uma nova conexão privada chegar.
|
||||||
*/
|
*/
|
||||||
public void connectionReceived(TcpConnection connection) {
|
public void connectionReceived(TcpConnection connection) {
|
||||||
String address = connection.getAddress().toString();
|
String address = addressKey(connection);
|
||||||
|
|
||||||
pendingConnections.remove(address);
|
pendingConnections.remove(address);
|
||||||
|
|
||||||
PrivateChatWindow window = new PrivateChatWindow(
|
PrivateChatWindow window = new PrivateChatWindow(
|
||||||
pitiupi,
|
connectionManager,
|
||||||
connection
|
connection
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -105,7 +109,7 @@ public class PrivateChatController {
|
|||||||
* Fecha a janela e remove o relacionamento.
|
* Fecha a janela e remove o relacionamento.
|
||||||
*/
|
*/
|
||||||
public void closeChat(TcpConnection connection) {
|
public void closeChat(TcpConnection connection) {
|
||||||
String address = connection.getAddress().toString();
|
String address = addressKey(connection);
|
||||||
|
|
||||||
PrivateChatWindow window = windows.remove(address);
|
PrivateChatWindow window = windows.remove(address);
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package plugin;
|
package plugin;
|
||||||
|
|
||||||
import GUI.MulticastMpChatMenu;
|
import GUI.MulticastMpChatMenu;
|
||||||
import GUI.PrivateChatWindow;
|
|
||||||
import GUI.PublicChatWindow;
|
import GUI.PublicChatWindow;
|
||||||
import GUI.UnicastMpChatMenu;
|
import GUI.UnicastMpChatMenu;
|
||||||
import control.PrivateChatController;
|
import control.PrivateChatController;
|
||||||
import net.MpChatMessage;
|
import net.MpChatMessage;
|
||||||
|
import pitiupi.control.ConnectionManager;
|
||||||
|
import pitiupi.control.GuiManager;
|
||||||
import pitiupi.models.Menus.MenuCategory;
|
import pitiupi.models.Menus.MenuCategory;
|
||||||
import pitiupi.models.Messages.Message;
|
import pitiupi.models.Messages.Message;
|
||||||
import pitiupi.net.tcp.TcpConnection;
|
|
||||||
import pitiupi.models.Plugin;
|
import pitiupi.models.Plugin;
|
||||||
import pitiupi.plugin.Pitiupi;
|
import pitiupi.net.tcp.TcpConnection;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -20,7 +20,7 @@ import java.net.InetAddress;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MultiProtocolChat extends Plugin {
|
public class MultiProtocolChat extends Plugin {
|
||||||
Pitiupi pitiupi;
|
private ConnectionManager connectionManager;
|
||||||
|
|
||||||
PrivateChatController privateChatController;
|
PrivateChatController privateChatController;
|
||||||
PublicChatWindow publicChatWindow;
|
PublicChatWindow publicChatWindow;
|
||||||
@@ -46,18 +46,18 @@ public class MultiProtocolChat extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPlugin(Pitiupi pitiupi) {
|
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
|
||||||
this.pitiupi = pitiupi;
|
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);
|
MulticastMpChatMenu multicastMpChatMenu = new MulticastMpChatMenu(publicChatWindow);
|
||||||
UnicastMpChatMenu unicastMpChatMenu = new UnicastMpChatMenu(privateChatController);
|
UnicastMpChatMenu unicastMpChatMenu = new UnicastMpChatMenu(privateChatController);
|
||||||
|
|
||||||
pitiupi.createMenu(MenuCategory.Social, multicastMpChatMenu);
|
guiManager.createMenu(MenuCategory.Social, multicastMpChatMenu);
|
||||||
pitiupi.createMenu(MenuCategory.Social, unicastMpChatMenu);
|
guiManager.createMenu(MenuCategory.Social, unicastMpChatMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -102,7 +102,11 @@ public class MultiProtocolChat extends Plugin {
|
|||||||
|
|
||||||
public void openConnection(String ip) {
|
public void openConnection(String ip) {
|
||||||
try {
|
try {
|
||||||
pitiupi.connectTcp(InetAddress.getByName(ip), getIdentifier(), getIdentifier());
|
connectionManager.connectPluginTcp(
|
||||||
|
InetAddress.getByName(ip),
|
||||||
|
getIdentifier(),
|
||||||
|
getIdentifier()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
System.out.println("Error opening connection: " + e.getMessage());
|
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;
|
package pitiupi.screenCast.GUI;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.util.List;
|
||||||
import javax.swing.JMenu;
|
|
||||||
|
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;
|
||||||
|
import pitiupi.models.Menus.MulticastMenus.MulticastMenu;
|
||||||
|
import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
*/
|
*/
|
||||||
public class ScreenCastMenu extends JMenu {
|
public class ScreenCastMenu extends MulticastSubMenu {
|
||||||
|
|
||||||
private final ScreenCastWindow screenCastWindow;
|
private final ScreenCastShowMenu showMenu;
|
||||||
private final javax.swing.JMenuItem menuItem;
|
|
||||||
|
|
||||||
public ScreenCastMenu(ScreenCastWindow screenCastWindow) {
|
public ScreenCastMenu(ScreenCastWindow screenCastWindow) {
|
||||||
this.screenCastWindow = screenCastWindow;
|
this.showMenu = new ScreenCastShowMenu(screenCastWindow);
|
||||||
this.setText("Screen Cast");
|
|
||||||
menuItem = new javax.swing.JMenuItem();
|
|
||||||
menuItem.setText("ScreenCast Window");
|
|
||||||
menuItem.addActionListener(this::chatMenuActionPerformed);
|
|
||||||
this.add(menuItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void chatMenuActionPerformed(ActionEvent ae) {
|
@Override
|
||||||
this.screenCastWindow.setVisible(true);
|
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.imageio.ImageIO;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import pitiupi.GUI.MainWindow;
|
|
||||||
import pitiupi.net.Message;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package pitiupi.screenCast.net;
|
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) {
|
public void setImageByte(byte[] imageByte) {
|
||||||
this.imageByte = imageByte;
|
this.imageByte = imageByte;
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
package pitiupi.screenCast.plugin;
|
package pitiupi.screenCast.plugin;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.util.List;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import pitiupi.GUI.MainWindow;
|
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.control.GuiManager;
|
import pitiupi.control.GuiManager;
|
||||||
import pitiupi.net.Message;
|
import pitiupi.models.Menus.MenuCategory;
|
||||||
import pitiupi.plugin.Plugin;
|
import pitiupi.models.Messages.Message;
|
||||||
|
import pitiupi.models.Plugin;
|
||||||
import pitiupi.screenCast.GUI.ScreenCastMenu;
|
import pitiupi.screenCast.GUI.ScreenCastMenu;
|
||||||
import pitiupi.screenCast.GUI.ScreenCastWindow;
|
import pitiupi.screenCast.GUI.ScreenCastWindow;
|
||||||
import pitiupi.screenCast.net.ScreenCastMessage;
|
import pitiupi.screenCast.net.ScreenCastMessage;
|
||||||
@@ -20,7 +19,7 @@ import pitiupi.screenCast.net.ScreenCastMessage;
|
|||||||
*
|
*
|
||||||
* @author flavio
|
* @author flavio
|
||||||
*/
|
*/
|
||||||
public class ScreenCastPlugin implements Plugin {
|
public class ScreenCastPlugin extends Plugin {
|
||||||
|
|
||||||
private ScreenCastWindow screenCastWindow;
|
private ScreenCastWindow screenCastWindow;
|
||||||
|
|
||||||
@@ -47,26 +46,18 @@ public class ScreenCastPlugin implements Plugin {
|
|||||||
@Override
|
@Override
|
||||||
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
|
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
|
||||||
this.screenCastWindow = new ScreenCastWindow(connectionManager, guiManager);
|
this.screenCastWindow = new ScreenCastWindow(connectionManager, guiManager);
|
||||||
guiManager.addMenu(new ScreenCastMenu(screenCastWindow));
|
guiManager.createMenu(MenuCategory.Social, new ScreenCastMenu(screenCastWindow));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receiveMessage(byte[] message) {
|
public List<Class<? extends Message>> getMessages() {
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(message);
|
return List.of(ScreenCastMessage.class);
|
||||||
ObjectInputStream ois;
|
}
|
||||||
try {
|
|
||||||
ois = new ObjectInputStream(bis);
|
@Override
|
||||||
if (ois.readObject() instanceof ScreenCastMessage msg) {
|
public void receiveMessage(Message message) {
|
||||||
this.screenCastWindow.receiveScreenCastMessage(msg);
|
if (message instanceof ScreenCastMessage msg) {
|
||||||
}
|
this.screenCastWindow.receiveScreenCastMessage(msg);
|
||||||
} catch (IOException | ClassNotFoundException ex) {
|
|
||||||
System.out.println(ex.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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,
|
Este arquivo foi substituído pela árvore em **[docs/](../../docs/README.md)**.
|
||||||
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
|
Conteúdo técnico fatiado em [docs/documentation/](../../docs/documentation/README.md).
|
||||||
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.
|
|
||||||
@@ -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