127 lines
4.1 KiB
Java
127 lines
4.1 KiB
Java
package nl.andrewl.aos2_client;
|
|
|
|
import nl.andrewl.aos_core.Net;
|
|
import nl.andrewl.aos_core.net.*;
|
|
import nl.andrewl.aos_core.net.udp.DatagramInit;
|
|
import nl.andrewl.record_net.Message;
|
|
import nl.andrewl.record_net.util.ExtendedDataInputStream;
|
|
import nl.andrewl.record_net.util.ExtendedDataOutputStream;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.io.IOException;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.InetAddress;
|
|
import java.net.Socket;
|
|
|
|
/**
|
|
* Class which handles the client's communication with the server. This
|
|
* involves establishing a TCP and UDP connection, and providing generic
|
|
* methods for sending messages and processing those we receive.
|
|
*/
|
|
public class CommunicationHandler {
|
|
private static final Logger log = LoggerFactory.getLogger(CommunicationHandler.class);
|
|
|
|
private final Client client;
|
|
private Socket socket;
|
|
private DatagramSocket datagramSocket;
|
|
private ExtendedDataOutputStream out;
|
|
private int clientId;
|
|
|
|
public CommunicationHandler(Client client) {
|
|
this.client = client;
|
|
}
|
|
|
|
public int establishConnection() throws IOException {
|
|
if (socket != null && !socket.isClosed()) {
|
|
socket.close();
|
|
}
|
|
InetAddress address = InetAddress.getByName(client.getConfig().serverHost);
|
|
int port = client.getConfig().serverPort;
|
|
String username = client.getConfig().username;
|
|
log.info("Connecting to server at {}, port {}, with username \"{}\"...", address, port, username);
|
|
|
|
socket = new Socket(address, port);
|
|
socket.setSoTimeout(1000);
|
|
ExtendedDataInputStream in = Net.getInputStream(socket.getInputStream());
|
|
out = Net.getOutputStream(socket.getOutputStream());
|
|
Net.write(new ConnectRequestMessage(username), out);
|
|
Message response = Net.read(in);
|
|
socket.setSoTimeout(0);
|
|
if (response instanceof ConnectRejectMessage rejectMessage) {
|
|
throw new IOException("Attempt to connect rejected: " + rejectMessage.reason());
|
|
}
|
|
if (response instanceof ConnectAcceptMessage acceptMessage) {
|
|
this.clientId = acceptMessage.clientId();
|
|
establishDatagramConnection();
|
|
log.info("Connection to server established. My client id is {}.", clientId);
|
|
new Thread(new TcpReceiver(in, client::onMessageReceived)).start();
|
|
new Thread(new UdpReceiver(datagramSocket, (msg, packet) -> client.onMessageReceived(msg))).start();
|
|
return acceptMessage.clientId();
|
|
} else {
|
|
throw new IOException("Server returned an unexpected message: " + response);
|
|
}
|
|
}
|
|
|
|
public void shutdown() {
|
|
try {
|
|
socket.close();
|
|
datagramSocket.close();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void sendMessage(Message msg) {
|
|
try {
|
|
Net.write(msg, out);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void sendDatagramPacket(Message msg) {
|
|
try {
|
|
byte[] data = Net.write(msg);
|
|
DatagramPacket packet = new DatagramPacket(data, data.length, socket.getRemoteSocketAddress());
|
|
datagramSocket.send(packet);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Establishes a UDP "connection" to the server, after we've already
|
|
* obtained our {@link CommunicationHandler#clientId} from our TCP
|
|
* connection. This continuously sends {@link DatagramInit} packets until
|
|
* the server responds with an echo of that packet.
|
|
* @throws IOException If an error occurs.
|
|
*/
|
|
private void establishDatagramConnection() throws IOException {
|
|
datagramSocket = new DatagramSocket();
|
|
boolean connectionEstablished = false;
|
|
int attempts = 0;
|
|
while (!connectionEstablished && attempts < 100) {
|
|
sendDatagramPacket(new DatagramInit(clientId));
|
|
byte[] buffer = new byte[UdpReceiver.MAX_PACKET_SIZE];
|
|
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
|
datagramSocket.receive(packet);
|
|
Message msg = Net.read(buffer);
|
|
if (msg instanceof DatagramInit echo && echo.clientId() == clientId) {
|
|
connectionEstablished = true;
|
|
} else {
|
|
attempts++;
|
|
}
|
|
}
|
|
if (!connectionEstablished) {
|
|
throw new IOException("Could not establish a datagram connection to the server after " + attempts + " attempts.");
|
|
}
|
|
log.debug("Established datagram communication with the server.");
|
|
}
|
|
|
|
public int getClientId() {
|
|
return clientId;
|
|
}
|
|
}
|