4

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?

KDecker
  • 6,928
  • 8
  • 40
  • 81
  • why are you asking this? – tbodt Jan 18 '14 at 03:09
  • 2
    Effectively your question is "How does the TCP stack work, starting at the top level in Java?". This is far too broad of a question and covered extensively in books on the subject. – Brian Roach Jan 18 '14 at 03:11
  • You can debug and inspect any object in an IDE. If that fails or is not clear you can intercept traffic with Wireshark and look at the packets. This is a very deep question - too many variables involved. I would just look at the source code of Socket to see what's going on. – yǝsʞǝla Jan 18 '14 at 03:15

4 Answers4

3

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.

Community
  • 1
  • 1
miraculixx
  • 10,034
  • 2
  • 41
  • 60
  • So, it will stay in the "local" socket's buffer if the buffer still has some room? Then depending on that, `read` call on the remote side may take more or less time? – stdout Mar 29 '20 at 19:40
  • 1
    @stdout no, network transmission works differently. In general there is a sender and a receiver. The sender just sends the data whenever it is ready (in most cases: immediately). The receiver waits until it receives the data and puts it into its local buffer. The blocking read call on the socket in the receiver waits until data is available in the local buffer. In principle the sender could be shutdown by the time this happens. That is the read call in the receiver does not have any effect on the sender and it is not made slower or faster by the sender's activity. – miraculixx Mar 31 '20 at 07:50
  • 1
    @stdout re the buffers - these exist because transmitting data is slow relatively to the CPU. Therefore the CPU transfers data into a buffer and instructs the network device to transmit the data from there. Meanwhile the CPU goes on to execute other code. The same happens on the receiving end. The network device receives data and writes it into a local buffer while the CPU executes other code. Once the data has been received the program is signalled so it can read the data from the buffer. – miraculixx Mar 31 '20 at 07:53
2

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 ...)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

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.

Weibo Li
  • 3,565
  • 3
  • 24
  • 36
2

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

waTeim
  • 9,095
  • 2
  • 37
  • 40
  • why this behaviour is different in Windows OS, then – UVM Jan 18 '14 at 03:41
  • @UVM My guess is that on Windows OS the behavior will in fact be similar since neither Linux nor Windows can dedicate unlimited kernel buffer space to a process writing to a socket, but I don't have direct experience with it. Maybe on Windows it will return immediately with an error? But then there's [this thread](http://stackoverflow.com/questions/1353118/how-to-set-sockets-to-blocking-mode-in-windows) – waTeim Jan 18 '14 at 04:27
  • I think the 'blocking behavior' particularly, is same in all OS.please correct if I am wrong – UVM Jan 18 '14 at 04:39
  • Note that there are multiple buffers -- the buffer in question is the PrintWriter's buffer. The socket buffer IMHO cannot be directly controlled from within Java as its the OS that handles it. – miraculixx Jan 18 '14 at 05:47
  • The Question is about the behaviour of `PrintWriter` and socket streams **in Java**. You can't generalize from how a typical C program is implemented / behaves. (If you are going to answer via the native behaviour, you need to consider the *actual* native implementation of the JVM.) – Stephen C Jan 18 '14 at 06:35