Because there is not a large number of connections to the small Web server requirements, not to use the heavy-weight server, so you write a server.
It also provides a simple web framework. It's easy to use.
The broad requirements include
- Able to handle HTTP protocol.
- The ability to provide an interface for users to write their own services.
Some of the code that temporarily affects the view is omitted. is not perfect enough for documenting problems and solutions, and may be modified in many places.
Let's get Started ~
Project's address: Github
Starting from ServerSocket
Point here is the complete code of this section, can be compared to observe
Everyone knows that the HTTP protocol is using a TCP service. and to use TCP to communicate all have to start from ServerSocket. ServerSocket listen to the specified IP address after the specified port, the other end can be connected by the ServerSocket to establish a one-off socket to send and receive data.
We first get the IP address and port number to listen from the command line parameters, and of course not use the default.
1 Public Static voidMain (string[] args) {2 ...3inetaddress IP =NULL;4 intPort;5 if(Args.length = = 2 && args[1].matches (". +:\\d+"))) {6 ...7ip = inetaddress.getbyname (address[0]);8 ...9}Else {Ten ... OneIP =inetaddress.getlocalhost (); A ... -Port = 8080; -System.out.println ("No address and port specified, use default IP and port ..." + ip.gethostaddress () + ":" +port); the } - -Server Server =NewServer (IP, port); - Server.start (); +}
Input is start 123.45.67.89:8080
either a direct or astart
InetAddress.getByName(address[0])
Constructs a InetAddress object from a string of IP addresses.
InetAddress.getLocalHost()
Gets the InetAddress object for localhost.
Next look at the server class.
First of all, this server should be considerate of the configuration of the PC, it is not appropriate to create too many threads. Consider using NIO for IO processing, one thread processing IO. So we need a selector to select the pipeline that is ready and use a thread pool to handle the task. (You can use to Runtime.getRuntime().availableProcessors()
get the number of processor cores available.) )
The server starts with ServerSocket bindings and other initialization work.
1 Serversocketchannel Serverchannel; 2 registerservices (); 3 Serverchannel = Serversocketchannel.open (); 4 Serverchannel.bind (new inetsocketaddress (this. Port)); 5 Serverchannel.configureblocking (false); 6 selector = Selector.open (); 7 Serverchannel.register (selector, selectionkey.op_accept);
registerServices()
Temporarily ignored, is used to register user-written services.
Because it is NIO, here is the Serversocketchannel, bound to IP and port, set up non-blocking, register the Accept event. You cannot use Selectior without setting a non-blocking state.
And then start looping and handling events.
1 Public voidstart () {2 init ();3 while(true) {4 ...5 Selector.select ();6 ...7Set<selectionkey> Readykeys =Selector.selectedkeys ();8Iterator<selectionkey> Iterator =readykeys.iterator ();9 while(Iterator.hasnext ()) {TenSelectionkey key =Iterator.next (); One Iterator.remove (); A if(Key.isacceptable ()) { -Serversocketchannel ServerSocket =(Serversocketchannel) Key.channel (); -...//Handling Accept Events the}Else if(Key.isreadable ()) { -Socketchannel client =(Socketchannel) Key.channel (); -...//Handling Read Events -}Else if(Key.iswritable ()) { +Socketchannel client =(Socketchannel) Key.channel (); -...//Handling Write Events + } A ... at } - } -}
slector.select()
will block until a registered event arrives. After getting a selectionkey you need to use iterator.next()
it to remove it from the Selectedkeys, or you selector.select()
will still get the key next time.
Each event is analyzed below.
Accept Event
The Accept event is actually very simple, it is possible to come up with a socket to establish a connection. So, like the following, after the accept creates a connection, the Socketchannel listens to the read event and reads it when there is data to read.
1 if (Key.isacceptable ()) {2 Serversocketchannel ServerSocket = (Serversocketchannel) Key.channel (); 3 Socketchannel client = serversocket.accept (); 4 Client.configureblocking (false); 5 client.register (selector, selectionkey.op_read); 6 }
Read Event
This event will allow the HTTP request to be received. After the data is read to the Controller
asynchronous HTTP request resolution, forwarded to the service processing class according to FilePath. After processing, the channel is registered for write listening. client.register(selector, SelectionKey.OP_WRITE)
.
And let key carry Response
the object (will be written in subsequent chapters)
1 if (Key.isreadable ()) {2 Socketchannel client = (Socketchannel) Key.channel (); 3 Bytebuffer buffer = bytebuffer.allocate (4096); 4 client.read (buffer); 5 Executor.execute (new Controller (buffer, client, selector)); 6 }
The problem here is that you don't know how to handle a big request.
Write Event
This event writes response to Socketchannel.
1 if(Key.iswritable ()) {2Socketchannel client =(Socketchannel) Key.channel ();3Response Response =(Response) key.attachment ();4 if(Response = =NULL) {5 Continue;6 }7Bytebuffer Bytebuffer =Response.getbytebuffer ();8 if(Bytebuffer.hasremaining ()) {9 Client.write (bytebuffer);Ten } One if(!bytebuffer.hasremaining ()) { A Key.cancel (); - client.close (); - } the}
If you find any problem or have any suggestions please advise. Thank you ~
Appendix Area:
[1] When the message body appears in the message, the transmission length (transfer-length) of a message is the message body (messagebody)
That is, after the entity body has been applied the transfer encoding (transfer-coding). When the message appears
Message body, the transmission length (transfer-length) of the message body is determined by the following (in order of precedence):
- Any message that cannot contain the message body (message-body), such as 1xx,204 and 304 responses and any
The first empty line (CRLF) after the Beatles domain is always terminated, regardless of whether the message exists
Entity header field (Entity-header fields).
- If the Transfer-encoding header field (see Section 14.41) appears, and its domain value is non-"dentity" transfer encoding
Value, the transmission length (transfer-length) is defined by the "block" (chunked) transfer encoding unless the message is passed
Ends when the connection is closed.
- If the Content-length header field (which belongs to the Entity header field) appears (see section 14.13), then its decimal value (in
Represents the length of the entity body (entity-length, the length of the entity is actually the length of the entity body,
Later, the entity-length is translated into the length of the entity body) and represents the transmission length (transfer-length). content-
The Length header field cannot be included in the message if the entity body length (entity-length) and the transmission length (transferlength)
The two are not equal (that is, the Transfer-encodind header field appears). If a message is present in the transfer translation
Code (transfer-encoding) header fields and also Content-length header fields, which are ignored.
- If the message is used to the media type "multipart/byteranges" and the transmission length (transfer-length) is also not
has been specified, then this self-bounding media type defines the transmission length (transfer-length). This media type cannot be
be exploited unless the sender knows how the receiver can parse it; If a range header appears in the HTTP1.1 client request
And with multiple byte range (byte-range) indicators, which means that the client can parse the Multipart/byteranges
Response.
A Range Request header field may be a HTTP1.0 proxy that does not understand multipart/byteranges
Forward again; In this case, the server must be able to use the method defined in this section of 1,3 or 5 to define this message.
- Closing the connection through the server determines the transmission length of the message. (The request side cannot indicate the request message body by closing the connection
, as this will give the server no chance to continue responding).
In order to be compatible with the http/1.0 application, a request containing the http/1.1 message body must include a valid content length
(content-length) header domain, unless the server is http/1.1-compliant. If a request consists of a message body
And does not give the content length (content-length), then the server should not judge the length of the message should be
400 response (Bad request), or 411 response (required length) if it insists on wanting to receive a valid content long
Degrees (content-length).
All http/1.1 applications that can receive entities must be able to accept the "chunked" Transmission Encoding (Section 3.6), so when
This mechanism can be used to process messages when the length of a message cannot be determined in advance.
Messages cannot include both the content-length (content-length) header domain and the non-identity transport encoding. If the message includes
A non-identity transfer encoding, the content length (content-length) header field must be ignored.
When the content-length (content-length) header field appears in a message with a message body (message-body),
Its domain value must exactly match the number of bytes in the message body. http/1.1 user agents when receiving a
Invalid length must be able to notify the user.
Use Java to write a Web server and frame with your bare hands < chapter I: NiO articles >