Skip to content

Instantly share code, notes, and snippets.

@trishagee
Created December 21, 2021 05:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trishagee/210c4fc96235a2d5c3034994b06ea8bd to your computer and use it in GitHub Desktop.
Save trishagee/210c4fc96235a2d5c3034994b06ea8bd to your computer and use it in GitHub Desktop.
Simple Chat Server and Client, NIO and ByteBuffers
package ch15;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleChatClient {
public static void main(String[] args) throws IOException {
SocketChannel 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 SocketChannel setUpNetworking() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 5000));
System.out.println("Networking established. Local address: " + socketChannel.getLocalAddress());
return socketChannel;
}
}
class ClientUI {
private JTextArea incoming;
public void go(SocketChannel socket) {
JFrame frame = new JFrame("Ludicrously Simple Chat Client");
JPanel mainPanel = new JPanel();
incoming = new JTextArea(15, 50);
incoming.setLineWrap(true);
incoming.setWrapStyleWord(true);
incoming.setEditable(false);
JScrollPane qScroller = new JScrollPane(incoming);
qScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
qScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JTextField outgoing = new JTextField(20);
JButton sendButton = new JButton("Send");
sendButton.addActionListener(new SendButtonListener(outgoing, socket));
mainPanel.add(qScroller);
mainPanel.add(outgoing);
mainPanel.add(sendButton);
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 SocketChannel channel;
public SendButtonListener(JTextField outgoingMessage, SocketChannel channel) {
outgoing = outgoingMessage;
this.channel = channel;
}
public void actionPerformed(ActionEvent ev) {
// pretty sure this is doing what I think it's doing
byte[] message = outgoing.getText().getBytes();
ByteBuffer buffer = ByteBuffer.wrap(message);
try {
channel.write(buffer);
} catch (Exception ex) {
ex.printStackTrace();
}
outgoing.setText("");
outgoing.requestFocus();
}
}
}
class IncomingReader implements Runnable {
private final SocketChannel channel;
private final JTextArea incomingMessageArea;
public IncomingReader(JTextArea incomingMessageArea, SocketChannel channel) {
this.incomingMessageArea = incomingMessageArea;
this.channel = channel;
}
public void run() {
processMessages();
}
private void processMessages() {
ByteBuffer buffer = ByteBuffer.allocate(80);
try {
while (channel.read(buffer) != -1) {
buffer.flip();
CharBuffer message = StandardCharsets.UTF_8.decode(buffer);
System.out.println("read " + message);
incomingMessageArea.append(message + "\n");
buffer.clear();
}
channel.close();
System.out.println("Connection closed");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
package ch15;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleChatServer {
private final ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) {
new SimpleChatServer().go();
}
public void go() {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress("localhost", 5000));
Broadcaster broadcaster = new Broadcaster();
while (serverSocketChannel.isOpen()) {
SocketChannel clientSocket = serverSocketChannel.accept();
broadcaster.addClient(clientSocket);
executorService.execute(new ClientHandler(clientSocket, broadcaster));
System.out.println("got a connection");
}
serverSocketChannel.close();
executorService.shutdown();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
class ClientHandler implements Runnable {
private final SocketChannel clientSocket;
private final Broadcaster broadcaster;
public ClientHandler(SocketChannel clientSocket, Broadcaster broadcaster) {
this.clientSocket = clientSocket;
this.broadcaster = broadcaster;
}
public void run() {
ByteBuffer buffer = ByteBuffer.allocate(80);
try {
while (clientSocket.read(buffer) != -1) {
buffer.flip();
broadcaster.tellEveryone(buffer);
buffer.clear();
}
disconnectClient();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void disconnectClient() throws IOException {
clientSocket.close();
System.out.println("Closed: " + clientSocket);
broadcaster.remove(clientSocket);
}
}
class Broadcaster {
private final ArrayList<SocketChannel> clientOutputStreams = new ArrayList<>();
void addClient(SocketChannel clientSocket) {
clientOutputStreams.add(clientSocket);
}
public void tellEveryone(ByteBuffer buffer) {
for (SocketChannel channel : clientOutputStreams) {
try {
while (buffer.hasRemaining()) {
channel.write(buffer);
}
buffer.rewind();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public void remove(SocketChannel clientSocket) {
clientOutputStreams.remove(clientSocket);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment