Java Networking: Explain InputStream and OutputStream in Socket
Asked Answered
B

2

16

I have created a server by using ServerSocket. After that, I have created Client using Socket, and connect to this server.

After that, I do "some stuff" with InputStream and OutputStream is taken from Socket Object. But, I don't really understand inputStream and outputStream so much. Here is my simple code :

private Socket sock = null;
private InputStream sockInput = null;
private OutputStream sockOutput = null;

...
            String msg = "Hello World";
            byte[] buffer = null;

            try {
                sockOutput.write(msg.getBytes(), 0, test.length());
                sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
                buffer = new byte[test.length()];
                sockInput.read(buffer, 0, test.length());
                System.out.println(new String(buffer));
                sockInput.read(buffer, 0, test.length());
                System.out.println(new String(buffer));
            } catch (IOException e1) {
                e1.printStackTrace();
                }

The result will be : "Hello World" and "Hello StackOverFlow".

Here is server side code :

private int serverPort = 0;
    private ServerSocket serverSock = null;

    public VerySimpleServer(int serverPort) {
        this.serverPort = serverPort;

        try {
            serverSock = new ServerSocket(this.serverPort);
        }
        catch (IOException e){
            e.printStackTrace(System.err);
        }
    }

    // All this method does is wait for some bytes from the
    // connection, read them, then write them back again, until the
    // socket is closed from the other side.
    public void handleConnection(InputStream sockInput, OutputStream sockOutput) {
        while(true) {
            byte[] buf=new byte[1024];
            int bytes_read = 0;
            try {
                // This call to read() will wait forever, until the
                // program on the other side either sends some data,
                // or closes the socket.
                bytes_read = sockInput.read(buf, 0, buf.length);

                // If the socket is closed, sockInput.read() will return -1.
                if(bytes_read < 0) {
                    System.err.println("Server: Tried to read from socket, read() returned < 0,  Closing socket.");
                    return;
                }
                System.err.println("Server: Received "+bytes_read
                                   +" bytes, sending them back to client, data="
                                   +(new String(buf, 0, bytes_read)));
                sockOutput.write(buf, 0, bytes_read);
                // This call to flush() is optional - we're saying go
                // ahead and send the data now instead of buffering
                // it.
                sockOutput.flush();
            }
            catch (Exception e){
                System.err.println("Exception reading from/writing to socket, e="+e);
                e.printStackTrace(System.err);
                return;
            }
        }

    }

    public void waitForConnections() {
        Socket sock = null;
        InputStream sockInput = null;
        OutputStream sockOutput = null;
        while (true) {
            try {
                // This method call, accept(), blocks and waits
                // (forever if necessary) until some other program
                // opens a socket connection to our server.  When some
                // other program opens a connection to our server,
                // accept() creates a new socket to represent that
                // connection and returns.
                sock = serverSock.accept();
                System.err.println("Server : Have accepted new socket.");

                // From this point on, no new socket connections can
                // be made to our server until we call accept() again.

                sockInput = sock.getInputStream();
                sockOutput = sock.getOutputStream();
            }
            catch (IOException e){
                e.printStackTrace(System.err);
            }

            // Do something with the socket - read bytes from the
            // socket and write them back to the socket until the
            // other side closes the connection.
            handleConnection(sockInput, sockOutput);

            // Now we close the socket.
            try {
                System.err.println("Closing socket.");
                sock.close();
            }
            catch (Exception e){
                System.err.println("Exception while closing socket.");
                e.printStackTrace(System.err);
            }

            System.err.println("Finished with socket, waiting for next connection.");
        }
    }

    public static void main(String argv[]) {
        int port = 54321;
        VerySimpleServer server = new VerySimpleServer(port);
        server.waitForConnections();
    }

My question is :

  1. When I use sockOutput.write and I can get back those message back by sockInput.read. So, those message has been saved, right? If this true, does it saved on Server I have created or just saved in some other thing such as Socket Object.

  2. If I have written to socket String A1, A2,... An so I will receive A1, A2, ... An String respectively, right?

Bart answered 3/10, 2012 at 19:18 Comment(5)
It would help if we could see the server end too. What do you mean by "saved"? If data goes to the server and back, then it will be held in the server's memory (possibly only briefly). I'm not clear on what your question actually is?Estradiol
It doesn't save it, why do you want to know? Try writing A1, A2 and read and see which one comes first.Athiste
@Estradiol I asked this question because, why I can take again byte array when I use input stream to write to socket.Bart
Assuming that your server is just echoing back the data, your messages are buffered in-memory by the networking layers, waiting for you to call InputStream.read(). Where precisely all that data is at any point in time is a complex question; all that detail is abstracted away by the libraries and networking stack.Estradiol
@Estradiol So, it means : it depend on Server side ? I have add server side code, please point it out for me. Thanks :)Bart
E
40

A socket is an abstraction that you use to talk to something across the network. See diagram below...

In Java, to send data via the socket, you get an OutputStream (1) from it, and write to the OutputStream (you output some data).

To read data from the socket, you get its InputStream, and read input from this second stream.

You can think of the streams as a pair of one-way pipes connected to a socket on the wall. What happens on the other side of the wall is not your problem!

In your case, the server has another socket (the other end of the connection) and another pair of streams. It uses its InputStream (2) to read from the network, and its OutputStream (3) to write the same data back across the network to your client, which reads it again via its InputStream (4) completing the round trip.

      Client                                                     Server

1. OutputStream -->\                                     /--> 2. InputStream -->
                    Socket <--> network <--> ServerSocket                       |
4. InputStream  <--/                                     \<--3. OutputStream <--

Updated: in reply to comment:

Note that the streams and sockets just send raw bytes; they have no notion of a "message" at this level of abstraction. So if you send X bytes and another X bytes, then read X bytes and read another X bytes, then your system behaves as if there are two messages, because that's how you've divided up the bytes.

If you send X bytes, and another X bytes, then read a reply of length 2X, then you might be able to read a single combined "message", but as you've noticed, the underlying implementation of the streams can choose when to deliver chunks of bytes, so it might return X bytes, then X bytes, later, or 2X at once, or 0.5X four times...

Estradiol answered 3/10, 2012 at 19:49 Comment(9)
yeah. I get this point. But I still cannot get in this point : if in Client, I use OutputStrem twice (send data to server two times), after that I use InputStream to read again (and two times too). I think in your map above, I just can receive one string (the latest) rather than receive both two string.Bart
why server can do that. In server code above, just have this line : sockOutput.write(buf, 0, bytes_read);. Please explain for me, thanks :)Bart
It depends how you read and write. You are reading (and writing) a fixed amount of data (test.length() bytes on the client side, and 1024 bytes on the server side), which may or may not chop up your data into two messages. If you write two messages of length X, then read a reply of length 2X, then you could read a single combined message.Estradiol
I think 1024 just the initialize condition for byte array. after that, it just write data equals size with bytes_read. Moreover, i think when using inputstream second times, I should receive null.Bart
OK, I see - but see the documentation for InputStream - it is up to the underlying implementation when to give you some data, which in your case is probably when a network packet arrives (your first write())...Estradiol
How can we get who wrote to the socketoutputstream?Lignocellulose
@Estradiol from what I understood as an newbie in java is, that outputstream will send something(data) to the current socket which it in turn will send it to the server present across the network and the programmer does not have to focus on the internal details of how it is sent to the server by the socket. And inputstream is used to get the details of what server has already sent to our socket and we then use inputstream to get this data from our socket. Please correct me if I've gone wrong !!Karlenekarlens
That's basically right, although mostly InputStream doesn't provide details of the data, separately from actually getting the data. You just have to attempt to read() and see what you receive!Estradiol
Thanks, the diagram makes the socket pattern simple to understand. Often times code examples themselves, not so muchNelson
D
5

InputStream and OutputStream are two completely separate streams. What you write into one has no a priori relation to what you read from the other. The InputStream gives you whatever data the server decides to send to you. I would also like to comment on this piece of your code:

sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());

You use the length of a string test (not shown in your code), which has nothing to do with the byte array you are passing as the first argument. This can cause an ArrayIndexOutOfBoundsException or truncation of your intended message.

Additional comments to your updated question

Reviewing your server-side code, it is not quite correctly written. You need to have try { handleConnection(...); } finally { socket.close(); } to ensure proper cleanup after an error, as well as when completing normally. Your code never closes anything on the server side.

Finally, and most critically, your entire code is written in a way that can result in a deadlock. Normally you need a separate thread to read and to write; otherwise the following may happen:

  1. You attempt to write some data to the output;
  2. The server reads it and tries to respond with data in your input;
  3. But, since the buffers are too small, you don't manage to send everything because the server wants to first send something to you, then receive the rest; but you don't get to the receiving part before you have sent everything you've got.
Dree answered 3/10, 2012 at 19:25 Comment(3)
So, what we get is depend on Server side, right ? I still cannot get, why after I write two times, I can get again two msg respectively. Please see my server side code. thanks :)Bart
Why we should close socket in HanldeConnection() method ? Because at waitforConnection(), I have already closed it. Does this way wrong ? Please tell me. Thanks :)Bart
The rule is to always close in try-finally. The way you're doing it, there's no guarantee that sock.close() will be reached because you catch only Exception and not all Throwables. Even then the approach is wrong from another standpoint, and that is that you are catching the Excepton too early and in realistic code it would be the wrong place to handle it. It is tolerable for learning code, I guess.Dree

© 2022 - 2024 — McMap. All rights reserved.