Search code examples
javaniosocketchannel

Distinguish between messages sent from CLIENT and other


I am using SocketChannel and Selector to write a server. The servers job is to allow clients to connect, read messages from clients and write messages to clients.

I am having some difficulty distinguishing between messages sent from the client, and other data that's firing off the read instruction.

ex. When a client connects, I noticed the read instruction is executed, without the client having sent any data. This is important, because after the server has read a message from the client, it must add that message to a message queue. These message will be removed and processed from the queue by an external application. The problem is, that every time the read fires, these yet UFM (unidentified messages) are breaking the external application when trying to decode them.

I am sorry if this question has already been answered I couldn't find a complete answer.

Here is my accept method that, if I am not mistaken, tells the selector to notify us when data is available to read.

private void accept(SelectionKey key) throws IOException {
    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

    // Accept the connection and make it non-blocking
    SocketChannel socketChannel = serverSocketChannel.accept();
    socketChannel.configureBlocking(false);

    socketChannel.register(this.selector, SelectionKey.OP_READ);
}

Here is the selector method which waits for an event.

while (should_run) {
    try {
        //Load all pending operations to key
        while (!operations.isEmpty()) {
            SelectionKey key = client.keyFor(getSelector());
            if (!key.isValid()) {
                operations.remove();
                throw new Exception("Key not valid");
            }
            key.interestOps(operations.remove());
        }

        selector.select();

        Iterator selectedKeys = selector.selectedKeys().iterator();
        while (selectedKeys.hasNext()) {
            SelectionKey key = (SelectionKey) selectedKeys.next();
            //Check if key is valid
            if (!key.isValid()) {
                throw new Exception("Key not valid");
            }

            if (key.isAcceptable()) {
                accept(key);
            } else if (key.isReadable()) {
                read(key);
            } else if (key.isWritable()) {
                write(key);
            }

            selectedKeys.remove();
        }
    } catch (Exception e) {
        System.err.println("Something went wrong: " + e.getMessage());
        e.printStackTrace();
    }
}

Here is the read function, called if key.isReadable()

private void read(SelectionKey key) {     
    System.out.println("Read fired");

    //Clear the buffer for new Data
    this.readBuffer.clear();

    int count;
    try {
        count = this.client.read(this.readBuffer);
        if (count == -1) {
            throw new IOException("Socket closed");
        }
    } catch (IOException e) {
        key.cancel();
        return;
    }

    //PROBLEM OCCURRING HERE
    this.worker.give(this.readBuffer.array(), count);
}

The read should read messages, and hand the message to a worker thread, where the message is decoded and other nice things happen to it.

The read method is called after each connection of a new client. The count is usually small, between 4 and 10 and when I decode using new String(data[], "UTF-8"), it turns out to be Japanese or something..

I tested this by simply printing the count every time the read is called.

This problem can be solved by simply checking the size of each incoming message and ignoring those which are to small. But this seems like it could backfire if fragmentation occurs.

EDIT: Client example code:

Socket socket = new Socket("localhost", 3000);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
while (true) {
    String input = JOptionPane.showInputDialog("Send something to the server");
    if (input == null)
            break;
    oos.writeObject(input);
}

NOTE: I don't have the source code for the peer, but this VERY SIMPLE example yields the exact same results. Before the MessageDialog even prompts for a message to send to the server, a message is received by the server, which is either Japanese or a series of question marks (?? ???).


Solution

  • and other data that's firing off the read instruction.

    There is no 'other data'. Only data from the client.

    'new ObjectOutputStream()' writes a stream header that starts with 0xAC. That's what you're reading.

    As the peer is using object streams you should be using blocking sockets, threads, and object input streams. Not NIO.

    The 'this.client' member should be removed as per my comments above. It assumes you only have one client.

    Closing the channel cancels the key. You rarely if ever need key.cancel(). I never use it.