package dasherJava.core.network; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class SingleSocketConnection { private final String name; //only needed for logging private final Object monitor = new Object(); //for synchronization private final Object monitor2 = new Object(); //for synchronization private final Thread serverSocketThread; private volatile ServerSocket serverSocket = null; private volatile Socket currentSocket = null; private volatile BufferedReader currentReader = null; private volatile BufferedWriter currentWriter = null; private volatile boolean shouldExit = false; public SingleSocketConnection(String name, int port) { this.name=name; serverSocketThread=new Thread(() -> { try { serverSocket=new ServerSocket(port, 1); while (!shouldExit) { try { System.out.println(name+": Waiting for connection on port "+port); currentSocket=serverSocket.accept(); currentReader=new BufferedReader(new InputStreamReader(currentSocket.getInputStream())); currentWriter=new BufferedWriter(new OutputStreamWriter(currentSocket.getOutputStream())); System.out.println(name+": Made new connection"); synchronized (monitor2) { monitor2.notifyAll(); } while (currentSocket!=null) { //loop until current connection is closed synchronized (monitor) { try { monitor.wait(); } catch (InterruptedException ex) { System.out.println(name+": ServerSocketThread: Wait interrupted"); if (shouldExit) break; } } } } catch (IOException ex) { if (!shouldExit) { System.out.println(name+": ServerSocketThread: IOException: "+ex.getMessage()); closeReaderAndWriterAndSocket(); } } } System.out.println(name+": Exiting"); } catch (IOException|SecurityException|IllegalArgumentException ex) { System.out.println(name+": Couldn't open ServerSocket on port "+port); } }, name+"ServerSocketThread"); serverSocketThread.start(); } public BufferedReader getCurrentReader() { //blocking, waits until there is a connection, but may still //return null on rare occasions and should thus be used in a loop while (currentReader==null) { //loop until a connection is made synchronized (monitor2) { try { monitor2.wait(); } catch (InterruptedException ex) { System.out.println(name+": Waiting for reader interrupted"); break; } } } return currentReader; } public BufferedWriter getCurrentWriter() { //non-blocking, returns null if there is no connection return currentWriter; } public void closeCurrentConnection() { closeReaderAndWriterAndSocket(); synchronized (monitor) { monitor.notifyAll(); } } public void terminate() { shouldExit=true; if (serverSocket!=null) { try { serverSocket.close(); } catch (IOException ex) { System.out.println(name+": Couldn't close ServerSocket: "+ex.getMessage()); } } serverSocketThread.interrupt(); closeReaderAndWriterAndSocket(); } private void closeReaderAndWriterAndSocket() { //Note: It's probably not necessary to close the reader, writer and socket separately since closing //the socket should close all streams as well. However, since closing an already closed stream is //defined to do nothing, we can explicitly close the reader and writer anyway just to be sure. try { if (currentWriter!=null) currentWriter.close(); } catch (IOException ex) { System.out.println(name+": Couldn't close writer: "+ex.getMessage()); } currentWriter=null; try { if (currentSocket!=null) currentSocket.close(); } catch (IOException ex) { System.out.println(name+": Couldn't close socket: "+ex.getMessage()); } currentSocket=null; //Must close the reader after closing the socket to avoid a deadlock, since it is not possible //to close the BufferedReader itself while another thread is still blocked in a read operation. try { if (currentReader!=null) currentReader.close(); } catch (IOException ex) { System.out.println(name+": Couldn't close reader: "+ex.getMessage()); } currentReader=null; } }