KDecker
KDecker

Reputation: 7128

Where does the data go when writing to a socket in java?

Say I have the following code which sets up a Socket and a in and out stream for reading and writing to the socket.

toMonitor = new Socket(sd.m_sMonName, sd.m_iMonPort);
out = new PrintWriter(toMonitor.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(toMonitor.getInputStream()));

Also say I have the method below called SendIt, which writes a String to the Sockets outputstream

public void SendIt (String message) throws IOException 
{
    try 
    {        
        newStatus("MessageParser [SendIt]: sent:\n\t" + message, true);

        out.println(message);
        if (out.checkError() == true) 
            throw (new IOException());
        out.flush();
        if (out.checkError() == true) 
            throw (new IOException());
    } 
    catch (IOException e) {} //Bubble the Exception upwards
}

When I call out.println(message); above, will that message sit in the buffer until the other end of the socket reads it? - I take it the underlying writer object will break it into bytes, send it to the Sockets output stream where it is assembled into packets and sent out, thus clearing the Buffer as it is written?

Will it sit there until I call out.flush()?

Does out.flush() ALWAYS clear the buffer, or could something stay in there, somehow..?

EDIT:

I am currently using Netbeans as my IDE, is there are way in which I could watch the actual value of the output buffer in real time as the program runs?

Upvotes: 4

Views: 1344

Answers (4)

Weibo Li
Weibo Li

Reputation: 3605

According to your code:

out = new PrintWriter(toMonitor.getOutputStream(), true);

calls the PrintWriter constructor:

//PrintWriter.java. Source of jdk6.
public PrintWriter(OutputStream out, boolean autoFlush) {
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);

// save print stream for error propagation
if (out instanceof java.io.PrintStream) { 
    psOut = (PrintStream) out;
}
}

When you call out.println(message);, it eventually calls:

//PrintWriter.java. Source of jdk6.
public void write(String s, int off, int len) {
try {
    synchronized (lock) {
    ensureOpen();
    out.write(s, off, len);
    }
}
catch (InterruptedIOException x) {
    Thread.currentThread().interrupt();
}
catch (IOException x) {
    trouble = true;
}
}

In your PrintWriter instance, out filed is a BufferedWriter instance. So out.write(s, off, len) actually calls:

//BufferedWriter.java. Source of jdk6.
public void write(String s, int off, int len) throws IOException {
synchronized (lock) {
    ensureOpen();

    int b = off, t = off + len;
    while (b < t) {
    int d = min(nChars - nextChar, t - b);
    s.getChars(b, b + d, cb, nextChar);
    b += d;
    nextChar += d;
    if (nextChar >= nChars)
        flushBuffer();
    }
}
}

According to the code above, if the char buffer in BufferedWriter is fully filled(if (nextChar >= nChars)), it is flushed automatically with flushBuffer(). Otherwise, write method won't flush the buffer.

And when you call out.flush() manually, the call stack also goes to BufferedWriter.flush(). The code is:

//BufferedWriter.java. Source of jdk6.
public void flush() throws IOException {
    synchronized (lock) {
        flushBuffer();
        out.flush();
    }
}

It flushes the buffer in it, and also flush the encapsulated output stream.

BTW, The code of flushBuffer is:

//BufferedWriter.java. Source of jdk6.
void flushBuffer() throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (nextChar == 0)
        return;
        out.write(cb, 0, nextChar);
        nextChar = 0;
    }
}

In conclusion:

  1. It won't sit there until you call out.flush(). If the buffer is full, it flushes the buffer automatically.
  2. out.flush() will ALWAYS flush the buffer in BufferedWriter unless the buffer is empty.

Your questions are answered above. The following is for further discussion.

But in some other cases(not your code), if the BufferedWriter(named out1 e.g.) instance encapsulates another BufferedWriter(named out2 e.g.) instance, flushBuffer() only means char will be taken from out1's buffer and writer into out2's buffer. It doesn't guarantee the chars go to the other endpoint of the stream.

Upvotes: 2

waTeim
waTeim

Reputation: 9235

If the VM is running on a unix like OS then yes, the java socket is going to equate to a unix system socket, and if the side that is consuming the data falls too far behind the side that is generating the data, then the sending side's send buffer will fill up and the sender will block until the receiver reads more data.

I'm pretty such this will be true for any stream (e.g. TCP/pipe) socket and not true for any datagram (e.g. UDP) socket.

You can verify this if you like for unix pipes by trying these C programs

Sender

#include <stdio.h>
#include <unistd.h>
int main(int argc,char *argv[])  
{
   char c;

   while(1)
   { 
       write(1,&c,1);
       putc('+',stderr);
   }  
}

Receiver 1

#include <unistd.h>
int main(int argc,char *argv[])
{
   char c;

   while(1) read(0,&c,1);
}

Receiver 2

#include <unistd.h>
int main(int argc,char *argv[])
{
   sleep(1000);
}

If you pipe the output of sender to receiver1, the command will run forever filling your terminal with '+''s but if you pipe the output of sender to receiver2, it will send data until the send buffer fills up and then block forever.

Check out SocketOptions to see how you can change the send buffer and receive buffer sizes.

You might find this useful to monitor unix-domain sockets on Linux

Upvotes: 2

Stephen C
Stephen C

Reputation: 718826

When I call out.println(message); above, will that message sit in the buffer until the other end of the socket reads it?

No. What the "other end of the socket" does has no effect on whether the message is sent or no.

Will it sit there until I call out.flush()?

It depends how you write.

You have instantiated out as a PrintWriter with auto-flushing enabled. According to the javadoc, that means that println will automatically flush the output. But if you had used print then auto-flushing is not triggered.

There is also the issue that if you write enough characters to the PrintWriter (even one at a time) you will fill its internal buffer and that will trigger a flush.

Does out.flush() ALWAYS clear the buffer, or could something stay in there, somehow..?

It pushes any buffered characters / bytes to the local OS network protocol stack. But if there are networking problems, the bytes may not make it to "the other end". They might be "stuck" in the local network stack buffers. (But this is unusual ...)

Upvotes: 2

miraculixx
miraculixx

Reputation: 10349

When I call out.println(message); above, will that message sit in the buffer until the other end of the socket reads it?

No, it will go to your network interface which transmits it to the remote node as soon as the buffer is full or you flush the io writer/stream. If there is no connection to the remote node, the socket is closed and will not generally accept writes and throw an exception (as long as it knows)

Will it sit there until I call out.flush()?

It may or may not. Note that the buffer that you flush here is the PrintWriter's buffer, not the network socket's buffer. Hence, out.flush() does not as such guarantee transmission, although that is what usually happens next given the connection is still ok. As the Java reference states:

(...) flushing the stream guarantees only that bytes previously written to the stream are passed to the operating system for writing; it does not guarantee that they are actually written to a physical device such as a disk drive.

Finally, your last question:

Does out.flush() ALWAYS clear the buffer, or could something stay in there, somehow..?

No, nothing will stay in any of your io writer or stream buffers, again according to the reference:

one flush() invocation will flush all the buffers in a chain of Writers and OutputStreams.

Upvotes: 3

Related Questions