Question

I have a socket program written in Java that uses NIO. I am trying to send a list of objects. I am doing that by sending the objects in the list one by one. These are the steps that I am following:

On the server

  • Send a 10 character string that signifies the size of the list
  • For each object
  • Send a 6 character string that signifies the size of each object
  • Send the object

And the corresponding client code.

The object I am sending in a LinkedList implements Serializable. Here's what I am able to do:

  • Send single objects
  • Send 16 objects in a list

Thw problem occurs when I try to send more than 16 objects. Exactly that number!! Sometimes(very very rarely) I am able to get all the objects that I send from the server(more than 17).

The server startup code:

private static final String CLIENT_CHANNEL_TYPE = "clientChannel";
private static final String SERVER_CHANNEL_TYPE = "serverChannel";
private static final String CHANNEL_TYPE = "dataChannel";
private static final String LOCAL_HOST = "localhost";
private static final int PORT_NUMBER = 4445;

public static void main(String[] args) {
    try {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(LOCAL_HOST, PORT_NUMBER));
        serverSocketChannel.configureBlocking(false);
        Selector selector = Selector.open();
        SelectionKey socketServerSelectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        Map<String, String> properties = new HashMap<String, String>();
        properties.put(CHANNEL_TYPE, SERVER_CHANNEL_TYPE);
        socketServerSelectionKey.attach(properties);
        for (;;) {
            if (selector.select() == 0) {
                continue;
            }
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                if (((Map<String, String>) key.attachment()).get(CHANNEL_TYPE).equals(SERVER_CHANNEL_TYPE)) {
                    SocketChannel clientSocketChannel = serverSocketChannel.accept();
                    if (clientSocketChannel != null) {
                        clientSocketChannel.configureBlocking(false);
                        SelectionKey clientKey = clientSocketChannel.register(selector, SelectionKey.OP_READ, SelectionKey.OP_WRITE);
                        Map<String, String> clientproperties = new HashMap<String, String>();
                        clientproperties.put(CHANNEL_TYPE, CLIENT_CHANNEL_TYPE);
                        clientKey.attach(clientproperties);
                    }
                } else {
                    SocketChannel clientSocketChannel = (SocketChannel) key.channel();
                    if (key.isReadable()) {
                        Request request = new RequestReceiver().getAppRequest(clientSocketChannel);
                        Response response = new AppController().execute(request);
                        IEDResponseHandler responseHandler = WebResponseHandlerFactory.getInstance().getResponseHandler(response.getPayloadType());
                        responseHandler.handle(response, clientSocketChannel);
                    }
                    clientSocketChannel.close();
                }
                iterator.remove();
            }
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    } catch (ClassNotFoundException ex) {
        ex.printStackTrace();
    }
}

The handler for the error case(sending a list of objects):

@Override
public void handle(Response arrayOfBeansResponse, SocketChannel clientSocketChannel) throws IOException {
    List payload = (List) arrayOfBeansResponse.getPayload();
    String listLength = String.valueOf(payload.size());
    while (listLength.length() < 10) {
        listLength = "0" + listLength;
    }
    CharBuffer buffer = CharBuffer.wrap(listLength);
    while (buffer.hasRemaining()) {
        clientSocketChannel.write(Charset.defaultCharset().encode(buffer));
    }
    buffer.clear();
    Iterator i = payload.iterator();
    int count = 0;
    while (i.hasNext()) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
        oos.writeObject(i.next());

        byte[] yourBytes = byteArrayOutputStream.toByteArray();
        String byteLength = String.valueOf(yourBytes.length);
        while (byteLength.length() < 6) {
            byteLength = "0" + byteLength;
        }
        System.out.println("Sending obj" + count++ + " of length" + byteLength);
        buffer = CharBuffer.wrap(byteLength);
        while (buffer.hasRemaining()) {
            clientSocketChannel.write(Charset.defaultCharset().encode(buffer));
        }
        ByteBuffer objectBuffer = ByteBuffer.wrap(yourBytes);
        while (objectBuffer.hasRemaining()) {
            clientSocketChannel.write(objectBuffer);
        }
        buffer.clear();
        objectBuffer.clear();
        oos.close();
        byteArrayOutputStream.close();
    }
}

Client startup code

int port = 4445;
    clientSocketchannel = SocketChannel.open();
    clientSocketchannel.configureBlocking(false);
    clientSocketchannel.connect(new InetSocketAddress("localhost", port));
    while (!clientSocketchannel.finishConnect()) {
    }

Client side code which receives the list of objects:

@Override
public List getResponse(SocketChannel clientSocketChannel) throws IOException, ClassNotFoundException {
    Object singleObj;
    List list = new LinkedList();
    ByteBuffer buffer = ByteBuffer.allocate(10);
    int bytesRead = 0;
    while (buffer.hasRemaining()) {
        bytesRead += clientSocketChannel.read(buffer);
    }
    if (bytesRead > 0) {
        buffer.flip();
        int listSize = Integer.parseInt(Charset.defaultCharset().decode(buffer).toString());
        for (int i = 0; i < listSize; i++) {
            buffer.clear();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            buffer = ByteBuffer.allocate(6);
            bytesRead = 0;
            while (buffer.hasRemaining() && bytesRead != 6) {
                bytesRead += clientSocketChannel.read(buffer);
            }
            if (bytesRead > 0) {
                buffer.flip();
                String buf = Charset.defaultCharset().decode(buffer).toString();
                System.out.println("buffer is: " + buf);
                int size = Integer.parseInt(buf);
                buffer.clear();
                buffer = ByteBuffer.allocate(size);
                bytesRead = 0;
                while (buffer.hasRemaining()) {
                    bytesRead += clientSocketChannel.read(buffer);
                }
                System.out.println("Getting obj " + i + " of length" + size + "bytes read is " + bytesRead);
                if (bytesRead > 0) {
                    buffer.flip();
                    ByteArrayInputStream bis = new ByteArrayInputStream(buffer.array());
                    ObjectInputStream in = new ObjectInputStream(bis);
                    singleObj = in.readObject();
                    list.add(singleObj);
                    buffer.clear();
                    in.close();
                    bis.close();
                } else {
                    throw new IOException("Response object not found");
                }
            } else {
                throw new IOException("Response object header not found");
            }
        }
    }

    return list;
}

Thread.sleep was added just for testing(so that it is not attempting to read before server has written).

Server console output:

run:
Sending obj0 of length000523
Sending obj1 of length000524
Sending obj2 of length000524
Sending obj3 of length000524
Sending obj4 of length000513
Sending obj5 of length000514
Sending obj6 of length000517
Sending obj7 of length000532
Sending obj8 of length000518
Sending obj9 of length000532
Sending obj10 of length000517
Sending obj11 of length000524
Sending obj12 of length000531
Sending obj13 of length000519
Sending obj14 of length000506
Sending obj15 of length000513
Sending obj16 of length000514
Sending obj17 of length000517
Sending obj18 of length000532
Sending obj19 of length000518
Sending obj20 of length000532
Sending obj21 of length000517
Sending obj22 of length000524
Sending obj23 of length000531
Sending obj24 of length000513
Sending obj25 of length000514
Sending obj26 of length000517
Sending obj27 of length000532
Sending obj28 of length000518
Sending obj29 of length000532
Sending obj30 of length000517
Sending obj31 of length000524
Sending obj32 of length000531
Sending obj33 of length000391
Sending obj34 of length000349

Client console output:

    run:
buffer is: 000523
Getting obj 0 of length523bytes read is 523
buffer is: 000524
Getting obj 1 of length524bytes read is 524
buffer is: 000524
Getting obj 2 of length524bytes read is 524
buffer is: 000524
Getting obj 3 of length524bytes read is 524
buffer is: 000513
Getting obj 4 of length513bytes read is 513
buffer is: 000514
Getting obj 5 of length514bytes read is 514
buffer is: 000517
Getting obj 6 of length517bytes read is 517
buffer is: 000532
Getting obj 7 of length532bytes read is 532
buffer is: 000518
Getting obj 8 of length518bytes read is 518
buffer is: 000532
Getting obj 9 of length532bytes read is 532
buffer is: 000517
Getting obj 10 of length517bytes read is 517
buffer is: 000524
Getting obj 11 of length524bytes read is 524
buffer is: 000531
Getting obj 12 of length531bytes read is 531
buffer is: 000519
Getting obj 13 of length519bytes read is 519
buffer is: 000506
Getting obj 14 of length506bytes read is 506
buffer is: 000513
Getting obj 15 of length513bytes read is 513
buffer is: ��  sr
Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: "��
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
at java.lang.Integer.parseInt(Integer.java:499)
at ****.responsehandler.ArrayOfBeansResponseHandler.getResponse(ArrayOfBeansResponseHandler.java:46)

There is only one server and one client(in this case).

Other information:

  • OS: Windows 7
  • Java 6

Here's what I am not looking for:

  • Debates on why NIO
  • Advice to use already existing frameworks like Apache MINA, Netty, Grizzly or anything else
  • Sending the list object as a single object instead of sending the objects within it

Could anyone explain anything wrong with the code? Why I am unable to receive more than 16 objects? If something is wrong, kindly post the correct code as well.

(P.S.: Its not that I am opposed to the points written in the 'What I am not lokking for' section. Just that, I want to figure out what is wrong with this code)

Updates to original question:

  • I had used Charset.forName("UTF-8") at one place. Dreamer and fge suggested to use defaultCharset. Changed, with no luck- the problem still persists.
Was it helpful?

Solution

while (buffer.hasRemaining()) {
        clientSocketChannel.write(Charset.defaultCharset().encode(buffer));
    }

This is already wrong. You should at least loop sending the same buffer. If the first iteration fails to complete the write you will be losing or resending data here, not sure which offhand.

The general technique of looping while hasRemaining() has knobs on it. If you ever encounter a short or zero write this burns the CPU. You should be using select() to tell you when the channel becomes writable again after you get a zero length write.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top