0

I've been fighting with this and my own poor coding skills apparently.

I'm trying to create a small desktop app that will act essentially as a "Socket Listener" where I can display any information sent to the localhost IP and any port I designate. I have the program working without a UI, but I wanted to make it an actual desktop app (part of my learning journey).

I've searched around and borrowed some ideas from others and almost have it working.

When I launch the app, enter a port number and then try to connect to it using PUTTY I don't see anything displaying in my JTextArea. What should appear is a "Waiting for client" message and then when connected a "Client connected" message, then, of course, any text I type into PUTTY.

If I try connecting using PUTTY, type some text, then exit putty, all of that text and the messages above will appear in the JTextArea (JTextArea is at the bottom of the code).

What on earth am I doing wrong here?

import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import java.awt.Font;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import java.awt.event.ActionEvent;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JTextArea;

public class Server extends JFrame {

    private JPanel contentPane;
    private JTextField portInput;
    static int sessionNum = 1;
    static String IPNum = "127.0.0.1";
    public JButton btnListen;
    private JTextField textField;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Server frame = new Server();
                    frame.setVisible(true);


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

    /**
     * Create the frame.
     */
    public Server() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 570, 503);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JLabel lblAddress = new JLabel("IP Address:");
        lblAddress.setHorizontalAlignment(SwingConstants.RIGHT);
        lblAddress.setFont(new Font("Tahoma", Font.PLAIN, 16));
        lblAddress.setBounds(10, 29, 81, 27);
        contentPane.add(lblAddress);

        JLabel lblDisplayAddress = new JLabel("127.0.0.1");
        lblDisplayAddress.setFont(new Font("Tahoma", Font.PLAIN, 16));
        lblDisplayAddress.setBounds(101, 29, 81, 27);
        contentPane.add(lblDisplayAddress);

        JLabel lblPort = new JLabel("Port: ");
        lblPort.setHorizontalAlignment(SwingConstants.RIGHT);
        lblPort.setFont(new Font("Tahoma", Font.PLAIN, 16));
        lblPort.setBounds(210, 33, 90, 19);
        contentPane.add(lblPort);

        portInput = new JTextField();
        portInput.setFont(new Font("Tahoma", Font.PLAIN, 16));
        portInput.setBounds(297, 31, 111, 22);
        contentPane.add(portInput);
        portInput.setColumns(10);

        btnListen = new JButton("LISTEN");
        btnListen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                int portNum =  Integer.parseInt(portInput.getText());

                try{
                    ServerSocket ss=new ServerSocket(portNum, sessionNum, InetAddress.getByName(IPNum));
                    System.out.println("Waiting for client.........");
                    Socket s=ss.accept();//establishes connection
                    System.out.println("Client connected.........");
                    var rawIn = s.getInputStream();
                    var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.UTF_8)); {


                        while (true){
                            String cmd = in.readLine();
                            if (cmd == null) break; //client is hung up
                            if (cmd.isEmpty()) continue; //empty line was sent
                            System.out.println("Client sent: " + cmd);
                        }

                        System.out.println("Closing connection");

                        s.close();
                    }

                }catch(IOException i){System.out.println(i);}

            }
        });
        btnListen.setFont(new Font("Tahoma", Font.PLAIN, 16));
        btnListen.setBounds(450, 31, 89, 23);
        contentPane.add(btnListen);

        JTextArea output = new JTextArea();
        output.setBounds(10, 77, 536, 382);
        contentPane.add(output);
        PrintStream out = new PrintStream(new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                output.append(""+(char)(b & 0xFF));
            }
        });
        System.setOut(out);


    }
}

UPDATED

Thank you so much for the input. I've incorporated your changes and it is so unbelievably close.

One question. When I run the current code (below), Why doesn't the JTextArea show the "Waiting for client..." message right away? It shows up once PUTTY connects, but not before.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import java.awt.Font;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import java.awt.event.ActionEvent;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JTextArea;

public class Server extends JFrame {

    private JPanel contentPane;
    private JTextField portInput;
    static int sessionNum = 1;
    static String IPNum = "127.0.0.1";
    public JButton btnListen;
    private JTextField textField;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Server frame = new Server();
                    frame.setVisible(true);


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

    /**
     * Create the frame.
     */
    public Server() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 570, 503);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JLabel lblAddress = new JLabel("IP Address:");
        lblAddress.setHorizontalAlignment(SwingConstants.RIGHT);
        lblAddress.setFont(new Font("Tahoma", Font.PLAIN, 16));
        lblAddress.setBounds(10, 29, 81, 27);
        contentPane.add(lblAddress);

        JLabel lblDisplayAddress = new JLabel("127.0.0.1");
        lblDisplayAddress.setFont(new Font("Tahoma", Font.PLAIN, 16));
        lblDisplayAddress.setBounds(101, 29, 81, 27);
        contentPane.add(lblDisplayAddress);

        JLabel lblPort = new JLabel("Port: ");
        lblPort.setHorizontalAlignment(SwingConstants.RIGHT);
        lblPort.setFont(new Font("Tahoma", Font.PLAIN, 16));
        lblPort.setBounds(210, 33, 90, 19);
        contentPane.add(lblPort);

        portInput = new JTextField();
        portInput.setFont(new Font("Tahoma", Font.PLAIN, 16));
        portInput.setBounds(297, 31, 111, 22);
        contentPane.add(portInput);
        portInput.setColumns(10);

        btnListen = new JButton("LISTEN");
        btnListen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                int portNum =  Integer.parseInt(portInput.getText());

                try {
                    ServerSocket ss = new ServerSocket(portNum, sessionNum, InetAddress.getByName(IPNum));
                    System.out.println("Waiting for client.........");
                    Socket s = ss.accept();// establishes connection
                    Thread t = new Thread(new Runnable() {
                        public void run() {
                            try {
                                System.out.println("Client connected.........");
                                var rawIn = s.getInputStream();
                                var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.UTF_8));
                                {
                                    while (true) { // Don't do this on the EDT.
                                        String cmd = in.readLine();
                                        if (cmd == null)
                                            break; // client is hung up
                                        if (cmd.isEmpty())
                                            continue; // empty line was sent
                                        System.out.println("Client sent: " + cmd);
                                    }

                                    System.out.println("Closing connection");

                                    s.close();
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    t.start();
                } catch (IOException i) {
                    System.out.println(i);
                }
            }
        });
        btnListen.setFont(new Font("Tahoma", Font.PLAIN, 16));
        btnListen.setBounds(450, 31, 89, 23);
        contentPane.add(btnListen);

        JTextArea output = new JTextArea();
        output.setBounds(10, 77, 536, 382);
        contentPane.add(output);
        PrintStream out = new PrintStream(new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                output.append(""+(char)(b & 0xFF));
            }
        });
        System.setOut(out);


    }
}

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
bwc
  • 1
  • 3
  • 1
    You're breaking Swing threading rules with your while true loop which will block the Swing event thread. Use a SwingWorker to help you create a background thread, and listen to the socket from that thread – DontKnowMuchBut Getting Better Feb 20 '22 at 00:04

1 Answers1

1

You have a ServerSocket blocking to read the client connection on the Event Dispatch Thread. Spawn a new thread when you accept a connection. Something like,

btnListen.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {

        int portNum = Integer.parseInt(portInput.getText());

        try {
            ServerSocket ss = new ServerSocket(portNum, sessionNum, InetAddress.getByName(IPNum));
            System.out.println("Waiting for client.........");
            final Socket s = ss.accept();// establishes connection
            Thread t = new Thread(new Runnable() {
                public void run() {
                    try {
                        System.out.println("Client connected.........");
                        var rawIn = s.getInputStream();
                        var in = new BufferedReader(new InputStreamReader(rawIn, StandardCharsets.UTF_8));
                        {
                            while (true) { // Don't do this on the EDT.
                                String cmd = in.readLine();
                                if (cmd == null)
                                    break; // client is hung up
                                if (cmd.isEmpty())
                                    continue; // empty line was sent
                                System.out.println("Client sent: " + cmd);
                            }

                            System.out.println("Closing connection");

                            s.close();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        } catch (IOException i) {
            System.out.println(i);
        }
    }
});
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • 1
    One plus, although if the OP wants to update the text area, they of course will need to take care to do so on the EDT (as I'm sure you well know). A SwingWorker's publish and process method pair would work well for this. I'm on my phone, but otherwise I'd give them a link to Concurrency and Swing – DontKnowMuchBut Getting Better Feb 20 '22 at 00:24
  • 1
    @DontKnowMuchButGettingBetter [Indeed](https://stackoverflow.com/a/60604571/2970947). – Elliott Frisch Feb 20 '22 at 00:35
  • Really appreciate the feedback. I've incorporated your changes and edited the original post above. It's almost perfect. Just weird that when I launch the app, enter a port and click the button is doesn't show: "Waiting for client......" until after the client connects. – bwc Feb 20 '22 at 01:34
  • You have to do the `accept()` in a new thread, and create a new thread per accepted connection. – user207421 Feb 20 '22 at 05:17
  • O.k., so basically use multi-threading to have the “Waiting for connection…” statements in their own thread and everything else in another? – bwc Feb 20 '22 at 22:52
  • @bwc No. The EDT is the event dispatch thread. You do everything in another thread or the GUI stops responding because you are working on its' thread. The EDT is what lets you click on things in your application. It also draws the things in your applications. – Elliott Frisch Feb 21 '22 at 01:17
  • O.k., I'm researching EDT and Swingworker and will try to implement it and report back. Thanks again! – bwc Feb 21 '22 at 02:44