Search code examples
javabytebuffersocketchannel

How to improve the performance of the following code using socketchannel? And how can I receive UTF8 string?


here is the important code:

public void setSelector() {
    try {

        this.writebuf.clear();
        this.selector = Selector.open();
        int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
        SelectionKey selectionKey = this.socketChannel.register(selector, interestSet);


        // Checking if the buffer is in write mode
        if (!(this.writebuf.limit() == this.writebuf.capacity())) {
            writebuf.flip();
        }

        int bytesRead = 0;

        while (selector.select() > -1) {

            // Wait for an event one of the registered channels

            // Iterate over the set of keys for which events are available
            Iterator selectedKeys = selector.selectedKeys().iterator();
            while (selectedKeys.hasNext()) {
                SelectionKey key = (SelectionKey) selectedKeys.next();
                selectedKeys.remove();
                if (!key.isValid()) {
                    continue;
                }
                if (key.isReadable()) {

                    bytesRead = this.socketChannel.read(this.writebuf);

                    if (bytesRead > 0 && writebuf.hasRemaining()) {
                        this.writebuf.flip();
                        this.parseSubtitleMessage(new String(writebuf.array(), charset));
                        this.writebuf.clear();
                        this.writebuf.flip();
                    }
                }
                if (key.isWritable()) {
                // Not yet implemented
                }
            }
        }

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

Socketchannel is set to nonblocking mode. The first problem is that the while loop creates huge workload. Basically I need something that would pause the code until the READ or WRITE event occurs. Another problematic part is this code:

this.parseSubtitleMessage(new String(writebuf.array(), charset));

I know that when I call the array() method I'll get the whole buffer including some forgotten bytes from the previous message, even though I call the clear method. I found a solution that uses a while cycle to iterate over single bytes until the buffer.hasRemaining() is set to false. But I don't know how to use the specified charset in this case.

Edit: I solved my "infinite loop" problem, here is the fixed code:

public void runSelector() {
    try {

        this.writebuf.clear();

        // Checking if the buffer is in write mode
        if (!(this.writebuf.limit() == this.writebuf.capacity())) {
            writebuf.flip();
        }

        this.selector = Selector.open();

        int interestSet_RW = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

        int bytesRead = 0;

        SelectionKey selectionKey = this.socketChannel.register(selector, SelectionKey.OP_READ);

        while(true) {

            int readyChannels = selector.select();

            if(readyChannels == 0) continue;


            Set<SelectionKey> selectedKeys = selector.selectedKeys();

            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

            while(keyIterator.hasNext()) {

                SelectionKey key = keyIterator.next();

                if (!key.isValid()) {
                    continue;
                }

                // Check if there's something to write in the queue and change interestops apropriately.
                if (monitorObject.tosendIsEmpty()) {
                    selectionKey.interestOps(SelectionKey.OP_READ);
                } else {
                    selectionKey.interestOps(interestSet_RW);
                }

                if (key.isWritable()) {
                }

                if (key.isReadable()) {
                    bytesRead = this.socketChannel.read(this.writebuf);

                    if (bytesRead > 0 && writebuf.hasRemaining()) {
                        this.parseSubtitleMessage(new String(this.writebuf.array(), charset));
                        this.writebuf.clear();
                    }
                }
                keyIterator.remove();
            }
        }




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

I unintentionally created sligthly duplicate thread. My google skills were probably really bad that day. However the second problem still remains and I'll definitely appreciate your help. Thanks

Edit 2: So I fixed my second problem too. Here is the code:

                if (key.isReadable()) {

                    bytesRead = this.socketChannel.read(this.writebuf);

                    bytes = new byte[bytesRead];

                    int x = 0;

                    this.writebuf.flip();

                    while(this.writebuf.hasRemaining()) {
                        bytes[x] = this.writebuf.get();
                        x++;
                    }

                    System.out.println(new String(bytes, charset));

                    this.writebuf.flip();
                    this.writebuf.clear();
                }

Solution

  • Here is the final solution:

    public void runSelector() {
        try {
    
            this.writebuf.clear();
    
            // Checking if the buffer is in write mode
            if (!(this.writebuf.limit() == this.writebuf.capacity())) {
                writebuf.flip();
            }
    
            this.selector = Selector.open();
    
            int interestSet_RW = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
    
            int bytesRead = 0;
    
            byte[] bytes = null;
    
            SelectionKey selectionKey = this.socketChannel.register(selector, SelectionKey.OP_READ);
    
            while(true) {
    
                int readyChannels = selector.select();
    
                if(readyChannels == 0) continue;
    
    
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
    
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
    
                while(keyIterator.hasNext()) {
    
                    SelectionKey key = keyIterator.next();
    
                    if (!key.isValid()) {
                        continue;
                    }
    
                    // Check if there's something to write in the queue and change interestops apropriately.
                    if (monitorObject.tosendIsEmpty()) {
                        selectionKey.interestOps(SelectionKey.OP_READ);
                    } else {
                        selectionKey.interestOps(interestSet_RW);
                    }
    
                    if (key.isWritable()) {
                    }
    
                    if (key.isReadable()) {
    
                        bytesRead = this.socketChannel.read(this.writebuf);
    
                        bytes = new byte[bytesRead];
    
                        int x = 0;
    
                        this.writebuf.flip();
    
                        while(this.writebuf.hasRemaining()) {
                            bytes[x] = this.writebuf.get();
                            x++;
                        }
    
                        System.out.println(new String(bytes, charset));
    
                        this.writebuf.flip();
                        this.writebuf.clear();
                    }
                    keyIterator.remove();
                }
            }
    
    
    
    
        } catch (IOException e) {
            e.printStackTrace();
        }
    }