159 lines
4.3 KiB
Java
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();
|
|
}
|
|
}
|
|
}
|