5

I have spent a while reading other posts, and nobody seems to have the same case as me. I have tried all solutions I have read with no results, so I decided to create this question.

I have been working on a server/client application, and the client seems to not read data from the socket, but the server can read data the client sends. The client freezes while trying to read the line. Here's the code I have:

Client:

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;

public class Main {

    public static Socket s = connect();

    public static void sendMessage(String msg) {
        if (s != null) {
            PrintWriter outWriter = null;
            try {
                outWriter = new PrintWriter(s.getOutputStream(), true);
                outWriter.println(msg);
                outWriter.flush();
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                outWriter.close();
            } finally {
            }
        } else {
            System.err.println("Error, socket is null!");
        }
    }

    public static String readMessage() {
        if (s != null) {
            Scanner in = null;
            try {
                in = new Scanner(s.getInputStream());
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            String ss = "";
            while (in.hasNext()) {
                ss += in.next();
            }
            System.out.println("REPONSE:" + ss);
            return ss;
        } else {
            System.err.println("Error, socket is null!");
        }
        return "err/readMessage";
    }

    public static Socket connect() {
        try {
            Socket sss = new Socket("localhost", 25586);
            sss.setKeepAlive(true);
            return sss;
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(-1);
        }
        return null;
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {

                Thread t = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        sendMessage("HELO");
                        System.out.println("Thread initialized.");

                        while (true) {
                            try {
                                System.out.println("Awaiting message...");
                                Thread.sleep(100);
                                String messages = readMessage();
                                System.out.println("Message recieved! '" + messages + "'");
                                String[] message = messages.split("/");
                                System.out.println(messages);
                                if (message[0].equalsIgnoreCase("DERP")) {                                       // err/reason
                                    System.out.println("IT'S FINALLY WORKING!");
                                } else {
                                    System.out.println("Didn't work :( response:" + messages);
                                }
                            } catch (InterruptedException ex) {
                                ex.printStackTrace();
                            }

                        }
                    }
                });
                t.start();
            }
        });
    }
}

Server:

import java.util.List;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main { /* SERVER */


    public static List<EchoThread> list = new ArrayList<>();

    public static int PORT = 25586;

    public static void main(String[] args) throws IOException {
        new Main(PORT);
    }

    public Main(int port) throws IOException {
        this.PORT = port;
        ServerSocket serverSocket = null;
        Socket socket = null;

        try {
            serverSocket = new ServerSocket(PORT);
        } catch (IOException e) {
            e.printStackTrace();

        }
        while (true) {
            try {
                socket = serverSocket.accept();
            } catch (IOException e) {
                System.out.println("I/O error: " + e);
            }
            // new threa for a client
            EchoThread s = new EchoThread(socket);
            s.start();
            list.add(s);
        }
    }

    public static void sendMessageToAll(String ss) {
        for (EchoThread s : list) {
            s.allMessage(ss);
        }
    }
}

class EchoThread extends Thread {

    protected Socket socket;
    InputStream inp = null;
    BufferedReader brinp = null;

    public EchoThread(Socket clientSocket) {
        this.socket = clientSocket;
        try {
            inp = socket.getInputStream();
            brinp = new BufferedReader(new InputStreamReader(inp));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        String line;
        while (true) {
            try {
                line = brinp.readLine();
                if ((line == null) || line.equalsIgnoreCase("EXIT")) {
                    socket.close();
                    return;
                } else {
                    handle(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        }
    }

    public void handle(String s) {
        String[] keys = s.split("/");
        System.out.println("random: Handling request\"" + s + "\"");
        System.out.println("Response: DERP");
        if (s.equalsIgnoreCase("HELO")) {
            System.out.println("Message recieved:" + s);
        }
        respond("DERP");
    }

    public void respond(String s) {
        try {
            System.out.println("Response_WILLBE:" + s);
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os, true);
            pw.println(s);
            pw.flush();
            System.out.println("Message sent!");
        } catch (Exception ex) {
            Logger.getLogger(EchoThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void allMessage(String s) {
        respond(s);
    }
}

I tried the flush() code and \r\n and println fixes, but none worked! -- Thank you for reading!

meowy cat
  • 121
  • 1
  • 7
  • 3
    Regardless of whether or not you have tried newlines and flushes and they failed to work, you should use them anyways (they are necessary). Make sure that every message sent using a PrintWriter (or any text-based output stream) ends with \n, and make sure to call `flush` after sending each message. – Alexander Guyer Nov 29 '15 at 00:59
  • +Nerdizzle Thanks for your answer! I've already tried that, but it didn't work. I figured since I'm using println instead of print it should work with or without the \n, but just in case, I tried it, and it still failed. I checked, and I am using PrintWriter, and I am flushing (as seen in the above code) – meowy cat Nov 29 '15 at 01:05
  • 2
    Your server produces a potentially infinite stream of data. `while (in.hasNext())` in the client will never terminate unless the server closes the connection. You can replace all that with `String ss = in.next();` or maybe `if (in.hasNext())`. Your client was receiving the data but was too greedy – zapl Nov 29 '15 at 01:08
  • Since this contained requests to prioritise this over other questions, I am downvoting with regret. Readers here are mostly volunteers who will answer questions that interest them, and they do so at their leisure. (Your question is otherwise quite a good one). – halfer Nov 29 '15 at 09:16

4 Answers4

4

I'm very curious about the differences between the client's and server's methods of communication. For instance, the client uses a Scanner to read input while the server uses a BufferedReader (which is my personal preference). Just a suggestion: be consistent.

Now -- firstly, the client only sends one message, but then begins to read indefinitely in an infinite loop. Seeing as you know exactly how the server should respond after you send "HELO" to it (it should respond with one line, "DERP"), there is no reason to read from the server in any sort of loop.

The same issue exists on the server. As the client is right now, it will always only send one line to the server ("HELO"). Consequently, the server should only expect one line and only read one line. There is absolutely no reason to read input within a loop. However, the actual issue persists only in the client's code:

You currently have while(in.hasNext()) as a conditional as to how long the client will read input from the server. The hasNext method differs in logical function depending on the method of communication. In the case of socket communication, hasNext expects a stream and, as such, will always return true unless the socket is closed.

Avoid using loops in socket communication in general unless you don't know how many lines to read. In such a scenario, you should first have the sender send some sort of number to the receiver, and then the receiver should read the subsequent lines in a for loop reading 'n' number of times (where n is the received number).

Here's a very basic client / server communication program using PrintWriters and BufferedReaders.

Client

public static void main(String[] args){
    try{
        Socket socket = new Socket("localhost", 12345);
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out.write("HELO\n"); //print("HELO\n") and println("HELO") should work too.
        out.flush();
        System.out.println("Server says " + in.readLine());
        in.close();
        out.close();
        socket.close();
    }catch(IOException e){e.printStackTrace();}
}

Server

public static void main(String[] args){
    try{
        ServerSocket serverSocket = new ServerSocket(12345);
        while(true){
            new Thread(new ClientConnectionThread(serverSocket.accept())).start();
        }
    }catch(IOException e){e.printStackTrace();}
}

private class ClientConnectionThread implements Runnable{
    private Socket socket;
    public ClientConnectionThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run(){
        try{
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream());
            System.out.println("Client says " + in.readLine());
            out.write("HELO Back!\n"); //again, print("HELO Back!\n") and
                //println("HELO Back!") should also work
            out.flush();
            out.close();
            in.close();
            socket.close();
        }catch(IOException e){e.printStackTrace();}
    }
}

Notice that no loops are necessary in the actual communication.

Alexander Guyer
  • 2,063
  • 1
  • 14
  • 20
  • Thanks soooo much! It is completely and entirely functional now! You just saved me sooooo much time! :) The HELO and DERP were just examples to simplify the program a bit. – meowy cat Nov 29 '15 at 04:00
  • 1
    Thanks for the example for me it was good start point. I've copy-pasted your code and it wasn't working: 1) new BuffereReader(new InputeStreamReader(..)) in client and 2) For my version JDK 9.0 it works only when I add out.flush, after out.println in client. The 2 I've done after reading this question and answer https://stackoverflow.com/questions/40004783/printwriter-wont-write – Nikita Nov 25 '17 at 02:39
  • @Nikita thank you, wrote this a long time ago and my Java was rusty. Added flush calls (though the server one is unnecessary since it flushes upon close), and fixed the client `BufferedReader` constructor – Alexander Guyer Dec 02 '17 at 04:55
1

As @zapl commented your client is far to greedy when reading data from the server. Lets look at the process you use to read data from the server:

String ss = "";
while (in.hasNext()) {
    ss += in.next();
}
System.out.println("REPONSE:" + ss);

This code will continually pull data from the server until the stream is closed. You probably use this type of code block when reading data from a file or parsing a string which is perfect for those cases because you want all the data from the stream into your string.

What you are looking to do is read the first resposne from the server designated by a new line \n. Instead of doing while(in.hasNext()) instead you should just check if in.hasNext() and if so then get that as the response.

String ss = "";
if (in.hasNext()) {
    ss += in.next();
}
System.out.println("REPONSE:" + ss);

Or maybe more elegantly

if (in.hasNext()) {
    String response = in.next();
    System.out.println("REPONSE:" + response);
    return response;
}
....
ug_
  • 11,267
  • 2
  • 35
  • 52
  • Assuming an IOException in the instantiation of an input stream (in this case the scanner, but a BufferedReader is even more easily handled) is to be handled the same way as an unexpected closed stream (meaning in.hasNext() returns false), I believe the `hasNext` method only makes handling errors more difficult and repetitious (since the same code would go in both the catch clause of the scanner's instantiation try block as well as the else statement to `if(in.hasNext())`). Doesn't it make more sense to just use a `BufferedReader` and catch all IOExceptions in the same way? – Alexander Guyer Nov 29 '15 at 01:59
  • @Nerdizzle I agree that using a Scanner is not advisable when working with Sockets. However given OP's question about why isnt his code working this is the cause. If this were the code review part of the StackExchange I would be inclined to write more about my personal opinion about his/her code. – ug_ Nov 29 '15 at 02:06
1

First, with sockets, I always like to run the server and telnet into it via localhost. When I do that with yours, I get the back and forth no problem. So now it's just a matter of fixing your client.

For the client, get rid of (for the time being) your usage of the Scanner class. This class has a whole other set of usages that can cause problems. Instead, keep it simple.

Change your client to use a BufferedReader. Here is your new readMessage() method:

    public  String readMessage() {
        if (s != null) {
            BufferedReader in = null;
            try {
                System.out.println("Creating reader");
                in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            } catch (IOException ex) {
            }
            String ss = "";
            try {
                ss = in.readLine();
            } catch (IOException e) {
            }
            System.out.println("REPONSE:" + ss);
            return ss;

Your client will now work (it does for me), which means you are having issues with the Scanner usage.

I will leave you to debug that since that isn't really what your question is about. Have fun! ;)

HulkSmash
  • 386
  • 1
  • 6
0

Stumbled upon this while looking for something else.

Alexander Guyer, the thing is that the sockets are meant to be completely duplex .. you can have a client sending messages to server on and on and on, without the server responding even once. Maybe you meant that, but I read it differently. If you are referring to request - response mechanism, it is more like Http messages, where the sender expects a response. Socket is an underlying protocol.

That being said, I created a simple example to teach the new-bees in my workplace. It is like this. You can change the sleep param, compile and spawn different clients. There are some exceptions that gets generated, but you have to remember that this is a VERY SIMPLE example. Meant for folks new to socket programming.

You can of course change the server/port to IP Address and port to a different server, I just went ahead with localhost.

Source code:

Server.java

package com.example.practice.sockets;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args) throws IOException {
        Server s = new Server();
        s.startTheProcess();
    }

    private void startTheProcess() throws IOException {
        Common c = new Common();
        Socket s;
        ServerSocket sc = new ServerSocket(9999);
        System.out.println("Waiting for connection ");

        for (;;) {
            try {
                s = sc.accept();
                System.out.println(Thread.currentThread().getName() + " A new client connected " + s);
                handleSocket(s, c);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    private void handleSocket(Socket s, Common c) {
        Thread readMessage = new Thread(new Runnable() {
            @Override
            public void run() {
                c.readFromSocketContinuously(s);
            }
        });

        Thread writeMessage = new Thread(new Runnable() {
            @Override
            public void run() {
                c.writeToSocketContinuously(s, "Server writes: ");
            }
        });

        writeMessage.start();
        readMessage.start();
    }
}

Client.java

package com.example.practice.sockets;

import java.io.IOException;
import java.net.Socket;

public class Client extends Thread {

    public static void main(String[] args) throws IOException {
        Client s = new Client();
        s.startTheProcess();
    }

    private void startTheProcess() throws IOException {
        Common c = new Common();
        Socket s;
        s = new Socket("localhost", 9999);

        handleSocket(s, c);
    }

    private void handleSocket(Socket s, Common c) {
        Thread readMessage = new Thread(new Runnable() {
            @Override
            public void run() {
                c.readFromSocketContinuously(s);
            }
        });

        Thread writeMessage = new Thread(new Runnable() {
            @Override
            public void run() {
                c.writeToSocketContinuously(s, "Client writes: ");
            }
        });

        writeMessage.start();
        readMessage.start();
    }
}

Common.java

package com.example.practice.sockets;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Common {
    
    public void readFromSocketContinuously(Socket s) {
        try {
            for(;s.getKeepAlive() || !s.isClosed();) {
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String name = br.readLine();
                System.out.println(Thread.currentThread().getName() + " readFromSocketContinuously " + name + "\r\n");
                if ( name == null )
                    break;
            }           
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void writeToSocketContinuously(Socket s, String serverOrClient) {
        try {
            for (int count = 0;s.getKeepAlive() || !s.isClosed(); count++) {
                String dataToWrite = serverOrClient + " From socket " + count + "\r\n";
                BufferedOutputStream out = new BufferedOutputStream(s.getOutputStream());
                if (out != null && !s.isClosed()) {
                    out.write(dataToWrite.getBytes());
                    out.flush();
                    System.out.println(Thread.currentThread().getName() + " writeToSocketContinuously " + dataToWrite);
                    Thread.sleep(500);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

E_net4
  • 27,810
  • 13
  • 101
  • 139
user1300830
  • 83
  • 1
  • 10