This article is mainly from when I write Java network programming in the use of Bufferedinputstream and Bufferedoutputstream when the bug encountered, To analyze the working mechanism of Bufferedinputstream and bufferedoutputstream and simple source analysis. 1. Bug Description
Recently, in writing a Java Network programming program, which involves the transfer of files, choose to use Bufferedinputstream and bufferedoutputstream to read data as a network traffic way.
Large files (hundreds of trillion or even g) are not problematic when testing, but accidental testing of small files (more than 10 B) always fails.
Large file transfer success, small file transfer is not successful, this shows that it is not a logical problem of the code, but should be a matter of detail.
After a careful consideration, to see the source of Bufferedoutputstream and Bufferedinputstream, finally found that the problem of the buffer zone .
After reading the blog, the little Buddy said that only describing Bugs was not a big reference for a stranger. So here I post my service-side (sender) and client (receiver) core code for read and write data in "bug description" for your reference.
Because the code logic is more cumbersome, and the method involved in the call is more, so the part of the code to make a general explanation.
Part of the code's error excerpt:
Sender.java Code Excerpt
The public void Sendsection () throws IOException {//Recieversectioninfo is a pojo that stores the basic information for a fragmented file for (Recie Versectioninfo sectioninfo:sectioninfolist) {//Send a header (with the basic information of the fragment) String Sectioni before sending the official content of the fragmented file
Nfostr = Packageutil.packagesectioninfo (Sectioninfo);
byte[] Sendheader = Packageutil.addheader (SECTIONINFOSTR);
Recieveoutputstream.write (Sendheader);
Read file contents using random read-write stream of files String fileName = Sendpath + sectioninfo.gettargetfilename ();
Randomaccessfile randomaccessfile = new Randomaccessfile (fileName, "RW");
Randomaccessfile.seek (Sectioninfo.getoffset ());
Transmit fragmented file content long Overlen = Sectioninfo.getsectionlen ();
while (Overlen > 0) {int size = Overlen > buffersize? buffersize: (int) Overlen;
int temp = randomaccessfile.read (buffer, 0, size);
Recieveoutputstream.write (buffer, 0, size); The following code is the key to get rid of the bug, add this sentence, the code has been changed, the specific reasons please see "bug solution"//Recieveoutputstream.flush ();
Overlen-= size; }
}
}
Receiver.java Code Excerpt
public void Receivesection (Recieversectioninfo sectioninfo) throws IOException {
String FilePath = TargetPath + secti Oninfo.gettempfilename ();
Randomaccessfile file = new Randomaccessfile (FilePath, "RW");
Start receiving file
int havelen = 0;
while (Havelen!= Sectioninfo.getsectionlen ()) {
//transmission of small files, this code throws an exception, the temp return value is-1
int temp = Inputstream.read ( Buffer, 0, buffersize);
File.write (buffer, 0, temp);
Once each reading succeeds, use observation mode to inform view once
sendonreceiving (temp);
Havelen + temp;
}
File.close ();
}
2. Bug Resolution
after every time I use bufferedoutput write () output data, I invoke the Flush () method to refresh the buffer so that the bug is changed.
So why can this be successful, this should be from the Bufferedoutputstream and bufferedinputstream work mechanism and source level explanation. the working mechanism of Bufferedinputstram and Bufferedoutputstream
Bufferedinputstram and Bufferedoutputstream are input and output streams with buffers. (1) Bufferedoutputstream
for Bufferedoutputstream, it does not immediately write data to a kernel-state buffer while it is writing the stream's data, but first writes the data to its own buffer (an array of bytes: byte[] buf). The data in its own buffer is written to the kernel-state buffer only when the array is full. about this, we are in the source to verify:
Let's take a look at Bufferedoutput's properties and construction methods:
protected byte buf[]; Bufferedoutputstream buffer
protected int count; The number of valid bytes
//Bufferedoutputstream for the buffer is the wrapper flow to the OutputStream (below which also wraps a layer of filteroutputstream) public
Bufferedoutputstream (OutputStream out) {This
(out, 8192);
}
The size is the bufferedoutputstream of the specified buffer buf
(outputstream out, int size) {
super;
if (size <= 0) {
throw new IllegalArgumentException ("Buffer size <= 0");
}
BUF = new Byte[size];
}
The following are the two write () methods in Bufferedoutputstream:
The first write () method
is public synchronized void write (byte b[], int off, int len) throws IOException {
if (len >= buf. Length) {
//////////Only when the number of bytes currently required for output is greater than the size of the buffer buf, the buffer is flushed///and an out
(outputstream instance is used, OutputStream is an output stream without buffering, and its write The () method is to directly write the data into the kernel-state buffer for data output
flushbuffer ();
Out.write (b, off, Len);
return;
}
If the data that is currently required to be written is greater than the remaining writable size of the buffer, the buffer is emptied before the data is written into the buffer
if (len > Buf.length-count) {
flushbuffer ();
}
System.arraycopy (b, off, buf, Count, Len);
Count = Len;
}
As we already know, Bufferedoutputstream provides a way to empty the buffer at the User Level flush (), and I have changed my bug by calling this method. So let's take a look at the source code for this method:
User-level Flush () method
//Call Internal emptying buffer method public
synchronized void Flush () throws IOException {
flushbuffer ();
The meaning of this code is also to empty the outputstream buffer,
//But essentially it did nothing! because the OutputStream flush () method is empty ~
Out.flush ();
}
Flushbuffer () method//Flushbuffer () for Bufferedoutputstream internal calls (
): Use the Write () of the OutputStream object as long as there is data in the current buffer array Method is written into a kernel-state buffer,
//Then the count value of the valid array for the record buffer is cleared 0
private void Flushbuffer () throws IOException {
if (Count > 0) { C12/>out.write (buf, 0, Count);
Count = 0;
}
}
(2) Bufferedinputstream
Bufferedinputstream's logic is more complex than Bufferedoutputstream's, and it provides more user-level methods than Bufferedoutputstream, so its source code is more complex ( I listed above the code is already the bufferedoutputstream of the entire source, so here I only say the working principle of bufferedinputstream, no source analysis.
like Bufferedoutputstream, Bufferedinputstream also has a buffer array.
when reading data from the input stream using Bufferedinputstream read (), Bufferedinputstream first reads the data in the input stream into the buffer array, and we call the Read () Method essentially reads data from a buffer. When the data for the buffer is read, Bufferediputstream reads part of the data from the input stream (the kernel buffer) into its own buffer array.
Doing so can effectively improve IO read-write efficiency because it is faster to read data from memory than to read from the hard disk. But the size of the buffer array is also limited, because the capacity of memory is always limited, it is impossible to read all the data into memory, and it is time-consuming to read large amounts of data into memory at once.
A more important approach to Bufferedinputstream's source code is fill (), which defines how to read data from a kernel-state buffer, and interested partners can look at this method.
Finally, attach a blog about the working mechanism of Bufferedinputstream and the source Code analysis: Bufferedinputstream (buffered input stream) 's cognition, source code and example