利用socket解析http,做一个我自己的'tomcat'

Server

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket=new ServerSocket(10006);
        Socket socket=null;
        System.out.println("服务端已启动");
        while(true){
            socket=serverSocket.accept();
            new Thread(new ServerThread(socket)).start();
        }
    }
}

ServerThread

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

public class ServerThread implements Runnable {
    private Socket socket;
    private int contentLength;
    private View view=new View();
    public ServerThread(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        try {
//            while(true) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                PrintStream out = new PrintStream(socket.getOutputStream());
                String tmp=reader.readLine();
                //用浏览器访问时会请求图标,不处理这个请求
                if(!tmp.contains("favicon")){
                    Request request = new Request();
                    request.parser(tmp);
                    while (!(tmp = reader.readLine()).equals("")) {
                        if (tmp.indexOf("Content-Length") != -1) {
                            contentLength = Integer.parseInt(tmp.substring(tmp.indexOf("Content-Length") + 16));
                        }
                        request.parser(tmp);
                    }
                    if (request.getMethod().equals("POST"))
                        getdata(reader, request);
                    view.display(out,request);
                }
                socket.close();
//                break;//....这样的话。。。一个线程处理完一次HTTP请求就关闭了,没有实现长连接
//            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getdata(BufferedReader reader, Request request) throws IOException {
        //取得数据!
        byte[] buf = {};
        int size = 0;
        if (contentLength != 0) {
            buf = new byte[contentLength];
            while(size<contentLength){
                int c = reader.read();
                buf[size++] = (byte)c;

            }
            String s=new String(buf, 0, size);
            request.parserPostParameter(s);
        }
    }
}

Request

import java.util.ArrayList;
import java.util.HashMap;

/**
 * HTTP请求的封装
 */
public class Request {
    private String method;
    private String protocol;// 协议版本
    private String requestURI;
    private String host;
    private String Connection;//Http请求连接状态信息 对应HTTP请求中的Connection
    private String agent;// 代理,标识浏览器信息,对应HTTP请求中的User-Agent:
    private String language;//对应Accept-Language
    private String encoding;
    private String charset;
    private String accept;// 对应Accept;
    private HashMap<String,String> parameter;

    public HashMap<String, String> getParameter() {
        return parameter;
    }

    public void setParameter(HashMap<String, String> parameter) {
        this.parameter = parameter;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public void setRequestURI(String requestURI) {
        this.requestURI = requestURI;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getConnection() {
        return Connection;
    }

    public void setConnection(String connection) {
        Connection = connection;
    }

    public String getAgent() {
        return agent;
    }

    public void setAgent(String agent) {
        this.agent = agent;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public String getAccept() {
        return accept;
    }

    public void setAccept(String accept) {
        this.accept = accept;
    }

    //暂时只支持get和post
    public void parser(String s) {
        if (s.startsWith("GET")) {
            System.out.println(s);
            String method = "GET";
            setMethod(method);

            int index = s.indexOf("HTTP");
            String uri = s.substring(3 + 1, index - 1);// 用index-1可以去掉连接中的空格
            setRequestURI(uri);
            parserGetParameter(uri);

            String protocol = s.substring(index);
            setProtocol(protocol);
        } else if(s.startsWith("POST")){
            System.out.println(s);
            String method = "POST";
            setMethod(method);

            int index = s.indexOf("HTTP");
            String uri = s.substring(4 + 1, index - 1);
            setRequestURI(uri);

            String protocol = s.substring(index);
            setProtocol(protocol);

        }
        else{
            switch (s.substring(0,s.indexOf(":"))){
                case "Accept":
                    String accept = s.substring("Accept:".length() + 1);
                    setAccept(accept);
                    break;
                case "User-Agent":
                    String agent = s.substring("User-Agent:".length() + 1);
                    setAgent(agent);
                    break;
                case "Host":
                    String host = s.substring("Host:".length() + 1);
                    setHost(host);
                    break;
                case "Accept-Language":
                    String language = s.substring("Accept-Language:".length() + 1);
                    setLanguage(language);
                    break;
                case "Accept-Charset":
                    String charset = s.substring("Accept-Charset:".length() + 1);
                    setCharset(charset);
                    break;
                case "Accept-Encoding":
                    String encoding = s.substring("Accept-Encoding:".length() + 1);
                    setEncoding(encoding);
                    break;
                case "Connection":
                    String connection = s.substring("Connection:".length() + 1);
                    setConnection(connection);
                    break;
            }
        }

    }

    /**
     * 解析get方式请求的参数
     * @param uri
     */
    private void parserGetParameter(String uri) {
        if(!uri.contains("?"))
            return;
        this.parameter=new HashMap<>();
        uri=uri.substring(uri.indexOf("?")+1);
        String[] list=uri.split("&");
        for(String e:list){
            String[] key_value=e.split("=");
            parameter.put(key_value[0],key_value[1]);
        }
    }

    /**
     * post的参数解析必须外部手动调用。
     * 知道这样做还有点蠢= =,时间紧迫
     * @param s 形如username=%E7%A7%A6%E8%91%97&password=123
     */
    public void parserPostParameter(String s){
        if(s==null||s.length()==0)
            return;
        this.parameter=new HashMap<>();
        String[] list=s.split("&");
        for(String e:list){
            String[] key_value=e.split("=");
            if(key_value.length!=1)//kev_value==1时是提交空表单的情况
                parameter.put(key_value[0],key_value[1]);
        }
    }
}

Service

import java.util.HashMap;
import java.util.Set;

/**
 * 业务处理,
 * 此处就是简单的登录和注册
 */
public class Service {
    public static HashMap<String,String> users=new HashMap<>();

    public static boolean register(Request request) {
        HashMap<String,String> parameter=request.getParameter();
        String username=parameter.get("username");
        Set<String> un=users.keySet();
        //账户已存在
        if(un.contains(username)){
            System.out.println("账户已存在");
            return false;
        }
        String password=parameter.get("password");
        String repassword=parameter.get("repassword");
        if(password.equals(repassword)){
            users.put(username,password);
            return true;
        }
        System.out.println("两次密码不相同");
        return false;
    }

    public static boolean login(Request request) {
        HashMap<String,String> parameter=request.getParameter();
        String username=parameter.get("username");
        String password=parameter.get("password");
        String pd=users.get(username);
        if(pd==null||pd.equals("")||pd.length()==0)
            return false;
        if(!pd.equals(password))
            return false;
        return true;
    }
}

View

import java.io.PrintStream;

/**
 * 根据客户端的类型(控制台/浏览器)
 * 显示页面或者简单字符
 */
public class View {

    public void display(PrintStream out, Request request) {
        String uri=request.getRequestURI();
        boolean isBrowser=true;
        //根据请求头里的Agent来判断是浏览器发出的请求还是控制台发出的请求
        if (request.getAgent().contains("Qin.Z.Qin"))
            isBrowser=false;
        switch (uri){
            case "/":
            case "/index":
                showLogin(out);
                break;
            case "/register":
                showRegister(out);
                break;
            case "/saveUser":
                if(Service.register(request))
                    showRegisterSuccess(out,isBrowser);
                else
                    showRegisterFail(out,isBrowser);
                break;
            case "/login":
                if(Service.login(request))
                    showLoginSuccess(out,isBrowser);
                else
                    showLoginFail(out,isBrowser);
        }
    }

    private void showLoginFail(PrintStream out,boolean isBrowser) {
        String s="登录失败,请重试";
        if(isBrowser)
            s="<div class=\"jumbotron\">\n" +
                "  <h1>error!</h1>\n" +
                "  <p>please check your username and password</p>\n" +
                "  <p><a class=\"btn btn-primary btn-lg\" href=\"/index\" role=\"button\">Retry</a></p>\n" +
                "</div>";
        output(s,out,isBrowser);
    }

    private void showLoginSuccess(PrintStream out,boolean isBrowser) {
        String s="登录成功";
        if(isBrowser)
            s="<div class=\"jumbotron\">\n" +
            "  <h1>login success!</h1>\n" +
            "  <p>This is Qin.Z.Qin's network program</p>\n" +
            "  <p><a class=\"btn btn-primary btn-lg\" href=\"/index\" role=\"button\">Home</a></p>\n" +
            "</div>";
        output(s,out,isBrowser);
    }

    private void showRegisterFail(PrintStream out,boolean isBrowser) {
        String s="注册失败,请检查用户名和密码";
        if(isBrowser)
            s="<div class=\"jumbotron\">\n" +
                "  <h1>error!</h1>\n" +
                "  <p>This is Qin.Z.Qin's network program</p>\n" +
                "  <p><a class=\"btn btn-primary btn-lg\" href=\"/register\" role=\"button\">go back</a></p>\n" +
                "</div>";
        output(s,out,isBrowser);
    }

    /**
     *
     * @param out
     * @param isBrowser 是否是浏览器在访问
     */
    private void showRegisterSuccess(PrintStream out, boolean isBrowser) {

        String s="注册成功";
        if(isBrowser)
            s="<div class=\"jumbotron\">\n" +
                "  <h1>register success!</h1>\n" +
                "  <p>This is Qin.Z.Qin's network program</p>\n" +
                "  <p><a class=\"btn btn-primary btn-lg\" href=\"/index\" role=\"button\">Home</a></p>\n" +
                "</div>";
        output(s,out,isBrowser);
    }

    private void showRegister(PrintStream out) {
        String s="<div class=\"container\">\n" +
                "\t<div class=\"row clearfix\">\n" +
                "\t\t<div class=\"col-md-12 column\">\n" +
                "\t\t\t<form role=\"form\" action=\"/saveUser\" method=\"post\">\n" +
                "\t\t\t\t<div class=\"form-group\">\n" +
                "\t\t\t\t\t <label for=\"exampleUsername1\">Username</label><input type=\"text\" name=\"username\" class=\"form-control\" id=\"exampleInputEmail1\" />\n" +
                "\t\t\t\t</div>\n" +
                "\t\t\t\t<div class=\"form-group\">\n" +
                "\t\t\t\t\t <label for=\"exampleInputPassword1\">Password</label><input type=\"password\" name=\"password\" class=\"form-control\" id=\"exampleInputPassword1\" />\n" +
                "\t\t\t\t\t <label for=\"exampleInputPassword1\">Repeat Password</label><input type=\"password\" name=\"repassword\" class=\"form-control\" id=\"exampleInputPassword2\" />\n" +
                "\t\t\t\t</div>\n" +
                "\t\t\t\t<div class=\"form-group\">\n" +
                "\t\t\t\t</div> <button type=\"submit\" class=\"btn btn-default\">Submit</button>\n" +
                "\t\t\t</form>\n" +
                "\t\t</div>\n" +
                "\t</div>\n" +
                "</div>";
        output(s,out,true);
    }

    /**
     * 显示登录页
     * @param out
     *
     */
    private void showLogin(PrintStream out) {
        String s="<div class=\"container\">\n" +
                "\t<div class=\"row clearfix\">\n" +
                "\t\t<div class=\"col-md-12 column\">\n" +
                "\t\t\t<form role=\"form\" action=\"/login\" method=\"post\">\n" +
                "\t\t\t\t<div class=\"form-group\">\n" +
                "\t\t\t\t\t <label for=\"exampleUsername1\">Username</label><input type=\"text\" name=\"username\" class=\"form-control\" id=\"exampleInputEmail1\" />\n" +
                "\t\t\t\t</div>\n" +
                "\t\t\t\t<div class=\"form-group\">\n" +
                "\t\t\t\t\t <label for=\"exampleInputPassword1\">Password</label><input type=\"password\" name=\"password\" class=\"form-control\" id=\"exampleInputPassword1\" />\n" +
                "\t\t\t\t</div>\n" +
                "\t\t\t\t<div class=\"form-group\">\n" +
                "\t\t\t\t</div> <button type=\"submit\" class=\"btn btn-default\">Submit</button>\n" +
                "\t\t\t\t<button class=\"btn btn-default\" type=\"button\" onclick=\"window.open(\'/register\')\">register</button>"+
                "\t\t\t</form>\n" +
                "\t\t</div>\n" +
                "\t</div>\n" +
                "</div>";
        output(s,out,true);
    }


    /**
     * 给body添加上响应行,响应头
     * @param s
     * @param out
     */
    private void output(String s, PrintStream out, boolean isBrowser) {
        if(isBrowser){
            out.println("HTTP/1.1 200 OK");
            out.println("Content-Type:text/html;charset:UTF-8");
            out.println();
            s="<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><html><body>"+
                    "<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\" integrity=\"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\">"+
                    s+
                    "</body></html>";
        }
        out.println(s);
        out.flush();
        out.close();
    }

}

Client

import java.io.*;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;

/**
 * 控制台客户端
 * (推荐使用浏览器作为客户端)
 */
public class Client {
    public static void main(String[] args) throws IOException {
        boolean flag = true;
        while(flag){
            Socket socket=new Socket("127.0.0.1",10006);
            socket.setSoTimeout(10000);
            Scanner in =new Scanner(System.in);
            PrintStream outer=new PrintStream(socket.getOutputStream());
            BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            System.out.println("欢迎使用粗陋的客户端,网页端地址:127.0.0.1:10006/index");
            System.out.println("【1】:登录");
            System.out.println("【2】:注册");
            System.out.println("【3】:关闭");
            System.out.println("请输入你的选择:");
            int a=in.nextInt();
            switch (a){
                case 1:
                    login(in,outer,reader,socket);
                    break;
                case 2:
                    register(in,outer,reader,socket);
                    break;
                case 3:
                    System.exit(0);
                default:
                    break;
            }
        }

    }

    private static void register(Scanner in, PrintStream outer, BufferedReader reader, Socket socket)
            throws IOException {
        String username,password,repassword;
        System.out.println("输入用户名");
        username=in.next();
        System.out.println("输入密码");
        password=in.next();
        System.out.println("确认密码");
        repassword=in.next();
        HashMap<String,String> map=new HashMap<>();
        map.put("username",username);
        map.put("password",password);
        map.put("repassword",repassword);
        String request= buildPostRequest(socket,"/saveUser",map);
        outer.println(request);
        String echo="";
        String tmp;
        try {
            while ((tmp=reader.readLine())!=null){
                echo+=tmp;
            }
        } catch (SocketTimeoutException e) {
            System.out.println("服务端响应超时");
        }
        System.out.println(echo+"\n\n");
    }

    private static void login(Scanner in, PrintStream outer, BufferedReader reader, Socket socket)
            throws IOException {
        String username,password;
        System.out.println("请输入你的用户名");
        username=in.next();
        System.out.println("请输入你的密码");
        password=in.next();
        HashMap<String,String> map=new HashMap<>();
        map.put("username",username);
        map.put("password",password);
        String request= buildPostRequest(socket,"/login",map);
        outer.println(request);
        String echo="";
        String tmp;
        try {
            while ((tmp=reader.readLine())!=null){
                echo+=tmp;
            }
        } catch (SocketTimeoutException e) {
            System.out.println("服务端响应超时");
        }
        System.out.println(echo+"\n\n");
    }


    private static String buildPostRequest(Socket socket, String uri, HashMap<String, String> parameter) {
        StringBuilder sb=new StringBuilder();

        String requestBody="";
        Set<String> keys=parameter.keySet();
        for(String key:keys){
            requestBody+=key+"="+parameter.get(key)+"&";
        }
        requestBody=requestBody.substring(0,requestBody.length()-1);

        sb.append("POST "+uri+" HTTP/1.1\n");
        sb.append("Host: "+socket.getInetAddress()+":"+socket.getPort()+"\n");
        sb.append("Connection: keep-alive\n");
        sb.append("Content-Length: "+requestBody.length()+"\n");
        sb.append("Cache-Control: max-age=0\n");
        sb.append("Upgrade-Insecure-Requests: 1\n");
        sb.append("Content-Type: application/x-www-form-urlencoded\n");
        sb.append("User-Agent: Qin.Z.Qin\n");
        sb.append("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\n");
        sb.append("Accept-Encoding: gzip, deflate, br\n");
        sb.append("Accept-Language: zh-CN,zh;q=0.9\n");
        sb.append("\n");
        sb.append(requestBody);
        return sb.toString();
    }
}

参考一片很有用的帖子

https://bbs.csdn.net/topics/310107460

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值