whomaniac
whomaniac

Reputation: 1298

Writing faster than reading from a socket

I did an experiment and I created a program with 2 threads: a server thread and a client thread. The server thread accepts a connection from the client, does a long process (emulated by sleep()), and prints the result.

The client on the other hand sends messages really fast.

See the code:

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

public class SocketTest {

    public static void main(String[] args) throws InterruptedException {
        Thread serverThread = new Thread(() -> server());
        Thread clientThread = new Thread(() -> client());

        serverThread.start();
        clientThread.start();

        serverThread.join();
        clientThread.join();
    }

    private static void server() {
        try (
            ServerSocket listener = new ServerSocket( 1234 );
            Socket client = listener.accept()){  // wait for connection

            while (true) {
                InputStream in = client.getInputStream();

                // read a newline or carriage-return-delimited string
                BufferedReader bin =
                        new BufferedReader( new InputStreamReader( in ) );
                String someString = bin.readLine();
                //process
                Thread.sleep(1000);
                System.out.println(someString);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void client () {
        try (Socket server = new Socket(InetAddress.getLocalHost(), 1234))
        {
            while (true) {
                OutputStream out = server.getOutputStream();

                // write a newline or carriage return delimited string
                PrintWriter pout = new PrintWriter(out, true);
                pout.println("Hello!");

                // send the the string
                pout.flush();
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

When I ran the program I got the following output:

Hello!
Hello!
llo!
o!

ello!
lo!
!
Hello!
llo!
o!

ello!
lo!
!
Hello!
llo!
o!

ello!
lo!
!
Hello!
llo!
o!

ello!
...

Now I am not sure I understand it 100%... But I did run the experiment of using an input the size of a power of 2 (including /n) and the output was not cut off.

Can someone explain to me:

  1. What's going on internally?
  2. Why no error is thrown?
  3. What would you do to overcome this problem?

Thanks!!

Upvotes: 0

Views: 587

Answers (1)

Steffen Ullrich
Steffen Ullrich

Reputation: 123260

private static void server() {
    ...
        while (true) {
            InputStream in = client.getInputStream();

            // read a newline or carriage-return-delimited string
            BufferedReader bin =
                    new BufferedReader( new InputStreamReader( in ) );
            String someString = bin.readLine();

This creates a new BufferedReader for each run of while loop. But readline will not only read a line from the socket. It will instead read a larger amount of data, check if there is a line end in it, read more if no line end yet etc and finally return the line. The rest of the read data is kept in the BufferedReader. By implicitly abandoning the BufferedReader at the end of loop run and creating a new one at the beginning of a new loop run you abandon all data in the buffer which were already read from the socket.

Instead you should create your BufferedReader outside the loop and use it there so that no data gets abandoned:

private static void server() {
    ...
        InputStream in = client.getInputStream();
        BufferedReader bin =
                new BufferedReader( new InputStreamReader( in ) );

        while (true) {
            // read a newline or carriage-return-delimited string
            String someString = bin.readLine();

Upvotes: 1

Related Questions