Search code examples
javasocketsnio

SocketChannel sockets not reading data


Here is my code. From the TestServer, I'm trying to send data through an outputstream and have it received from the test client. I'm using a SocketChannel because I need the client to listen on 3 ports at the same time. At the moment, i'm only trying to read from one socket. However it doesnt seem to be receiving any data from the server. For the run method of KBThread, if I uncomment the nodata println, that will execute over and over.

TestServer.java

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;


public class TestServer extends JPanel implements KeyListener, MouseListener, MouseMotionListener {

    private final int MAX_CLIENTS = 8;

    JPanel listenerPanel = new JPanel();
    JFrame listenerFrame = new JFrame();

    static DataOutputStream kbOut;
    static DataOutputStream mOut;
    static Socket dataSocket;

    public TestServer() {
        this.setFocusable(true);
        listenerPanel.addKeyListener(this);
        listenerPanel.addMouseMotionListener(this);

        listenerFrame.add(listenerPanel);
        listenerFrame.setSize(1376,808); // 10 more x, 40 more y.
        listenerFrame.setVisible(true);
        listenerFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        listenerPanel.requestFocusInWindow();

    }

    public static void main(String[] args) {

        new TestServer().startServer();
    }

    public void startServer() {

        final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(MAX_CLIENTS);

        Runnable serverTask = () -> {
            try {
                ServerSocket serverSocket = new ServerSocket(1111);
                System.out.println("Waiting for clients.");
                while (true) {
                    Socket clientSocket = serverSocket.accept();
                    clientProcessingPool.submit(new ClientTask(clientSocket));
                }
            } catch (IOException ex) {
                System.err.println("Error with client socket.");
            }
        };

        Thread serverThread = new Thread(serverTask);
        serverThread.start();
    }

    private class ClientTask implements Runnable {
        private final Socket clientSocket;

        private ClientTask(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {

            try {
                String clientIP = clientSocket.getInetAddress().getHostAddress();
                System.out.println("Client connected from " + clientIP);

                Socket kbSocket = new Socket(clientIP, 1112);
                System.out.println("Keyboard socket connected to " + clientIP);
                kbOut = new DataOutputStream(kbSocket.getOutputStream());

                Socket mSocket = new Socket(clientIP, 1113);
                System.out.println("Mouse socket connected to " + clientIP);
                mOut = new DataOutputStream(mSocket.getOutputStream());

                //new TestServer().startKBServer(clientIP);
                //new TestServer().startMServer(clientIP);

                try {
                    clientSocket.close();
                } catch (IOException ex) {
                }
            } catch (IOException ex) {
                Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void startKBServer(String clientAddress) {

        Runnable kbTask = () -> {
            try {
                Socket kbSocket = new Socket(clientAddress, 1112);
                System.out.println("Keyboard socket connected to " + clientAddress);
                new KBTask(kbSocket);

            } catch (IOException ex) {
                System.out.println("Error Calling Back " + clientAddress);
            }
        };

        Thread kbThread = new Thread(kbTask);
        kbThread.start();
    }

    private class KBTask implements Runnable {
        private final Socket kbSocket;

        private KBTask(Socket kbSocket) {
            this.kbSocket = kbSocket;
        }

        @Override
        public void run() {
            try {
                kbOut = new DataOutputStream(kbSocket.getOutputStream());
            } catch (IOException ex) {
                Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    @Override
    public void keyPressed(KeyEvent ke) {
        try {
            int key = ke.getKeyCode();

            System.out.println("Key Pressed: " + key);

            kbOut.writeInt(key);
            kbOut.flush();

        } catch (IOException ex) {
            System.out.println("Error writing key data to server");
        }
    }

    @Override
    public void keyReleased(KeyEvent ke) {
        try {
            int key = ke.getKeyCode();

            System.out.println("Key Pressed: " + -key);

            kbOut.writeInt(-key);
            kbOut.flush();

        } catch (IOException ex) {
            System.out.println("Error writing -key data to server");
        }
    }

        @Override
    public void mouseMoved(MouseEvent me) {
        try {
            int mouseX = me.getX();
            int mouseY = me.getY();

            if (mOut != null) {
                mOut.writeInt(mouseX);
                mOut.writeInt(mouseY);
                mOut.flush();
                System.out.println("Mouse Moved To: " + mouseX + "," + mouseY);
            }


        } catch (IOException | NullPointerException ex) {
            System.out.println("Error writing mouse data to server");
        }
    }

    @Override
    public void mouseClicked(MouseEvent me) {

    }

    @Override
    public void mousePressed(MouseEvent me) {

    }

    @Override
    public void mouseReleased(MouseEvent me) {

    }

    @Override
    public void mouseEntered(MouseEvent me) {

    }

    @Override
    public void mouseExited(MouseEvent me) {

    }

    @Override
    public void mouseDragged(MouseEvent me) {

    }

    @Override
    public void keyTyped(KeyEvent ke) {

    }
}

TestClient.java

import java.awt.AWTException;
import java.awt.Robot;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class TestClient {

    private final static String SERVER_IP = "192.168.0.50";

    JPanel clientPanel = new JPanel();
    JFrame clientFrame = new JFrame();

    public void setupGUI() {

        clientFrame.add(clientPanel);
        clientFrame.setSize(200,200);
        clientFrame.setVisible(true);
        clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        clientPanel.requestFocusInWindow();
    }

    public static void main(String[] args) {

        try {
            new TestClient().setupGUI();

            Robot keyRobot = new Robot();

            Socket firstSocket = new Socket(SERVER_IP, 1111);
            System.out.println("Connected to Commander. Address sent. Waiting for callback.");
            firstSocket.close();

            Selector selector = Selector.open();

            int ports[] = new int[] { 1112, 1113 };

            for (int port : ports) {
                ServerSocketChannel serverChannel = ServerSocketChannel.open();
                serverChannel.configureBlocking(false);
                serverChannel.socket().bind(new InetSocketAddress(port));
                serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            }

            while (true) {
                // After the 2 accept methods fire, it stops here and program doesnt continue.
                selector.select();
                Set setKeys = selector.selectedKeys();
                Iterator selectedKeys = setKeys.iterator();


                while (selectedKeys.hasNext()) {
                    SelectionKey selectedKey = (SelectionKey) selectedKeys.next();
                    if (selectedKey.isAcceptable()) {
                        SocketChannel socketChannel = ((ServerSocketChannel) selectedKey.channel()).accept();
                        socketChannel.configureBlocking(false);

                        switch (socketChannel.socket().getLocalPort()) {
                            case 1112:
                                System.out.println("Keyboard socket open.");
                                Runnable kbr = new KBThread(socketChannel.socket());
                                new Thread(kbr).start();
                                break;

                            case 1113:
                                System.out.println("Mouse socket open.");
                                break;
                        }
                    }
                    selectedKeys.remove();
                }


            }

            } catch (ConnectException ece) {
            System.out.println("Failed to connect to server: " + SERVER_IP);
        } catch (IOException | AWTException eio) {
            Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, eio);
        }
    }

    private static class KBThread implements Runnable {
        private final Socket kbSocket;
        private int dataID = 0;

        private KBThread(Socket kbSocket) {
            this.kbSocket = kbSocket;
        }

        @Override
        public void run() {

            try {
                DataInputStream kbDis = new DataInputStream(kbSocket.getInputStream());
                while (true) {
                    try {


                        if (kbDis.available() > 0) {
                            dataID = kbDis.readInt();
                            System.out.println(dataID);
                        }
                        //else System.out.println("noData");
                    } catch (IOException ex) {
                        Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            } catch (IOException ex) {
                Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Solution

  • This is not how IO multiplexing is supposed to work.

    Once a connection is accepted and marked non-blocking, add it to the same selector with OP_READ. Check for readable event in the loop, read from the socket. You don't need threads for this.

    Alternatively, if you want to go with threads, don't set accepted client connection as non-blocking, just do regular reads from it in the dedicated thread.

    Edit 0:

    The best advise I can offer is to go over some good Java NIO tutorial. There many on the Intenet, but you can start here: