Write your own simple HTTP server in Java

Source: Internet
Author: User
Tags date now

HTTP is a big protocol. The full-function HTTP server must respond to resource requests and convert the URL to the Resource Name of the local system. Responds to various forms of HTTP requests (such as get and post ). Process nonexistent file requests, return various forms of status codes, parse MIME types, and so on. However, HTTP servers with many specific features do not need all of these features. For example, many websites only want to display the message "under construction. Obviously, Apache is useless for such websites. Such a website can use a single-task custom server. Java Network Library makes it easy to write such a single task server.

 

Custom servers are not only used for small websites. Large-traffic websites such as Yahoo also use custom servers, because compared with general-purpose servers, servers that only do one thing are usually much faster. It is easy to optimize servers for a specific purpose for a task. The results are often much more efficient than general servers that need to respond to many types of requests. For example, it is better to use a single server to process icons and images that are repeatedly used for multiple pages or large-traffic pages (and avoid carrying unnecessary cookies during requests, therefore, you can reduce the request/response data, reduce the download bandwidth, and increase the speed). The server reads all image files into the memory at startup and directly provides these files from Ram, instead of reading from the disk for each request. In addition, if you do not want to record these images separately in addition to page requests that contain these images, this independent server will avoid wasting time on log records.

 

This article briefly demonstrates three types of HTTP servers:

(1) Simple Single-File Server

(2) Redirect Server

(3) fully functional HTTP Server

Simple Single-File Server

Function of the server: the same file is always sent regardless of the request received. This server is named singlefilehttpserver. The file name, local port, and content encoding method are read from the command line. If the default port is used, it is assumed that the port number is 80. If the default encoding method is used, it is assumed to be ASCII.

Import Java. io. *; import java.net. serversocket; import java.net. socket; public class singlefilehttpserver extends thread {private byte [] content; private byte [] header; private int Port = 80; private singlefilehttpserver (string data, string encoding, string mimetype, int port) throws unsupportedencodingexception {This (data. getbytes (encoding), encoding, mimetype, Port);} public singlefilehttpserver (byte [] dat A, string encoding, string mimetype, int port) throws unsupportedencodingexception {This. content = data; this. port = port; string header = "HTTP/1.0 200 OK \ r \ n" + "server: onefile 1.0 \ r \ n" + "Content-Length:" + this. content. length + "\ r \ n" + "Content-Type:" + mimetype + "\ r \ n"; this. header = header. getbytes ("ASCII");} public void run () {try {serversocket Server = new serversocket (this. port); system. out. println ("accepting Connections on port "+ server. getlocalport (); system. out. println ("data to be sent:"); system. out. write (this. content); While (true) {socket connection = NULL; try {connection = server. accept (); outputstream out = new bufferedoutputstream (connection. getoutputstream (); inputstream in = new bufferedinputstream (connection. getinputstream (); stringbuffer request = new stringbuffer (); While (true) {int c = in. read (); If (C = '\ R' | C =' \ n' | C =-1) {break;} request. append (char) C);} // if HTTP/1.0 and later protocols are detected, a MIME header if (request. tostring (). indexof ("HTTP /")! =-1) {out. write (this. header);} Out. write (this. content); out. flush ();} catch (ioexception e) {// todo: handle exception} finally {If (connection! = NULL) {connection. close () ;}}} catch (ioexception e) {system. err. println ("cocould not start server. port occupied ") ;}} public static void main (string [] ARGs) {try {string contenttype =" text/plain "; if (ARGs [0]. endswith (". html ") | ARGs [0]. endswith (". htm ") {contenttype =" text/html ";} inputstream in = new fileinputstream (ARGs [0]); bytearrayoutputstream out = new bytearrayoutputstream (); int B; while (B = in. read ())! =-1) {out. write (B);} byte [] DATA = out. tobytearray (); // set the listening port int port; try {Port = integer. parseint (ARGs [1]); If (Port <1 | Port> 65535) {Port = 80 ;}} catch (exception e) {Port = 80 ;} string encoding = "ASCII"; if (ARGs. length> 2) {encoding = ARGs [2];} thread t = new singlefilehttpserver (data, encoding, contenttype, Port); T. start ();} catch (arrayindexoutofboundsexception e) {system. out. println ("Usage: Java singlefilehttpserver filename port encoding");} catch (exception e) {system. err. println (E); // todo: handle exception }}}

The singlefilehttpserver class is a subclass of thread. Its run () method processes inbound connections. This server may only provide small files and only support low-throughput Web websites. All the work required by the server for each connection is to check whether the client supports HTTP/1.0 and generate one or two smaller byte Arrays for the connection. Therefore, this may be sufficient. On the other hand, if you find that the client is rejected, you can use multiple threads. Many things depend on the size of the provided files, the number of peak connections expected per minute and the Java thread model on the host. For the complex write server of the program, using multithreading will have obvious benefits.

Run () method to create a serversocket on the specified port. Then it enters an infinite loop, constantly accepting connections and processing connections. When a socket is accepted, an inputstream reads the request from the client. It checks whether the first line contains the string HTTP. If the string is contained, the server assumes that the client understands HTTP/1.0 or a later version, so it sends a MIME header to the file and then sends data. If the client request does not contain the string HTTP, the server ignores the header and directly sends data. Finally, the server closes the connection and tries to accept the next connection.

The main () method only reads parameters from the command line. Read the file name to be provided from the first command line parameter. If no file is specified or the file cannot be opened, an error message is displayed and the program exits. If the file can be read, its content will be read into the byte array data. The content type of the file will be reasonably guessed and the results will be stored in the contenttype variable. Next, read the port number from the second command line parameter. If no port is specified or the second parameter is not an integer between 0 and 65535, port 80 is used. Read the encoding method from the third command line parameter (provided only ). Otherwise, the encoding method is assumed to be ASCII. Use these values to construct a singlefilehttpserver object and start running. This is the only possible interface.

The test result is as follows:

Run the command line to compile the code and set parameters:

TELNET ::

First, enable the telnet service (if not, Google it), and then test the port of the Host:

Result (the output content of the request is displayed ):

HTTP protocol test:

Documentation (this is the previous article-car animation documentation ):

Redirect Server

Implemented feature-redirects users from one web site to another. The following example reads the URL and port number from the command line. The server that opens the port number may be very fast, so multithreading is not required. Although the use of multithreading on a daily basis may bring some benefits, especially for websites with low network bandwidth and low throughput. This is mainly for demonstration, so the server has been made into multiple threads. For the sake of simplicity, a thread is enabled for each connection, rather than a thread pool. It may be easier to understand, but it is a waste of system resources and inefficient.

Import Java. io. bufferedinputstream; import Java. io. bufferedwriter; import Java. io. ioexception; import Java. io. inputstreamreader; import Java. io. outputstreamwriter; import Java. io. reader; import Java. io. writer; import java.net. bindexception; import java.net. serversocket; import java.net. socket; import Java. util. date; public class redirector implements runnable {private int port; private string newsite; Public re Director (string site, int port) {This. port = port; this. newsite = site ;}@ overridepublic void run () {try {serversocket Server = new serversocket (port); system. out. println ("redirecting connection on port" + server. getlocalport () + "to" + newsite); While (true) {try {Socket socket = server. accept (); thread = new redirectthread (socket); thread. start ();} catch (ioexception e) {// todo: handle exception} catch (Bi Ndexception e) {system. err. println ("cocould not start server. port occupied ");} catch (ioexception e) {system. err. println (e) ;}} class redirectthread extends thread {private socket connection; redirectthread (socket s) {This. connection = s;} public void run () {try {writer out = new bufferedwriter (New outputstreamwriter (connection. getoutputstream (), "ASCII"); reader in = new inputstreamreader (New bufferedinputstr EAM (connection. getinputstream (); stringbuffer request = new stringbuffer (80); While (true) {int c = in. read (); If (C = '\ T' | C =' \ n' | C =-1) {break;} request. append (char) C);} string get = request. tostring (); int firstspace = get. indexof (''); int secondspace = get. indexof ('', firstspace + 1); string thefile = get. substring (firstspace + 1, secondspace); If (get. indexof ("HTTP ")! =-1) {out. write ("HTTP/1.0 302 found \ r \ n"); date now = new date (); out. write ("Date:" + now + "\ r \ n"); out. write ("server: redirector 1.0 \ r \ n"); out. write ("Location:" + newsite + thefile + "\ r \ n"); out. write ("Content-Type: text/html \ r \ n"); out. flush ();} // not all browsers support redirection. // We need to generate an HTML file for all browsers to describe this line as out. write ("<HTML> 

HTTP test:

Listen to port 8010 and redirect to Baidu:

The main () method provides a very simple interface to read the URL of the new website (to redirect the link to this URL) and listen to the local port. It uses this information to construct an rredirector object. Then it uses the generated runnable object (redirector implements runnable) to generate a new thread and start it. If no port is specified, rredirector listens to port 80.

The run () method of redirectro binds the server socket to this port, displays a brief status message, enters an infinite loop, and listens for connections. Each time a connection is accepted, the returned socket object is used to construct a redirectthread. Then the redirectthread is started. The new thread completes further interaction with the client. The run () method of redirector is only waiting for the next inbound connection.

The run () method of redirectthread has completed a lot of work. It first links a writer to the output stream of the socket and a reader to the input stream of the socket. Both the input and output streams are buffered. Then run () to read the first line sent by the client. Although the client may send the entire MIME header, we ignore this. The first line contains all required information. This line may be like this:

GET/directory/filename.html HTTP/1.0

The first word may be post or put, or there may be no HTTP Version.

Returned output. The first line is displayed as follows:

HTTP/1.0 302 found

Is this an HTTP/1.0 response, telling the client to be redirected. The second line is the header of "Date:", indicating the current time of the server. This line is optional. The third line is the server name and version. This line is optional, but the spider program can use it to count and record the most popular Web servers. The next line is the header of "Location:", which is required for this server. It tells the client where to redirect. Finally, the standard "Content-Type:" header. The text/html type is sent here, which is only the HTML that the client will see. Finally, an empty row is sent to identify the end of the first data.

If the browser does not support redirection, the HTML Tag will be sent.

Fully functional HTTP Server

Here, we develop an HTTP server with complete functions and become jhttp. It provides a complete document tree, including images, applets, HTML files, and text files. It is very similar to singlefilehttpserver, but it focuses on GET requests. This server is still quite lightweight; after reading this code, we will discuss other features that may be desired to be added.

Because the server must provide large files in the file system for a network connection that may be slow, you need to change the mode. Here, the inbound connection is not processed in the main thread of execution, but in the pool. A requestprocessor class instance removes the connection from the pool and processes it.

import java.io.File;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import org.omg.CORBA.Request;public class JHTTP extends Thread {private File documentRootDirectory;private String indexFileName="index.html";private ServerSocket server;private int numThreads=50;public JHTTP(File documentRootDirectory,int port , String indexFileName)throws IOException {if (!documentRootDirectory.isDirectory()) {throw new IOException(documentRootDirectory+" does not exist as a directory ");}this.documentRootDirectory=documentRootDirectory;this.indexFileName=indexFileName;this.server=new ServerSocket(port);}private JHTTP(File documentRootDirectory, int port)throws IOException {this(documentRootDirectory, port, "index.html");}public void run(){for (int i = 0; i < numThreads; i++) {Thread t=new Thread(new RequestProcessor(documentRootDirectory, indexFileName));t.start();}System.out.println("Accepting connection on port "+server.getLocalPort());System.out.println("Document Root: "+documentRootDirectory);while (true) {try {Socket request=server.accept();RequestProcessor.processRequest(request);} catch (IOException e) {// TODO: handle exception}}}/** * @param args */public static void main(String[] args) {File docroot;try {docroot=new File(args[0]);} catch (ArrayIndexOutOfBoundsException e) {System.out.println("Usage: java JHTTP docroot port indexfile");return;}int port;try {port=Integer.parseInt(args[1]);if (port<0||port>65535) {port=80;}} catch (Exception e) {port=80;}try {JHTTP webserver=new JHTTP(docroot, port);webserver.start();} catch (IOException e) {System.out.println("Server could not start because of an "+e.getClass());System.out.println(e);}}}

The main () method of the jhttp class sets the document root directory according to args [0. The port is read from ARGs [1], or the default port 80 is used to construct a new jhttp thread and start it. This jhttp thread generates 50 requestprocessor threads to process requests. Each thread obtains inbound connection requests from the requestprocessor pool when available. The jhttp thread repeatedly accepts inbound connections and places them in the requestprocessor pool. Each connection is processed by the run () method of the requestprocessor class shown in the following example. This method will wait until a socket is obtained from the pool. Once the socket is obtained, the input and output streams are obtained and linked to the reader and writer. In addition to the processing of multiple file directories and paths, the subsequent processing involves other single file servers.

Import Java. io. bufferedinputstream; import Java. io. bufferedoutputstream; import Java. io. datainputstream; import Java. io. file; import Java. io. fileinputstream; import Java. io. ioexception; import Java. io. inputstreamreader; import Java. io. outputstream; import Java. io. outputstreamwriter; import Java. io. reader; import Java. io. writer; import java.net. socket; import Java. util. date; import Java. util. list; import Java. UTI L. using list; import Java. util. stringtokenizer; public class requestprocessor implements runnable {Private Static list pool = new jsonlist (); private file documentrootdirectory; private string indexfilename = "index.html"; Public requestprocessor (File documentrootdirectory, string indexfilename) {If (documentrootdirectory. isfile () {Throw new illegalargumentexception(registry.this.doc umentrootdirectory = Document Rootdirectory; try again this.doc umentrootdirectory = documentrootdirectory. getcanonicalfile ();} catch (ioexception e) {} if (indexfilename! = NULL) {This. indexfilename = indexfilename;} public static void processrequest (socket request) {synchronized (pool) {pool. add (pool. size (), request); pool. policyall () ;}@ overridepublic void run () {// Security Detection string root = documentrootdirectory. getpath (); While (true) {socket connection; synchronized (pool) {While (pool. isempty () {try {pool. wait ();} catch (interruptedexception e) {}} connection = (socket) pool. remove (0);} Try {string filename; string contenttype; outputstream raw = new bufferedoutputstream (connection. getoutputstream (); writer out = new outputstreamwriter (raw); reader in = new inputstreamreader (New bufferedinputstream (connection. getinputstream (), "ASCII"); stringbuffer request = new stringbuffer (80); While (true) {int c = in. read (); If (C = '\ T' | C =' \ n' | C =-1) {break;} request. append (char) C);} string get = request. tostring (); // record the log system. out. println (get); stringtokenizer ST = new stringtokenizer (get); string method = ST. nexttoken (); string version = ""; if (Method = "get") {filename = ST. nexttoken (); If (filename. endswith ("/") {filename + = indexfilename;} contenttype = guesscontenttypefromname (filename); If (St. hasmoretokens () {version = ST. nexttoken ();} file thefile = new file (documentrootdirectory, filename. substring (1, filename. length (); If (thefile. canread () & thefile. getCanonicalPath (). startswith (Root) {datainputstream FD = new datainputstream (New bufferedinputstream (New fileinputstream (thefile); byte [] thedata = new byte [(INT) thefile. length ()]; FCM. readfully (thedata); Sox. close (); If (version. startswith ("HTTP") {out. write ("HTTP/1.0 200 OK \ r \ n"); date now = new date (); out. write ("Date:" + now + "\ r \ n"); out. write ("server: jhttp 1.0 \ r \ n"); out. write ("Content-Length:" + thedata. length + "\ r \ n"); out. write ("Content-Type:" + contenttype + "\ r \ n"); out. flush ();} raw. write (thedata); raw. flush ();} else {If (version. startswith ("HTTP") {out. write ("HTTP/1.0 404 file not found \ r \ n"); date now = new date (); out. write ("Date:" + now + "\ r \ n"); out. write ("server: jhttp 1.0 \ r \ n"); out. write ("Content-Type: text/html \ r \ n"); out. flush ();} Out. write ("<HTML> \ r \ n"); out. write ("

Deficiency and improvement:

This server provides certain functions, but it is still very simple. You can also add the following features:

(1) Server Management Interface

(2) Support CGI programs and Java Servlet APIs

(3) Support for other request methods

(4) Common web log file formats

(5) support for multi-document root directories so that users can have their own websites

Finally, let's take a moment to consider how to optimize the server. If you really want to use jhttp to run a high-traffic website, you can also do something to speed up this server. The first and most important point is to use the instant Compiler (JIT), such as hotspot. JIT can increase the program performance by about one order of magnitude. The second thing is smart cache. Remember the received requests and store the data of the most frequently requested files in hashtable to store them in memory. Use a low-priority thread to update this cache.

-- Refer to self-Network Programming

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.