Commit c5677579 authored by Trisha Gee's avatar Trisha Gee
Browse files

[Ch15] Using Channels.newWriter/Reader instead of creating them from the socket

parent 4c0a7978
Loading
Loading
Loading
Loading
+27 −56
Changes for src/main/java/ch15/SimpleChatClient.java: 27 added lines, 56 removed lines.
Original line number Diff line number Diff line
@@ -9,34 +9,18 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleChatClient {
  private JTextArea incoming;
  private JTextField outgoing;
  private BufferedReader reader;
  private PrintWriter writer;

  public static void main(String[] args) throws IOException {
    Socket socket = setUpNetworking();

    ClientUI clientUI = new ClientUI();
    clientUI.go(socket);

    IncomingReader incomingReader = new IncomingReader(clientUI.getIncoming(), socket);
    ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
    threadExecutor.execute(incomingReader);
    threadExecutor.shutdown();
  }

  private static Socket setUpNetworking() throws IOException {
    Socket socket = new Socket("127.0.0.1", 5000);
    System.out.println("networking established");
    return socket;
  public static void main(String[] args) {
    new SimpleChatClient().go();
  }
}

class ClientUI {
  private JTextArea incoming;

  public void go(Socket socket) {
  public void go() {
    JFrame frame = new JFrame("Ludicrously Simple Chat Client");
    JPanel mainPanel = new JPanel();
    incoming = new JTextArea(15, 50);
@@ -46,34 +30,33 @@ class ClientUI {
    JScrollPane qScroller = new JScrollPane(incoming);
    qScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    qScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    JTextField outgoing = new JTextField(20);
    outgoing = new JTextField(20);
    JButton sendButton = new JButton("Send");
    sendButton.addActionListener(new SendButtonListener(outgoing, socket));
    sendButton.addActionListener(new SendButtonListener());
    mainPanel.add(qScroller);
    mainPanel.add(outgoing);
    mainPanel.add(sendButton);
    setUpNetworking();
    Thread readerThread = new Thread(new IncomingReader());
    readerThread.start();
    frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
    frame.setSize(800, 500);
    frame.setVisible(true);
  }

  public JTextArea getIncoming() {
    return incoming;
  }

  public static class SendButtonListener implements ActionListener {
    private final JTextField outgoing;
    private final PrintWriter writer;
  } // close go

    public SendButtonListener(JTextField outgoingMessage, Socket socket) {
  private void setUpNetworking() {
    try {
      Socket socket = new Socket("127.0.0.1", 5000);
      InputStreamReader streamReader = new InputStreamReader(socket.getInputStream());
      reader = new BufferedReader(streamReader);
      writer = new PrintWriter(socket.getOutputStream());
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
      outgoing = outgoingMessage;
      System.out.println("networking established");
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  } // close setUpNetworking

  public class SendButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent ev) {
      try {
        writer.println(outgoing.getText());
@@ -84,31 +67,19 @@ class ClientUI {
      outgoing.setText("");
      outgoing.requestFocus();
    }
  }
}

class IncomingReader implements Runnable {
  private final BufferedReader reader;
  private final JTextArea incomingMessageArea;

  public IncomingReader(JTextArea incomingMessageArea, Socket socket) throws IOException {
    this.incomingMessageArea = incomingMessageArea;
    reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  }
  } // close inner class

  public class IncomingReader implements Runnable {
    public void run() {
    processMessages();
  }

  private void processMessages() {
      String message;
      try {
        while ((message = reader.readLine()) != null) {
          System.out.println("read " + message);
        incomingMessageArea.append(message + "\n");
          incoming.append(message + "\n");
        } // close while
      } catch (Exception ex) {
        ex.printStackTrace();
      }
  }
    } // close run
  } // close inner class
} // close outer class
 No newline at end of file
+42 −48
Changes for src/main/java/ch15/SimpleChatServer.java: 42 added lines, 48 removed lines.
Original line number Diff line number Diff line
package ch15;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.nio.channels.Channels;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleChatServer {
  private final ExecutorService executorService = Executors.newCachedThreadPool();
  ArrayList<Writer> clientOutputStreams;

  public static void main(String[] args) {
    new SimpleChatServer().go();
  }

  public void go() {
    clientOutputStreams = new ArrayList<>();
    try {
      ServerSocket serverSock = new ServerSocket(5000);
      Broadcaster broadcaster = new Broadcaster();
      while (!serverSock.isClosed()) {
        Socket clientSocket = serverSock.accept();
        broadcaster.addClient(clientSocket);
        executorService.execute(new ClientHandler(clientSocket, broadcaster));
      ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
      serverSocketChannel.bind(new InetSocketAddress("localhost", 5000));

      while (true) {
        SocketChannel clientSocket = serverSocketChannel.accept();
        Writer writer = Channels.newWriter(clientSocket, StandardCharsets.UTF_8);
        clientOutputStreams.add(writer);
        Thread t = new Thread(new ClientHandler(clientSocket));
        t.start();
        System.out.println("got a connection");
      }
      executorService.shutdown();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  } // close go

  public void tellEveryone(String message) {
    for (Writer writer : clientOutputStreams) {
      try {
        writer.append(message).write("\n");
        writer.flush();
      } catch (Exception ex) {
        ex.printStackTrace();
      }
}
    } // end while
  } // close tellEveryone

class ClientHandler implements Runnable {
  private final BufferedReader reader;
  private final Broadcaster broadcaster;
  public class ClientHandler implements Runnable {
    BufferedReader reader;
    SocketChannel socket;

  public ClientHandler(Socket clientSocket, Broadcaster broadcaster) throws IOException {
    this.broadcaster = broadcaster;
    InputStreamReader isReader = new InputStreamReader(clientSocket.getInputStream());
    reader = new BufferedReader(isReader);
    public ClientHandler(SocketChannel clientSocket) {
      try {
        socket = clientSocket;
        reader = new BufferedReader(Channels.newReader(socket, StandardCharsets.UTF_8));
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    } // close constructor

    public void run() {
@@ -50,31 +64,11 @@ class ClientHandler implements Runnable {
      try {
        while ((message = reader.readLine()) != null) {
          System.out.println("read " + message);
        broadcaster.tellEveryone(message);
          tellEveryone(message);
        } // close while
      } catch (Exception ex) {
        ex.printStackTrace();
      }
  }
}

class Broadcaster {
  private final ArrayList<PrintWriter> clientOutputStreams = new ArrayList<>();

  void addClient(Socket clientSocket) throws IOException {
    clientOutputStreams.add(new PrintWriter(clientSocket.getOutputStream()));
  }

  public void tellEveryone(String message) {
    Iterator<PrintWriter> it = clientOutputStreams.iterator();
    while (it.hasNext()) {
      try {
        PrintWriter writer = it.next();
        writer.println(message);
        writer.flush();
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }
}
 No newline at end of file
    } // close run
  } // close inner class
} // close class
 No newline at end of file
+73 −0
Changes for src/test/java/ch15/SimpleChatServerTest.java: 73 added lines, 0 removed lines.
Original line number Diff line number Diff line
package ch15;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.junit.jupiter.api.Assertions.assertTrue;

// Needs a SimpleChatServer running (is not a real unit test class, more like an integration test)
class SimpleChatServerTest {
  @Test
  void testSend() throws IOException {
    Socket socket = connect();
    assertTrue(send(socket, "Hi"));
    socket.close();
  }

  @Test
  // this will work if run when there are no other clients connected
  // this will likely hang if other clients are already connected (e.g. running testReceive)
  void testSendAndReceive() throws IOException, InterruptedException {
    Socket socket = connect();
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(() -> Assertions.assertEquals("Hi", receive(socket)));
    assertTrue(send(socket, "Hi"));
    Thread.sleep(10_000);
    socket.close();
  }

  @Test
  // NOT a real unit test - run this to have a client sat waiting for a response. You can get this to
  // pass by sending "Hi" to the server with another client, e.g. by calling testSend.
  void testReceive() throws IOException {
    Socket socket = connect();
    Assertions.assertEquals("Hi", receive(socket));
    socket.close();
  }

  public boolean send(Socket socket, String payload) throws IOException {
    PrintWriter writer = new PrintWriter(socket.getOutputStream());

    writer.println(payload);
    writer.flush();
    return true;
  }

  public String receive(Socket socket) {
    try (InputStreamReader streamReader = new InputStreamReader(socket.getInputStream());
         BufferedReader reader = new BufferedReader(streamReader)) {
      String message = reader.readLine();
      System.out.println("read " + message);
      return message;

    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  private Socket connect() throws IOException {
    Socket socket = new Socket("127.0.0.1", 5000);
    System.out.println(socket.getLocalPort() + " connected");
    return socket;
  }

}
 No newline at end of file