Search code examples
javaniononblockingsocketchannel

Java NIO - SocketChannel.write() on server many times but Client only receive result once


I'm doing an exercise requires making a server - client chat program using Java Non-Blocking IO. At the moment, the way the program works is simple: when a client send a message to the server, the server (which already keep track of all the clients) echo the message back to all the clients.

This is my some parts of my server-side code:

public static ByteBuffer str_to_bb(String msg) {
    try {
        return encoder.encode(CharBuffer.wrap(msg));
    } catch(Exception e) {
        e.printStackTrace();
    }
    return null;
}

private static void broadcastMessage(String nickname, String message) {
    System.out.println(">clientSocketChannels size " + clientSocketChannels.size());
    Iterator clientSocketChannelsIterator = clientSocketChannels.iterator();
    while (clientSocketChannelsIterator.hasNext()) {
        SocketChannel sc = (SocketChannel) clientSocketChannelsIterator.next();
        try {
            ByteBuffer bb = str_to_bb(message);
            System.out.println("bufferRemaining: " + bb.remaining()); // returns 2048
            int writeResult = sc.write(bb);
            System.out.println("writeResult: " + writeResult); // returns 2048
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The following is my client-side code:

import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

/**
 * Created by ThaiSon on 7/6/2015.
 */
public class ChatRoomClientGUI {
    private JTextArea textAreaMessages;
    private JTextField textFieldMessage;
    private JButton buttonSendMsg;
    private JPanel jPanel1;
    private JLabel txtFieldInfo;

    private static InetAddress inetAddress;
    private static final int PORT = 1234;
    private static Socket socket = null;
    private static Scanner input = null;
    private static PrintWriter output = null;

    private static ChatRoomClientGUI singleton;

    public ChatRoomClientGUI() {
        singleton = this;
        buttonSendMsg.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                if (e.getButton() == MouseEvent.BUTTON1) {
                    String message = textFieldMessage.getText();
                    output.println(message);
                    textFieldMessage.setText("");
                }
            }
        });
    }

    public static void main(String[] args) {
        JFrame promptFrame = new JFrame();
        Object nickname = JOptionPane.showInputDialog(promptFrame, "Enter your nickname:");
        promptFrame.dispose();

        JFrame frame = new JFrame("ChatRoomClientGUI");
        frame.setContentPane(new ChatRoomClientGUI().jPanel1);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);

        System.out.println("> Client with nickname " + nickname);

        try {
            inetAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        accessServer(nickname.toString());
    }

    private static void accessServer(String nickname) {
        try {
            socket = new Socket(inetAddress, PORT);
            input = new Scanner(socket.getInputStream());
            output = new PrintWriter(socket.getOutputStream(), true);
            output.println(nickname); // Register nickname with the server

            //TODO update the txtFieldInfo content

            // Create a new thread to listen to InputStream event
            InputStreamEvent inputStreamEvent = new InputStreamEvent(socket);
            inputStreamEvent.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void handleInputStream(){
        String response = input.nextLine();
        System.out.println("TODO " + response);
        singleton.textAreaMessages.append(response + "\n");
    }

    static class InputStreamEvent extends Thread{
        Socket socket;
        public InputStreamEvent(Socket socket){
            this.socket = socket;
        }
        public void run(){
            try {
                InputStream inputStream = socket.getInputStream();
                byte[] buffer = new byte[2048];
                int read;
                while (true){
                    if(inputStream.available() > 0){
                        handleInputStream();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

The problem I'm facing with now is that when I use a client (which works well with the old multithreaded server) to send message to the server, the client only get return the first message it sends. All the next responses from the server is empty (the server does send back, but only an empty message).

So my attempts to debug includes:

  • check if the messages from the client has reach the server or not. They does.
  • log the buffer.remaining() and socketChannel.write(buffer) result as shown above, all the log result seems to be normal to me.

Hope you guys can help me with this.


Solution

    1. This:

      if(inputStream.available() > 0){
      

      Get rid of this test. With it, your client is smoking the CPU. Without it, it will block in readLine() as God intended.

    2. Are you sure your server is still sending lines? with line terminators? If it isn't, readLine() will block forever looking for one, until end of stream or an exception occurs.