Socket input buffer device-InternalInputBuffer, socket buffer
The world of the Internet is very complex, and the process of transmitting information from one end to the other is also quite complex. There may be several hardware in the middle. In order to improve the sending and receiving efficiency, both the sending end and the receiving end will introduce the buffer, so the sockets at both ends have their own buffer. Of course, the introduction of this buffer also brings an uncertain delay, generally, the sender writes the message to the buffer before it is sent until the buffer is filled up. The receiver only reads messages that do not exceed the buffer size at a time.
Tomcat needs to read the client request data when processing client requests. It also needs a buffer for receiving byte streams, that is, the socket input buffer device, its primary responsibility is to provide a buffer mode to read byte streams from the socket, provide a buffer filling method, that is, to read byte into the buffer buf, and provide a method to parse http Request lines, the method for parsing the http Request header is provided, and the Request object Request is assembled according to the parsing result.
The working principle of the socket input buffer is not complex, as shown in. InternalInputBuffer contains the following variables: byte array buf, integer pos, integer lastValid, and integer end. Buf is the byte stream used to store the buffer. Its size is set by the program. In tomcat, the default value is 8*1024, that is, 8 K Bytes. pos indicates reading the pointer, the value at which the request is read. lastValid indicates that the data read from the underlying operating system is filled to the last position in the buf. end indicates the position at which the http Request Header ends in the buffer buf, it also indicates the start position of the style.
As shown in the preceding figure, the buffer buf is empty at the beginning, and several byte streams at the underlying layer of the socket operating system are read to the buf. Therefore, the status is shown in Table ②, the read byte stream fills the buf from the beginning to the end, and pos is 0, lastValid is the last position value after the read, and then reads several byte streams at the bottom of the operating system for the second time, the number of bytes read each time is unknown. The byte stream should be followed by the position specified by lastValid in ②, instead of starting from the beginning. In this case, pos and lastValid are assigned a new value according to the actual situation, if you read it again, the final state is ⑤, and an additional end variable indicates the request line of the http request message and the end position of the request header.
To better understand how to read and parse byte streams from the underlying layer, the following describes a simplified process. First, you need to provide a method to read byte streams, as shown below. inputStream indicates the input stream of the socket, through socket. getInputStream () is obtained. The read method is used to read byte streams, which indicates that the most data is read from the underlying layer (buf. length-lastValid), and fill these byte streams in the buf array. the starting position of the fill is buf [pos], and nRead indicates the number of bytes actually read. By performing operations on these variables, You can accurately operate the buffer device and return true if the buffer device is filled successfully.
Publicclass InternalInputBuffer {
Byte [] buf = newbyte [8*1024];
Int pos = 0;
Int lastValid = 0;
Public booleanfill (){
Int nRead = inputStream. read (buf, pos, buf. length-lastValid );
If (nRead> 0 ){
LastValid = pos + nRead;
}
Return (nRead> 0 );
}
}
With the padding method, a packet parsing operation is required. Due to the length impact, only the method and path of the request line are parsed as an example, you can perform similar operations for other resolutions. The format of the http request message. A request line has three values to be parsed: Request Method, request url, and Protocol version, separated by spaces and ended with a carriage return character. The resolution method is as follows:
Publicboolean parseRequestLine (){
Int start = 0;
Byte chr = 0;
Boolean space = false;
While (! Space ){
If (pos> = lastValid)
Fill ();
If (buf [pos] = (byte )''){
Space = true;
Byte [] methodB = new byte [pos-start];
System. arraycopy (buf, start, methodB, 0, pos-start );
String method = newString (methodB );
Request. setMethod (method );
}
Pos ++;
}
While (space ){
If (pos> = lastValid)
Fill ();
If (buf [pos] = (byte )''){
Pos ++;
} Else {
Space = false;
}
}
Start = pos;
While (! Space ){
If (pos> = lastValid)
Fill ();
If (buf [pos] = (byte )''){
Space = true;
Byte [] uriB = newbyte [pos-start];
System. arraycopy (buf, start, uriB, 0, pos-start );
String uri = new String (uriB );
Request. setUri (uri );
}
Pos ++;
}
Return true;
}
The first while loop is used to parse the method name. Before each operation, you must determine whether to read byte streams from the underlying layer. When the pos is greater than or equal to lastValid, you must call the fill method for reading, when a byte is equal to an ASCII space, the byte array between start and pos is intercepted. These are the byte composition of the method name, converted to a String object, and set it to the request object; the second while loop is used to skip all spaces between the method name and uri. The third while loop is used to parse the uri. Its logic is similar to that of the previous method name resolution, the parsed uri is finally set to the request object.
So far, the working principle of the buffer device has been clarified, A complete process is to read from the underlying byte stream to parse these byte streams and assemble them into a request object for later use by the program, because the byte stream read from the underlying layer cannot be accurately guaranteed each time, the pos and lastValid variables are controlled to complete the accurate reading and receiving of the byte stream. In addition, the input buffer device also provides a method to parse the request header. The processing logic is to parse the header according to the http protocol and then put it into the request object in sequence. In practice, tomcat does not parse the request line and request header parameters into String type and set them to request. Instead, it uses ASCII codes to store these values, because these ASCII code transcoding will cause performance problems, the idea is that transcoding is performed only when necessary. Many parameters are not used, so as to improve processing performance. The details are covered in the Request section.