package com.faforever.client.net; import org.apache.commons.compress.utils.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import java.util.function.Consumer; public final class SocketUtil { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private SocketUtil() { throw new AssertionError("Not instantiatable"); } /** * Reads the specified socket and processes them using the specified consumer. * * @param executor the {@link Executor} to run the background thread in */ public static void readSocket(ThreadPoolExecutor executor, final DatagramSocket socket, Consumer<DatagramPacket> consumer) { String localSocketAddress = socket.getLocalSocketAddress().toString(); logger.debug("Reading socket {}", localSocketAddress); executor.execute(() -> { byte[] buffer = new byte[1500]; DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length); try { while (!socket.isClosed()) { socket.receive(datagramPacket); // Passing the original datagramPacket can cause locks byte[] payload = new byte[datagramPacket.getLength()]; System.arraycopy(datagramPacket.getData(), 0, payload, 0, datagramPacket.getLength()); DatagramPacket packetCopy = new DatagramPacket(payload, payload.length); packetCopy.setSocketAddress(datagramPacket.getSocketAddress()); consumer.accept(packetCopy); } } catch (SocketException e) { if (!e.getMessage().equals("socket closed")) { logger.warn("Socket has been closed: ({})", e.getMessage()); } } catch (IOException e) { logger.warn("Exception while forwarding socket: " + localSocketAddress, e); throw new RuntimeException(e); } finally { IOUtils.closeQuietly(socket); } logger.debug("Socket {} closed gracefully", localSocketAddress); }); } }