Created
December 21, 2021 05:16
-
-
Save trishagee/210c4fc96235a2d5c3034994b06ea8bd to your computer and use it in GitHub Desktop.
Simple Chat Server and Client, NIO and ByteBuffers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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