实现一个http的简单网络编程

本文介绍了一个基于Java的HTTPV3服务器实现,包括请求和响应的解析与构造,支持GET和POST方法,处理用户登录和会话管理,展示了一个简单的登录页面和登录逻辑。
package HTTPV3;


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

//请求
public class HttpRequest {

    //首行  方法 url(“&和=”)  版本号
    //header “: 和=”
    //空行
    //body (“&和=”)

    private String method;
    private String url;//url解析后放入parameters
    private String version;
    //headers是一个个键值对
    private Map<String, String> headers = new HashMap<>();
    //url 和 body 中的参数都放到这个中
    private Map<String, String> parameters = new HashMap<>();
    private Map<String, String> cookies = new HashMap<>();
    private String body;//body解析后放入parameters中

    public static HttpRequest build(InputStream inputStream) throws IOException {
        HttpRequest request = new HttpRequest();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

        //1.处理首行
        String fistLine = bufferedReader.readLine();
        //这个首行 方法 url 以及version(版本号)都是由" "分隔开
        String[] fistLineTokens = fistLine.split(" ");
        request.method = fistLineTokens[0];
        request.url = fistLineTokens[1];
        request.version = fistLineTokens[2];
        //然后我们需要解析url 我们知道有时候有些信息是放在url中的
        //并且若是有"?"就表示是有这些信息的
        //并且后面的信息是以键值对的形式存在的
        int pos = request.url.indexOf("?");//若是不存在则返回-1;
        if (pos != -1) {
            // =-1的话就没有这些信息,就不用解析了
            String str = request.url.substring(pos + 1);
            parseKV(str, request.parameters);//解析这些信息,并存放入parameters
        }

        //首行解析完,我们需要解析headers
        //循环处理headers
        //header的结束标志是一个空行,可以用while中处理
        String line = "";
        while ((line = bufferedReader.readLine()) != null && line.length() != 0) {
            String[] headersTokens = line.split(": ");//headers是键值对 用": "拆开
            //然后将这个键值对放入headers这个map中
            request.headers.put(headersTokens[0], headersTokens[1]);
        }
        //解析cookie cookie是header中特殊的头部,我们来特殊解析
        String cookie = request.headers.get("Cookie");
        if (cookie != null) {
            parseCookie(cookie, request.cookies);
        }

        //解析 body
        //用不区分大小写的比较
        if ( "POST".equalsIgnoreCase(request.method) ||
             "PUT".equalsIgnoreCase(request.method) ) {
            //我们需要解析body的长度,以字节为单位,并且body的长度是存在于header中的
            int contentLength = Integer.parseInt(request.headers.get("Content-Length"));
            //但是我们创建缓冲区是以char为单位,我们不需要纠结
            //缓冲区的大小 大于body的长度是可以,但是不能小于
            char[] buffer = new char[contentLength];
            int len = bufferedReader.read(buffer);
            request.body = new String(buffer, 0, len);
            //我们知道body的解析其实和url中信息解析式一样
            //都是由“&”拆开键值对。“=”拆开键和值
            //也将其解析到parameters中
            parseKV(request.body, request.parameters);
        }

        //这就逐步解析完了
        return request;
    }

    private static void parseCookie(String cookie, Map<String, String> cookies) {
        //"; "拆开cookie信息组
        String[] cookieTokens = cookie.split("; ");
        for (String kv : cookieTokens) {
            String[] ret = kv.split("=");
            cookies.put(ret[0], ret[1]);
        }
    }

    private static void parseKV(String str, Map<String, String> parameters) {
        // "&"拆开信息组
        String[] kvTokens = str.split("&");
        // "="拆开键和值
        for (String kv : kvTokens) {
            String[] ret = kv.split("=");
            parameters.put(ret[0], ret[1]);
        }

    }

    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    public String getVersion() {
        return version;
    }

    public String getBody() {
        return body;
    }

    public String getHeader(String key) {
        return headers.get(key);
    }

    public String getParameter(String key) {
        return parameters.get(key);
    }

    public String getCookie(String key) {
        return cookies.get(key);
    }
}
package HTTPV3;


import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;

//回应
public class HttpResponse {

    //首行  版本号 状态码  描述信息
    //header “: 和=”
    //空行
    //body “&和=”

    private String version = "HTTP/1.1";
    private int status;//就是 404 200 303 之类的,状态码的描述信息
    private String message;// 状态码的描述信息
    private Map<String, String> headers = new HashMap<>();
    private StringBuilder body = new StringBuilder();//方便body中信息的拼接
    private OutputStream outputStream = null;

    public static HttpResponse build(OutputStream outputStream) {
        HttpResponse response = new HttpResponse();
        response.outputStream = outputStream;

        return response;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setHeader(String key, String value) {
        this.headers.put(key, value);
    }

    public void setBody(String content) {
        this.body.append(content);//这就是将body写出StringBuilder的原因
    }

    public void flush() throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write(version + " " + status + " " + message + "\n");
        headers.put("Content-Length", body.toString().getBytes().length + " ");
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            bufferedWriter.write(entry.getKey() + ": " + entry.getValue());
        }
        bufferedWriter.write("\n");
        bufferedWriter.write(body.toString());
        bufferedWriter.flush();//将缓冲区中的信息刷新到内存中
    }

}
在这里插入代码package HTTPV3;



import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpServerV3 {

    static class User {
        public String userName;
        public int age;
        public String school;
    }
    
    private ServerSocket serverSocket = null;

    //session会话, 指的就是同一个用户的一组访问服务器的操作,归类到一起
    //session中有很多的会话,会话指的就是一个请求和应答,可以理解为一个键值对就是一组会话
    private Map<String, User> sessions = new HashMap<>();
    
    public HttpServerV3(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true) {
            Socket clientSocket = serverSocket.accept();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    process(clientSocket);
                }
            });
        }
    }

    private void process(Socket clientSocket) {
        try {
            //1.计算请求并且解析
            HttpRequest request = HttpRequest.build(clientSocket.getInputStream());
            HttpResponse response = HttpResponse.build(clientSocket.getOutputStream());
            //2.根据请求计算响应
            //方法是get下如何处理
            if ("GET".equalsIgnoreCase(request.getMethod())) {
                doGET(request, response);
            //方法是post如何处理
            } else if ("POST".equalsIgnoreCase(request.getMethod())) {
                doPOST(request, response);
            } else {
                response.setStatus(405);
                response.setMessage("Method Not Allowed");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void doPOST(HttpRequest request, HttpResponse response) {
        if (request.getUrl().startsWith("/login")) {
            String userName = request.getParameter("username");
            String password = request.getParameter("password");

            if ("li".equals(userName) && "123".equals(password)) {
                response.setStatus(200);
                response.setMessage("OK");
                response.setHeader("Content-Type", "text/html; charset=utf-8");

                String sessionId = UUID.randomUUID().toString();
                User user = new User();
                user.userName = "li";
                user.age = 20;
                user.school = "陕科大";
                sessions.put(sessionId, user);
                response.setHeader("Set-Cookie", "sessionId=" + sessionId);

                response.setBody("<html>");
                response.setBody("<div>欢迎" + userName + "</div>");
                response.setBody("</html>");
            } else {
                //登陆失败
                response.setStatus(403);
                response.setMessage("Forbidden");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                response.setBody("<html>");
                response.setBody("<div>登陆失败<div>");
                response.setBody("</html>");
            }
        }

    }

    private void doGET(HttpRequest request, HttpResponse response) throws IOException {
        //1.能够支持返回一个html文件
        String sessionId = request.getCookie("sessionId");
        User user = sessions.get(sessionId);

        if (sessionId == null || user == null) {
            //用户第一次登陆
            response.setStatus(200);
            response.setMessage("OK");
            response.setHeader("Content-Type", "text/html; charset=utf-8");
            //                          获取一个类的对象   获取当前类的“类加载器”   根据文件名再Resources的目录中找到对应文件
            InputStream inputStream = HttpServerV3.class.getClassLoader().getResourceAsStream("index.html");

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = null;
            while ((line = bufferedReader.readLine())!= null) {
                response.setBody(line + "\n");
            }
            bufferedReader.close();
        } else {
            //用户已经登陆,无法在登录
            response.setStatus(200);
            response.setMessage("OK");
            response.setHeader("Content-Type", "text/html; charset=utf-8");
            response.setBody("<html>");
            response.setBody("<div>" + "你已经登录了" + user.userName + "</div>");
            response.setBody(+ user.age + "div");
            response.setBody("<div>" + user.school + "</div>");
            response.setBody("</html>");
        }
    }
}
package HTTPV3;

import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        HttpServerV3 httpServerV3 = new HttpServerV3(80);
        httpServerV3.start();
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>试验</title>
</head>
<body>
    <form method="post" action="/login">
        <div style="margin-bottom: 5px">
            <input type="text" name="username" placeholder="请输入姓名">
        </div>
        <div style="margin-bottom: 5px">
            <input type="password" name="password" placeholder="请输入密码">
        </div>
        <div>
            <input type="submit" value="登陆">
        </div>
    </form>

</body>
</html>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值