0

The code below is part of a larger attempt; it tries to transfer a file's details between server and client and fails miserably. Server writes 3 elements to socket, client should receive same 3 elements. Client blocks after reading first element. What am I doing wrong?!?

public class SocketIssues {

    static void client() throws Exception {

        new Thread() {

            @Override
            public void run() {

                try {
                    Thread.sleep(1000); // enough time for server to open its socket
                    Socket s = new Socket("localhost", 50001);

                    final DataInputStream dis = new DataInputStream(s.getInputStream());
                    final BufferedReader in = new BufferedReader(new InputStreamReader(dis));
                    System.out.println("CLIENT STARTED");
                    System.out.println("Operation: " + in.readLine());
                    System.out.println("Length: " + dis.readLong());
                    System.out.println("Name: " + dis.readUTF());
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

        }.start();

    }

    static void server() throws Exception {

        ServerSocket ss = new ServerSocket(50001);
        Socket s = ss.accept();
        System.out.println("SERVER: client connected");
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(dos));
        long l = 2194;
        String nume = "The file name";

        out.write("FILE1" + System.lineSeparator());
        out.flush();
        dos.writeLong(l);
        dos.flush();
        dos.writeUTF(nume);
        dos.flush();
        System.out.println("SERVER: done sending" + System.lineSeparator());
    }

    public static void main(String[] args) throws Exception {
        client();
        server();

    }
}

1 Answers1

1

Try sticking with just the DataOutputStream instead of mixing them up between data and a buffered writer and use the read/write UTF() method like you're already doing with the last object:

    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
    long l = 2194;
    String nume = "The file name";

    dos.writeUTF("FILE1");
    dos.flush();
    dos.writeLong(l);
    dos.flush();
    dos.writeUTF(nume);
    dos.flush();
    System.out.println("SERVER: fisier trimis" + System.lineSeparator());

then in the client:

                final DataInputStream dis = new DataInputStream(s.getInputStream());
                System.out.println("CLIENT STARTED");
                System.out.println("Operation: " + dis.readUTF());
                System.out.println("Length: " + dis.readLong());
                System.out.println("Name: " + dis.readUTF());

Essentially, there's 2 layers of buffering going on. It's possible that when you call readLine() from the BufferedReader, it goes ahead and steals more bytes from the underlying stream because, well, that's what it's supposed to do. Then, when you go back to the DataInputStream and try to read an object, the preamble is gone (BufferedReader stole it), and it'll block waiting for it, eventhough there's bytes in the stream.

Jon Lin
  • 142,182
  • 29
  • 220
  • 220
  • Thank you Jon! But is there an explanation to this? Why does my client block after the first read? Should I avoid cascading streams like that entirely? After all, they have been designed to be cascaded... – user3043540 Nov 27 '13 at 21:40
  • @user3043540 See the explanation in my answer, I've edited it – Jon Lin Nov 27 '13 at 21:51
  • Thank you, that makes sense. I wrongly assumed that the BuferedReader only reads a line and stops. Ok, so if this is what happens, I shouldn't mix streams; but then how can I send various types of data (string, file contents, primitive values etc) over a single socket if I don't create some high-level streams to help me with that? – user3043540 Nov 27 '13 at 21:58
  • You have to use the `DataOutputStream` for everything. It supports primitives, `writeUTF(),` everything you need except arbitrary objects, and there is `ObjectOutputStream` for that. The moral is twofold: (a) don't use multiple kinds of streams/Readers/Writers on the same socket, and (b) don't mix binary and text protocols. – user207421 Nov 27 '13 at 22:00
  • @user3043540 DataOutputStream is [*made* to do stuff like that](http://docs.oracle.com/javase/6/docs/api/java/io/DataOutputStream.html), bytes[], chars, ints, Strings, etc. – Jon Lin Nov 27 '13 at 22:02
  • I'll keep that in mind, thanks! What I was trying to build was a little chat program which can also do file transfers. I thought I could send messages as text using readers/writers, and files as sequences of bytes or as array objects. If I stick to Data*putStream, should I transfer all chat messages with writeUTF()? And what if I need to transfer objects in the future? – user3043540 Nov 27 '13 at 22:09
  • @user3043540 See: [ObjectOutputStream](http://docs.oracle.com/javase/6/docs/api/java/io/ObjectOutputStream.html). If this answers your question, you should click on that green checkmark so that your question shows up as "resolved". – Jon Lin Nov 27 '13 at 22:12
  • Great idea, I totally forgot that ObjectOutputStream can also write primitives and Strings! Thanks. I'll try that and see if it solves the problem. – user3043540 Nov 27 '13 at 22:18