欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA实现简易HTTP服务器

程序员文章站 2022-05-04 18:51:19
...

http://blog.csdn.net/aaron_yang666/article/details/52795978



  • 1
  • 说实话,之前完全没有想过,我还能写出服务器。感觉服务器这么高端的东西,能会用就不错了,还能写。

不吐槽了,开始了。 
这次的作业是搭建一个服务器,要能接收请求,并给浏览器返回正确响应。 
项目的下载地址

项目目标:实现一个简易的多线程服务器,可以处理来自浏览器的请求(GET/POST),并做出正确的回应。 
请求分以下四种类型: 
1. 无参数,文本文件类型 
2. 无参数,图片文件类型 
3. 有参数,GET方式请求,并完成表单验证(登陆验证) 
4. 有参数,POST方式请求,并完成表单验证(登陆验证)

首先,应该明确这个项目的基本实现原理,从浏览器读入用户请求的信息,服务器解析并记录返回的文件名和参数列表,如果文件存在,用流读取文件,并返回到浏览器上,如果不存在,返回相应的提示信息,参数列表和服务器存储的相同的话,返回登陆成功,否则返回失败。

第一步,既然要解析从浏览器传过来的信息,那就要明白传过来信息的所使用的协议HTTP\UDP\FTP 和 URL的组成元素,因为是简易服务器,我们就只解析HTTP协议先。

JAVA实现简易HTTP服务器

Request是指从客户端到服务器端的请求消息 
Request 消息分为3部分,第一部分叫请求行, 第二部分叫http header, 第三部分是body. header和body之间有个空行,结构如下图

JAVA实现简易HTTP服务器

Method表示请求方法,比如”POST”,”GET”, 
Path-to-resoure表示请求的资源, 
Http/version-number 表示HTTP协议的版本号, 
当使用的是”GET” 方法的时候, body是为空的,当使用”POST”,body不为空,但是没有换行,readline()方法不能读

Response是指服务器端到客户端的响应信息 
和Request消息的结构基本一样。 同样也分为三部分,第一部分叫request line, 第二部分叫request header,第三部分是body. header和body之间也有个空行, 结构如下图

JAVA实现简易HTTP服务器

状态码用来告诉HTTP客户端,HTTP服务器是否产生了预期的Response. HTTP/1.1中定义了5类状态码,1XX 提示信息 - 表示请求已被成功接收,继续处理;2XX 成功 - 表示请求已被成功接收,理解,接受;3XX 重定向 - 要完成请求必须进行更进一步的处理;4XX 客户端错误 - 请求有语法错误或请求无法实现;5XX 服务器端错误 - 服务器未能实现合法的请求,当然不写也是可以得,状态码就是便于程序员去分析当前页面是正确响应还是错误的。

第二步,在了解URL和HTTP协议之后,就可以开始构建项目了。

目前这个项目的UML图

JAVA实现简易HTTP服务器

第三步,准备文本、图片、HTML文件,然后开始编编编

效果图:(端口号:23333)2333… 
默认访问 
JAVA实现简易HTTP服务器

aaron.txt 
JAVA实现简易HTTP服务器

a.jpg 
JAVA实现简易HTTP服务器

GET/POST请求 
注意地址栏的变化 
login.html(GET) 
JAVA实现简易HTTP服务器

login.html(GET) (登陆失败情况) 
JAVA实现简易HTTP服务器

login.html(GET) (登陆成功情况) 
JAVA实现简易HTTP服务器

login.html(POST) 
JAVA实现简易HTTP服务器

login.html(POST) (登陆失败情况) 
JAVA实现简易HTTP服务器

login.html(POST) (登陆成功情况) 
JAVA实现简易HTTP服务器

部分源码:(全部源码去上面下载)

//Server.java
package cn.net.sight.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import cn.net.sight.thread.ServerThread;

public class Server {
    private static ServerSocket server;
    private static Properties properties;
    private int port;

    static {
        properties = new Properties();
        try {
            properties.load(new FileInputStream(new File("src/resources/property.proterties")));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //initialize the server
    public void init() {
        try {
            port = Integer.parseInt(properties.getProperty("port"));
            server = new ServerSocket(port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //receive the request from the web browser
    public void receive() {
        Socket clientSocket = new Socket();
        try {
            clientSocket = server.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }

        ServerThread thread = new ServerThread(clientSocket);
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    //startup server
    public static void main(String[] args) {
        Server Aaroncat = new Server();
        Aaroncat.init();
        System.out.println("----Aaroncat has startup----");
        while(true){
            Aaroncat.receive();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
//Request.java
package cn.net.sight.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

import cn.net.sight.util.MessageUtil;

public class Request {

    private InputStream input; // Socket --> InputStream
    private Socket socket; // client --> socket
    private BufferedReader buffer; // InputStream --> BufferedReader
    private String schema; // the schema of the request GET or POST
    private String requestFileName; // exact file name
    private String requestData; // file name + <key=value>...
    private String values_Str; // a string the user input in the form
    private int paramLength; // using in the POST: the length of parameters
    private Map<String, String> socketValues;// values_str --> MAP
    private PrintStream print;

    protected MessageUtil messageUtil = new MessageUtil();

    //省略了全部的setter() getter()

    private void doSchema(String firstLineInData) throws IOException {
        socketValues = new HashMap<String, String>();
        if (this.schema.equals("GET")) {
            // GET请求 --> 包含文件名和参数键值对
            // 实现了对FileName、SocketValues的赋值
            this.setRequestData(messageUtil.getRequestData(firstLineInData));
            if (this.requestData.contains("?")) {
                this.setRequestFileName(messageUtil.getFileName(this.getRequestData()));
                this.setSocketValues(messageUtil.getValues(this.getRequestData()));
            } else {
                // GET请求 -->只包含文件名
                // 实现了对FileName的赋值
                this.setRequestFileName(requestData);
            }
        } else {
            // POST请求 第一行只包含文件名
            // 实现了对FileName、SocketValues的赋值
            this.setRequestFileName(messageUtil.getRequestData(firstLineInData));
            this.getUserInfo(buffer);
        }
    }

    private void getUserInfo(BufferedReader br) throws IOException {
        while (this.buffer.ready()) {
            String remained = buffer.readLine();
            if (remained.contains("Content-Length")) {
                String[] temp = remained.split(" ");
                this.setParamLength(Integer.parseInt(temp[1]));
                break;
            }
        }
        buffer.readLine();
        String userInfo = "";
        for (int i = 0; i < this.getParamLength(); i++) {
            userInfo += (char) buffer.read();
        }
        this.setValues_Str(userInfo);
        this.setSocketValues(messageUtil.getValues(this.getValues_Str()));
    }

    public Request(Socket clientSocket) throws IOException {
        this.setSocket(clientSocket);
        this.setPrint(new PrintStream(clientSocket.getOutputStream()));
        this.setInput(clientSocket.getInputStream());
        this.setBuffer(new BufferedReader(new InputStreamReader(clientSocket.getInputStream())));

        // get something from the first line
        String firstLineInData = buffer.readLine();

        this.setSchema(messageUtil.getSchema(firstLineInData)); // 获得请求方式Schema

        doSchema(firstLineInData); // 对Schema进行判断

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
//Response.java
package cn.net.sight.server;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Date;
import java.util.Map;
import cn.net.sight.util.FileUtil;
import cn.net.sight.util.LoginUtil;

public class Response {

    private String fileName;
    private Map<String, String> userValues;
    private PrintStream ps;
    private Request request;
    private Socket clientSocket;

    protected FileUtil fileUtil = new FileUtil();
    protected LoginUtil loginUtil = new LoginUtil();

    public String getFileName() {
        return fileName;
    }

    //省略了全部的setter() 和 getter()

    public Response(Request request) {
        this.setRequest(request);
        this.setClientSocket(request.getSocket());
        this.setFileName(request.getRequestFileName());
        userValues = this.request.getSocketValues();

        try {
            this.init();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void showup(String fileName) throws IOException {
        this.ps = this.request.getPrint();
        // 要处理正常文件名和空文件名
        if (!fileName.equals("")  && !fileName.equals("error.html")) {
            this.ps.println("HTTP/1.1 200 OK");
            this.ps.println();
            fileUtil.readFile(fileName, ps);
        } else if (fileName.equals("error.html")) {
            ps.println("HTTP/1.1 404 fileNotFound");
            ps.println();
            fileUtil.readFile("error.html", ps);
        } else {
            ps.println(new Date().toString());
        }
        if (ps != null){
            ps.close();
        }
    }

    public void init() throws IOException {
        //如果信息MAP是空,则代表是普通文件或者是默认访问
        if (userValues.isEmpty()) {
            if (fileName != "") {
                this.showup(fileName);
            } else {
                this.ps = this.request.getPrint();
                ps.println(new Date().toString());
                if(ps != null) ps.close();
                if(clientSocket != null)clientSocket.close();
            }
        } else {
            //如果信息MAP不为空,代表是GET/POST请求,并带有参数键值对
            this.Check(userValues, fileName);
        }

    }

    public void Check(Map<String, String> values_list, String respFileName) throws IOException {
        // 验证用户输入信息的合法性
        if (loginUtil.isValid(values_list)) {
            this.showup(respFileName);
        } else {
            this.showup("error.html");
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
//ServerThread.java
package cn.net.sight.thread;

import java.io.IOException;
import java.net.Socket;
import cn.net.sight.server.Response;
import cn.net.sight.server.Request;

public class ServerThread extends Thread {
    private Socket clientSocket;
    private Request request;
    private Response response;

    public ServerThread() {
        super();
    }

    public ServerThread(Socket clientSocket) {
        super();
        this.clientSocket = clientSocket;
    }

    public Socket getClientSocket() {
        return clientSocket;
    }

    public void setClientSocket(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    public Request getRequest() {
        return request;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    public Response getResponse() {
        return response;
    }

    public void setResponse(Response response) {
        this.response = response;
    }

    public void run(){
        super.run();
        try {
            this.setRequest(new Request(clientSocket));
            this.response = new Response(request);
            this.setResponse(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
//FileUtil.java
package cn.net.sight.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;

public class FileUtil {
    private static final int BUFFER_SIZE = 1024;

    public void readFile(String file_Name, PrintStream ps) throws IOException {
        byte[] buffer = new byte[BUFFER_SIZE];
        int length;
        File file = new File("src/resources/" + file_Name);
        FileInputStream fis = null;

        if (file.exists()) {
            try {
                fis = new FileInputStream(file);
                while ((length = fis.read(buffer)) != -1) {
                    ps.write(buffer, 0, length);
                    ps.flush();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        } else {
            ps.println("File not found");
            ps.println();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
//LoginUtil.java
package cn.net.sight.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;

public class LoginUtil {
    protected boolean flag = false;
    protected Map<String, String> values;
    private static Properties properties;

    static {
        properties = new Properties();
        try {
            properties.load(new FileInputStream(new File("src/resources/property.proterties")));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public Map<String, String> getValues() {
        return values;
    }

    public void setValues(Map<String, String> values) {
        this.values = values;
    }

    // 验证用户信息的合法性(应用JDBC桥,连接数据库)
    public boolean isValid(Map<String, String> values) {

        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        if (values.get("username").equals(username)) {
            if (values.get("password").equals(password)) {
                flag = true;
                System.out.println("The user " + values.get("username") + " was log the server.");
                return flag;
            }
        } else {
            System.out.println("Forbide the " + values.get("username") + " log the server");
            return flag;
        }
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
//MessageUtil.java
package cn.net.sight.util;

import java.util.HashMap;
import java.util.Map;

public class MessageUtil {

    // schema : GET or POST
    public String getSchema(String requestMsg) {
        String[] result = new String[1];
        if (requestMsg.contains(" ")) {
            result = requestMsg.split(" ");
            requestMsg = result[0];
        }
        return requestMsg;
    }

    // get the resquestData = (filename + map<S,S>)
    public String getRequestData(String firstLineInData) {
        String[] result = new String[10];
        result = firstLineInData.split(" ");
        firstLineInData = result[1].substring(1);
        return firstLineInData;
    }

    // get the filename from the requestData
    public String getFileName(String requestData) {
        String[] result = new String[10];
        result = requestData.split("[?]");
        return result[0];
    }

    // save the info into the map<S,S>
    public Map<String, String> getValues(String requestData) {
        Map<String, String> values = new HashMap<String, String>();
        String[] result = new String[10];
        String regex = "[&=]";

        if (requestData.contains("?")) {
            result = requestData.split("[?]");
            String data_List = result[1];
            result = data_List.split(regex);
            for (int i = 0; i < result.length - 1; i += 2) {
                values.put(result[i], result[i + 1]);
            }
            return values;
        } else {
            result = requestData.split(regex);
            for (int i = 0; i < result.length - 1; i += 2) {
                values.put(result[i], result[i + 1]);
            }
            return values;
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

整个项目结构 
JAVA实现简易HTTP服务器

版权声明:可以转载,但请不要全部复制,加一点自己的实际体会或者优化方案好不好 http://blog.csdn.net/AARON_YANG666/article/details/52795978