Search code examples
javasocketsiostream

Java socket client-server connection. Why does the server read from the client's input and not from the output stream?


I'm really confused about how and why this example of client-server connection works.

I have two classes, NetworkClient and NetworkServer.

NetworkClient creates a socket and connects it to the server:

class NetworkClient {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("localhost", 3141);

            OutputStream out = socket.getOutputStream();
            PrintStream ps = new PrintStream(out, true);
            InputStream in = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            ps.println("Es begab sich aber zu der Zeit,");
            System.out.println("client sent: Es begab sich aber zu der Zeit,");

            System.out.println("client received: "+reader.readLine());
        }
        //some other code
    }
} 

And NetworkServer accepts that socket:

public class NetworkServer {
    private final ServerSocket srv;
    public NetworkServer(int port) throws IOException {
        srv = new ServerSocket(port);
    }

    private void connectAndTalk() {
        Socket socket = null;
        try {
            socket = srv.accept();
            communicate(socket);
        }
        // some code
    }

    private void communicate(Socket socket) throws IOException, InterruptedException {
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintStream out = new PrintStream(socket.getOutputStream());
        String s;

        do {
            s = in.readLine();
            System.out.println("server received: "+s);

            if(s.equals("Es begab sich aber zu der Zeit,")) {
                TimeUnit.SECONDS.sleep(4);
                out.println("dass ein Gebot ausging vom Kaiser Augustus,");
            }
            // some code

The client writes to his output stream and reads from his input.

Why does the server read from the client's input and not from the output stream? And, analogously, why does the server write to the client's output stream? Why doesn't it look like this?

    private void communicate(Socket socket) throws IOException, InterruptedException {
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getOutputStream()));
        PrintStream out = new PrintStream(socket.getInputStream());

Solution

  • Metaphorical example

    Imagine a conversation between two people, person C and person S.

    1. Person C speaks to S with the mouth of C. Person S receives C's speach with the ears of S. This is the one way of the communication.
    2. Person S speaks to C with the mouth of S. Person C receives S's speach with the ears of C. This is the other way of the communication (hence a two-way communication).

    This needs:

    1. Person C's mouth.
    2. Person C's ears.
    3. Person S's mouth.
    4. Person S's ears.

    As you can see above, there are 4 elements required to enabled this two way communication.

    The metaphor is:

    1. Person C's mouth is the OutputStream of person C.
    2. Person C's ears are the InputStream of person C.
    3. Person S's mouth is the OutputStream of person S.
    4. Person S's earch are the InputStream of person S.
    5. Person C is the client-side Socket. It holds its two streams (mouth and ears).
    6. Person S is the server-side Socket. It holds its two streams (mouth and ears).

    The communication involves two people. Likewise there exist two separate Java-wise instances of Socket: one at client and one at server, each with a pair of InputStream/OutputStream. This enables two way communication. "Person C speaks using its mouth" is translated to "client-side Socket sends data using its OutputStream". Person C decides what to say to the other end of the communication, hence they write to their OutputStream that data. The other end of the communication does not know what data is going to be sent by C, so they read their InputStream to figure out.

    Why does the server read from the client's input and not from the output stream? And, analogously, why does the server write to the client's output stream?

    There are some obvious rules (let me say it this way) which dictate the communication:

    1. Any person can only hear from their own ears, ie each Socket can only receive from its own InputStream. A person cannot hear from any mouth, nor can they hear from any other individual's ears, ie each Socket cannot receive from any OutputStream, nor can it receive from the other endpoint's InputStream.
    2. Any person can only speak using their own mouth, ie each Socket can only send using its own OutputStream. A person cannot speak from any ear, nor can they speak from any other individual's mouth, ie each Socket cannot send using any InputStream, nor can it send from the other endpoint's OutputStream.

    Example code (matching the metaphor)

    Server side code:

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server {
        public static void main(final String[] args) throws IOException {
            try (final ServerSocket serverSocket = new ServerSocket(12345);
                 final Socket sPerson = serverSocket.accept()) { //This is one of the Sockets in the two way communication.
                final OutputStream sPersonMouth = sPerson.getOutputStream();
                final InputStream sPersonEars = sPerson.getInputStream();
                sPersonMouth.write(3);
                System.out.println("Sent 3 to client!");
                System.out.println("Received from client: " + sPersonEars.read());
            }
        }
    }
    

    Server side output:

    Sent 3 to client!
    Received from client: 2
    

    Client side code:

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class Client {
        public static void main(final String[] args) throws IOException {
            try (final Socket cPerson = new Socket("localhost", 12345)) { //This is one of the Sockets in the two way communication.
                final OutputStream cPersonMouth = cPerson.getOutputStream();
                final InputStream cPersonEars = cPerson.getInputStream();
                cPersonMouth.write(2);
                System.out.println("Sent 2 to server!");
                System.out.println("Received from server: " + cPersonEars.read());
            }
        }
    }
    

    Client side output:

    Sent 2 to server!
    Received from server: 3
    

    The above code illustrates the metaphor through corresponding variable names.

    Note

    This is answer is just a metaphorical example of what @RemyLebeau is saying...