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>
5.6 KiB
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 é:
- Procurar uma conexão existente no mapa de conexões.
- Verificar se a conexão está fechada.
- Caso não exista uma conexão válida, criar um novo Socket TCP para o endereço do peer.
- Encapsular o socket em uma
TcpConnection. - Registrar a conexão no mapa.
- Iniciar uma tarefa de recepção para essa conexão.
- 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:
- o
Socketutilizado na conexão; - um
DataInputStreampara recepção; - um
DataOutputStreampara 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():
- obtém o tamanho do vetor de bytes;
- escreve o tamanho no
DataOutputStream; - escreve os dados da mensagem;
- 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():
- lê o tamanho da mensagem;
- verifica se o tamanho é válido;
- lê a quantidade correspondente de bytes;
- 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