一个简单的 HTTP 服务器

  cheney

    按照 HTTP 协议

    客户端发送:
    方法 - URI - 协议/版本
    请求头
    请求内容

    服务端返回:
    协议/版本 - 状态码 - 状态描述
    响应头
    响应内容

    可以通过 Socket, ServerSocket 编写一个简单的静态文件服务器:

    package me.gpio.tomcat;
    
    import java.io.*;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**
     * Created by  Cheney
     */
    public class SimpleHttpServer {
    
        public static void main(String[] args) {
            SimpleHttpServer simpleHttpServer = new SimpleHttpServer();
            simpleHttpServer.await();
        }
        // 静态文件根目录
        public static final String WEB_ROOT = System.getProperty("user.dir");
    
    
        // 关闭服务命令
        public static final String CMD_SHUTDOWN = "/shutdown";
    
        // 等待响应请求的函数
        public void await(){
            ServerSocket serverSocket = null;
            String host = "127.0.0.1";
            int port = 1234;
    
            try {
                // 第二个参数是等待队列的长度
                serverSocket = new ServerSocket(port,1, InetAddress.getByName(host));
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            System.out.println(String.format("SimpleHTTPServer is running on %s:%d",host,port));
    
            // 等待客户端请求
            while (true){
                Socket socket = null;
                InputStream in = null;
                OutputStream out = null;
    
                try {
                    socket = serverSocket.accept();
                    in = socket.getInputStream();
                    out = socket.getOutputStream();
                    // 解析响应
                    Request request = new Request(in);
                    request.parse();
                    String uri = request.getUri();
                    if( CMD_SHUTDOWN.equals(uri) ){
                        break;
                    }
    
                    // 生成请求
                    Response reponse = new Response(out);
                    reponse.setRequest(request);
                    reponse.SendStaticResource();
    
    
    
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    Closeable[] needClose = {in,out,socket};
                    for( Closeable elem : needClose ){
                        if( null != elem ){
                            try {
                                elem.close();
                            } catch (IOException e) {
                            }
                        }
                    }
                }
            }
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    自定义 Request 类负责解析请求:

    package me.gpio.tomcat;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * Created by Cheney
     */
    public class Request {
        private static final int MAX_BUFF_SIZE = 1024;
        private InputStream in;
        private String uri;
        private String stringRequest;
    
        public Request(InputStream in) {
            this.in = in;
        }
    
        public void parse() {
            byte[] buff = new byte[MAX_BUFF_SIZE];
            StringBuffer stringBuffer = new StringBuffer(MAX_BUFF_SIZE);
            // 一般不会很长,读一次够了
            int ch = -1;
            try {
                ch = in.read(buff,0,MAX_BUFF_SIZE);
            } catch (IOException e) {
                e.printStackTrace();
                ch = -1;
            }
    
            for( int i=0 ; i < ch ; i++ ){
                stringBuffer.append((char)buff[i]);
            }
    
            stringRequest = stringBuffer.toString();
            System.out.println(stringRequest);
    
            uri = parseUri();
    
        }
    
        private String parseUri() {
            assert null != stringRequest;
    
            int index1 = stringRequest.indexOf(" ");
            int index2 = stringRequest.indexOf(" ",index1 +1);
            if( index2 > index1 ){
                return stringRequest.substring(index1+1,index2);
            }
            return null;
        }
    
        public String getUri(){
            return uri;
        }
    
    }
    

    自定义 Response 类负责响应请求:

    package me.gpio.tomcat;
    import java.io.*;
    
    /**
     * Created by Cheney
     */
    public class Response {
    
        private static final int MAX_BUFF_SIZE = 1024;
        private OutputStream out = null;
        private Request request = null;
    
        public Response(OutputStream out) {
            this.out = out;
        }
    
        public void setRequest(Request request){
            this.request = request;
        }
    
        public void SendStaticResource(){
            byte[] buff = new byte[MAX_BUFF_SIZE];
            FileInputStream fis = null;
            File file = null;
    
            try {
                String filePath = SimpleHttpServer.WEB_ROOT + request.getUri();
                System.out.println(filePath);
                file = new File( filePath );
                if( file.exists() && file.isFile()) {
                    fis = new FileInputStream(file);
                    int ch = fis.read(buff,0,MAX_BUFF_SIZE);
                    while (-1 != ch){
                        out.write(buff,0,ch);
                        ch = fis.read(buff,0,MAX_BUFF_SIZE);
                    }
                }else{
                    String errMsg = "HTTP/1.1 404 File Not Found\r\n" +
                            "Content-Type: text/html\r\n" +
                            "Content-Length: 23\r\n" +
                            "\r\n" +
                            "<h1>File not Found</h1>";
                    out.write(errMsg.getBytes());
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if( null != fis ){
                    try {
                        fis.close();
                    } catch (IOException e) {
                    }
                }
            }
    
        }
    }