javaarrayssocketsdatainputstreamdataoutputstream

How should a client properly wait for a reponse from the server after a message has been sent?


I am trying to make a client and server properly echo messages.

This means the client should send a message to the server, the server should receive and send the same message back to the client, and then the client prints out the received message.

I am having trouble making the client properly wait for the server to finish sending the response before the client then receives it and prints it out.

The only way it sort of works is when I comment out the EchoClient2.java RECEIVE MESSAGE code.

EchoClient2.java

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class EchoClient2 {
    private Socket clientSocket;
    private DataOutputStream out;
    private DataInputStream in;

    private void start(String ip, int port) {
        try {
            clientSocket = new Socket(ip, port);
            out = new DataOutputStream(clientSocket.getOutputStream());
            in = new DataInputStream(clientSocket.getInputStream());
        } catch (IOException e) {
            System.out.println("Error when initializing connection");
        }
    }

    private void sendMessage(String message) throws IOException {
        //SENDING MESSAGE
        byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);

        System.out.println("Client will send the text: " + message);

        out.write(messageBytes);
        out.flush();
    
        //RECEIEVE MESSAGE - Doesn't give server a chance to respond?
        byte[] data = new byte[8];
        ByteArrayOutputStream getData = new ByteArrayOutputStream();
        int numBytes;
        while ((numBytes = in.read(data)) != -1) { 
            getData.write(data, 0, numBytes);
        }
        
        byte[] message2Bytes = getData.toByteArray();
        String text = new String(message2Bytes, StandardCharsets.UTF_8);
        
        System.out.println("Server sent the client text: " + text);
    }

    private void stop() {
        try {
            in.close();
            out.close();
            clientSocket.close();
        } catch (IOException e) {
            System.out.println("error when closing");
        }
    }

    public static void main(String[] args) throws IOException {
        EchoClient2 client = new EchoClient2();
        client.start("127.0.0.1", 4444);
        client.sendMessage("exit");
        client.stop();
    }
}

EchoServer2.java

import java.net.*;
import java.nio.charset.StandardCharsets;
import java.io.*;

public class EchoServer2 {
    private ServerSocket serverSocket;
    private Socket clientSocket;
    private DataOutputStream out;
    private DataInputStream in;

    public void start(int port) {
        try {
            serverSocket = new ServerSocket(port);
            clientSocket = serverSocket.accept();
            out = new DataOutputStream(clientSocket.getOutputStream());
            in = new DataInputStream(clientSocket.getInputStream());   
        } catch (IOException e) {
            System.out.println("Error when establishing/accepting server connection");
        }
    }

    private void actionMessage() throws IOException {
        //RECEIEVE MESSAGE
        byte[] data = new byte[8];
        ByteArrayOutputStream getData = new ByteArrayOutputStream();
        int numBytes;
        while ((numBytes = in.read(data)) != -1) { 
            getData.write(data, 0, numBytes);
        }

        byte[] messageBytes = getData.toByteArray();
        String text = new String(messageBytes, StandardCharsets.UTF_8);
        
        System.out.println("Server got " + text + " and will send this back to client");

        //SENDING BACK SAME MESSAGE
        out.write(messageBytes);
        out.flush();
    }

    public void stop() {
        try {
            in.close();
            out.close();
            clientSocket.close();
            serverSocket.close();
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    public static void main(String[] args) throws IOException {
        EchoServer2 server = new EchoServer2();

        System.out.println("Now waiting for incoming connection...");

        server.start(4444);

        server.actionMessage();
        
        server.stop();
    }
}

Would anyone know why this is not working properly?


Solution

  • The server is reading the client’s input in 8-byte chunks until the client disconnects (which it is not doing), THEN it is writing a response back. The server needs to instead write back each chunk as it arrives, then disconnect after the client has disconnected. That means moving out.write() inside the while reading loop.

    private void actionMessage() throws IOException {
        byte[] data = new byte[8];
        int numBytes;
        while ((numBytes = in.read(data)) != -1) {
            out.write(data, 0, numBytes);
            out.flush();
        }
    } 
    

    This is fine for a simple echo server, but it is not very useful for a more realistic messaging server. You need a more defined protocol to exchange messages back and forth without having to lose the connection in between them (unless you want to implement a stateless protocol, like HTTP, which allows disconnects between requests). For example:

    Client:

    private void sendMessage(String message) throws IOException {
        System.out.println("Client will send the text: " + message);
    
        //SEND MESSAGE
        byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        out.writeInt(messageBytes.length);
        out.write(messageBytes);
        out.flush();
    
        //RECEIVE MESSAGE
        int len = in.readInt();
        byte[] data = new byte[len];
        in.readFully(data);
        String text = new String(data, StandardCharsets.UTF_8);
        System.out.println("Server sent the client text: " + text);
    }
    
    public static void main(String[] args) throws IOException {
        EchoClient2 client = new EchoClient2();
        client.start("127.0.0.1", 4444);
        try {
            client.sendMessage("hello");
            client.sendMessage("exit");
        } catch (IOException e) {
            ...
        }
        client.stop();
    }
    

    Server:

    private void actionMessage() throws IOException {
        //RECEIVE MESSAGE
        int len = in.readInt();
        byte[] data = new byte[len];
        in.readFully(data);
        String text = new String(data, StandardCharsets.UTF_8);
        System.out.println("Server got " + text + " and will send this back to client");
    
        //SEND MESSAGE
        data = text.getBytes(StandardCharsets.UTF_8);
        out.writeInt(data.length);
        out.write(data);
        out.flush();
    }
    
    public static void main(String[] args) throws IOException {
        EchoServer2 server = new EchoServer2();
        System.out.println("Now waiting for incoming connection...");
        server.start(4444);
        while (true) {
            try {
                server.actionMessage();
            } catch (IOException e) {
                ...
                break;
            }
        }
        server.stop();
    }