HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Chat server implemented using Java NIO

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
niochatimplementedjavausingserver

Problem

I am learning Java's NIO and as an exercise, I have implemented a simple chat server. I would like to ask if I am using NIO's features in the correct way.

```
public class ChatServer implements Runnable {
private static final int BUFFER_SIZE = 256;
private static final int DEFAULT_PORT = 10523;
private static final String WELCOME_MESSAGE = "Welcome to NioChat!\n";

private final int mPort;
private ServerSocketChannel mServerChannel;
private Selector mSelector;
private ByteBuffer mBuffer = ByteBuffer.allocate(BUFFER_SIZE);
private final ByteBuffer mWelcomeBuffer = ByteBuffer.wrap(WELCOME_MESSAGE
.getBytes());

public ChatServer(int port) throws IOException {
mPort = port;
mServerChannel = ServerSocketChannel.open();
mServerChannel.socket().bind(new InetSocketAddress(port));
mServerChannel.configureBlocking(false);
mSelector = Selector.open();
mServerChannel.register(mSelector, SelectionKey.OP_ACCEPT);
}

public static void main(String[] args) throws IOException {
ChatServer server = new ChatServer(DEFAULT_PORT);
new Thread(server).start();
}

@Override
public void run() {
try {
System.out.println("Server started on port " + mPort);

Iterator iter;
SelectionKey key;
while (mServerChannel.isOpen()) {
mSelector.select();
iter = mSelector.selectedKeys().iterator();
while (iter.hasNext()) {
key = iter.next();
iter.remove();

if (key.isAcceptable())
handleAccept(key);
if (key.isReadable())
handleRead(key);
}
}
} catch (IOException e) {
System.out.println("IOException, server on port " + mPort
+ " terminating. Stack trace:");
e.printStac

Solution

Bad support for fragmented tcp packets

StringBuilder sb = new StringBuilder();

mBuffer.clear();
int read = 0;
while ((read = ch.read(mBuffer)) > 0) {
    mBuffer.flip();
    byte[] bytes = new byte[mBuffer.limit()];
    mBuffer.get(bytes);
    sb.append(new String(bytes));
    mBuffer.clear();
}


The above implementation only works reliable with messages smaller than 567 (IPV4) or 1200 (ipv6) bytes, because larger messages can come in fragmented in multiple chunks with large intervals in case of packet loss. This will result is half send chat messages that are broadcasted

Using default charset

You are not specifying any charset when reading the messages, this will result in platform and even java dependent behaviour when reading special characters.

Manually using stringbuilder

String address = (new StringBuilder(sc.socket().getInetAddress()
                .toString())).append(":").append(sc.socket().getPort())
                .toString();


You don't need to use stringbuilder if you already have all the used arguments on 1 line, the compiler will automatically add a stringbuilder for you.

String address = sc.socket().getInetAddress() + ":" + sc.socket().getPort();

Code Snippets

StringBuilder sb = new StringBuilder();

mBuffer.clear();
int read = 0;
while ((read = ch.read(mBuffer)) > 0) {
    mBuffer.flip();
    byte[] bytes = new byte[mBuffer.limit()];
    mBuffer.get(bytes);
    sb.append(new String(bytes));
    mBuffer.clear();
}
String address = (new StringBuilder(sc.socket().getInetAddress()
                .toString())).append(":").append(sc.socket().getPort())
                .toString();
String address = sc.socket().getInetAddress() + ":" + sc.socket().getPort();

Context

StackExchange Code Review Q#125538, answer score: 2

Revisions (0)

No revisions yet.