Files
Projet-UDP/ChatApp/app/src/main/java/network/Socket.java
2025-03-12 13:18:27 +01:00

159 lines
4.3 KiB
Java

package network;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import network.protocol.Packet;
import utilities.Signal;
public class Socket {
private final DatagramSocket socket;
private final List<PacketHandler> handlers;
private final Thread readThread;
private final PacketPool packetPool;
public final Signal onClose = new Signal();
/**
* Construct a UDP Socket to connect to a server
* @throws SocketException
*/
public Socket() throws SocketException {
this.socket = new DatagramSocket();
this.handlers = new ArrayList<>();
this.packetPool = new PacketPool(this);
this.readThread = new Thread(this::readLoop);
this.readThread.start();
}
/**
* Construct a UDP Socket to listen to connexion at the specified port
* @param listeningPort the port to listen to
* @throws SocketException
*/
public Socket(int listeningPort) throws SocketException {
this.socket = new DatagramSocket(listeningPort);
this.handlers = new ArrayList<>();
this.packetPool = new PacketPool(this);
this.readThread = new Thread(this::readLoop);
this.readThread.start();
}
public void addHandler(PacketHandler handler) {
this.handlers.add(handler);
}
/**
* @return this.socket.getLocalPort()
*/
public int getLocalPort() {
return this.socket.getLocalPort();
}
// needs to be accessible by PacketPool
/**
* Send a packet to the specified address
* @param packet the packet to send
* @param address the address to send to
* @throws IOException
*/
void sendPacket(ReliablePacket packet, InetSocketAddress address) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(stream);
oos.writeObject(packet);
oos.flush();
byte[] data = stream.toByteArray();
DatagramPacket dataPacket = new DatagramPacket(data, data.length, address.getAddress(),
address.getPort());
this.socket.send(dataPacket);
}
/**
* Try to recieve packets and send them to the PacketPool.
* This method blocks until something has been read
* @throws IOException
* @throws ClassNotFoundException
*/
private void recievePacketReliable()
throws IOException, ClassNotFoundException {
byte[] buffer = new byte[65535];
DatagramPacket dataPacket = new DatagramPacket(buffer, buffer.length);
socket.receive(dataPacket);
InetSocketAddress address = new InetSocketAddress(dataPacket.getAddress(), dataPacket.getPort());
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dataPacket.getData()));
ReliablePacket packet = (ReliablePacket) ois.readObject();
this.packetPool.onPacketReceived(packet, address);
}
/**
* Send a packet to the specified address
* @param packet the packet to send
* @param address the address to send to
* @throws IOException
*/
public void sendPacket(Packet packet, InetSocketAddress address) throws IOException {
this.packetPool.sendPacket(packet, address);
}
/**
* Recieve packet in a reliable way (packets are in the right order).
* This method is blocking
* @throws IOException
* @throws ClassNotFoundException
*/
private void recievePacket() throws IOException, ClassNotFoundException {
this.recievePacketReliable();
var entry = this.packetPool.getNextPacket();
while (entry != null) {
this.handlePacket(entry.getKey(), entry.getValue());
entry = this.packetPool.getNextPacket();
}
}
/**
* Close the socket
*/
public void close() {
this.socket.close();
this.packetPool.close();
this.readThread.interrupt();
this.onClose.emit();
}
/**
* Dispatch the packet
* @param packet the packet to dispatch
* @param address the incoming address
*/
void handlePacket(Packet packet, InetSocketAddress address) {
for (PacketHandler handler : this.handlers) {
handler.handlePacket(packet, address);
}
}
/**
* Loop to read all packets
*/
private void readLoop() {
try {
while (!Thread.interrupted()) {
recievePacket();
}
} catch (IOException | ClassNotFoundException e) {
// e.printStackTrace();
}
}
}