手写httpServer Demo案例

2023-05-16

相信每一个java程序猿在学习javaWeb的时候,或多或少接触了Servlet,或者说通过Servlet来完成页面发送的请求。
今天,模仿Servlet接受和处理请求实现一个简单的httpServer。该Server能处理简单的表单提交请求。
一、首先建立一个maven工程,目录如图:
在这里插入图片描述

二、核心组件类
1.启动类Server.java

package com.httpServer.server;


import java.io.IOException;
import java.net.ServerSocket;

/**
 * 服务器类  启动服务
 */
public class Server {
    private ServerSocket serverSocket;
    private boolean isShutDown = false;

    public static void main(String[] args){
        Server server = new Server();
        server.start(8080);
    }

    private void start(int port) {
        try {
            serverSocket = new ServerSocket(8080);
            receive(serverSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void receive(ServerSocket serverSocket) {
        while(! isShutDown) {   //接受多个客户端的请求
            try {
                new Thread(new Dispatcher(serverSocket.accept())).start();   //对于每个客户端的请求都生成一个线程处理
            } catch (IOException e) {
                stop();
            }
        }
    }

    private void stop(){
        try {
            serverSocket.close();
            isShutDown = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

2,处理客户端请求的线程类

package com.httpServer.server;

import java.io.IOException;
import java.net.Socket;

public class Dispatcher implements Runnable{

    private int code;
    private Request request;
    private Response response;

    public Dispatcher(Socket socket){
        try {
            request = new Request(socket.getInputStream());
            response = new Response(socket.getOutputStream());
            code = 200;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            Servlet servlet = WebApp.getServlet(request.getUrl());
            servlet.service(request, response);  //调用对应servlet 的service方法
            response.pushToClient(code);
        } catch (Exception e) {
            code = 404;
            e.printStackTrace();
        }
    }
}

3.Request.java

package com.httpServer.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Request {
    private String method;      //请求方式
    private String url;         //请求路径
    private Map<String, List<String>> paramsMap;        //请求参数   值可以是多个,如兴趣爱好


    private String requestInfo;
    private String CRLF = "\r\n";

    public Request(InputStream is){
        requestInfo = new String();
        byte[] msg = new byte[10*1024];
        try {
           int len =  is.read(msg);
           if(len != -1)
           	requestInfo = new String(msg, 0, len).trim();
        } catch (IOException e) {
            e.printStackTrace();
        }

        parseRequestInfo();
    }

    public String[] getParameterValues(String name){
        if(paramsMap == null){
            return null;
        }
        return paramsMap.get(name).toArray(new String[0]);
    }

    public String getParameter(String name){
        String[] arr = getParameterValues(name);
        if(arr == null){
            return null;
        }
        return arr[0];
    }


    //解析请求头
    private void parseRequestInfo(){
        {
            if(requestInfo == null || "".equals(requestInfo.trim())){
                return;
            }
            System.out.println(requestInfo);
            String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF));
            int idx  = firstLine.indexOf("/");
            this.method = firstLine.substring(0, idx).trim();
            String urlStr = firstLine.substring(idx, firstLine.indexOf("HTTP/")).trim();
            if("POST".equalsIgnoreCase(method)){
                this.url  = urlStr;
                String paramStr = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
                this.paramsMap = parseParams(paramStr);
            }else if("GET".equalsIgnoreCase(method)){
                if(urlStr.contains("?")){
                    String[] arr = urlStr.split("\\?");
                    this.url = arr[0];
                    String paramStr = arr[1];
                    this.paramsMap = parseParams(paramStr);
                }else{
                    this.url = urlStr;
                }

            }

        }
    }


    /**
     * 解析请求参数
     * @return
     */
    private Map<String, List<String>> parseParams(String paramStr){
        Map<String, List<String>>  paramMap = new HashMap<String, List<String>>();
        if(paramStr.contains("&")){
            String[] paramsArr = paramStr.split("&");
            for(int i = 0; i < paramsArr.length; i++){
                String str = paramsArr[i];
                if(str.contains("=")){
                    String[] paramArr = str.split("=");
                    if(paramArr.length == 2){
                        if(paramMap.containsKey(paramArr[0])){
                            List<String> list = paramMap.get(paramArr[0]);
                            list.add(decode(paramArr[1], "utf-8"));
                            paramMap.put(paramArr[0], list);
                        }else{
                            List<String> list = new ArrayList<String>();
                            list.add(decode(paramArr[1], "utf-8"));
                            paramMap.put(paramArr[0], list);
                        }
                    }else{  //uname=&pass=123
                        if(paramMap.containsKey(paramArr[0])){
                            List<String> list = paramMap.get(paramArr[0]);
                            list.add(null);
                            paramMap.put(paramArr[0], list);
                        }else{
                            List<String> list = new ArrayList<String>();
                            list.add(null);
                            paramMap.put(paramArr[0], list);
                        }
                    }

                }
            }
        }
        return paramMap;
    }

    //对前端传入的中文解码
    private String decode(String name, String code){
        try {
            return  java.net.URLDecoder.decode(name, code);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getUrl() {
        return url;
    }
}

4.Response.java

package com.httpServer.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Date;

public class Response {

    private StringBuffer headInfo;
    private StringBuilder content;
    private BufferedWriter bw;


    private int code;
    private int len;
    private final String BLANK = " ";
    private final String CRLF = "\r\n";

    public Response(OutputStream outputStream){
        headInfo = new StringBuffer();
        content = new StringBuilder();
        
        bw = new BufferedWriter(new OutputStreamWriter(outputStream));
    }

    private void createHeader(int code) {
            headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
            switch(code){
                case 200 : headInfo.append("OK");
                    break;
                case 404 : headInfo.append("NOT FOUND");
                    break;
                case 500 : headInfo.append("SERVER ERROR");
                    break;
            }
            headInfo.append(CRLF);
            headInfo.append("Server:myServer Server/0.0.1").append(CRLF);
            headInfo.append("Date:").append(new Date()).append(CRLF);
            headInfo.append("Content-type:text/html;charset=UTF-8").append(CRLF);
            headInfo.append("Content-Length:").append(len).append(CRLF);
            headInfo.append(CRLF);  //至关重要  头部和 内容要隔一行
    }

    public Response print(String content){
        this.content.append(content).append(CRLF);
        this.len += (content + CRLF).getBytes().length;
        return this;
    }

    public void pushToClient(int code) throws IOException {
        this.code = code;

        createHeader(code);
        bw.append(headInfo.toString());
        bw.append(content.toString());

        bw.flush();
        bw.close();

    }
}

5.业务抽象类Servlet.java

package com.httpServer.server;

public abstract class Servlet {

    public void service(Request request, Response response){
        doGet(request, response);
        doPost(request, response);
    }

    public abstract void doGet(Request request, Response response);
    public abstract void doPost(Request request, Response response);
}

6.ServletContext.java

package com.httpServer.server;

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

/**
 * 存放  url -> servletName;  servletName->servletClassPath 映射
 * url:servletName   多对一
 */
public class ServletContext {
    Map<String, String> servlet;   //key: servletName,  value:servlet 类路径
    Map<String, String> mapping;    //key: 请求路径   value:servletName

    public ServletContext(){
        servlet = new HashMap<String, String>();
        mapping = new HashMap<String, String>();
    }

    public Map<String, String> getServlet() {
        return servlet;
    }

    public void setServlet(Map<String, String> servlet) {
        this.servlet = servlet;
    }

    public Map<String, String> getMapping() {
        return mapping;
    }

    public void setMapping(Map<String, String> mapping) {
        this.mapping = mapping;
    }
}

7.WebApp.java

package com.httpServer.server;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class WebApp {
    private static ServletContext servletContext;

    static{
        servletContext = new ServletContext();

        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        try {
            SAXParser saxParser = saxParserFactory.newSAXParser();

            WebHandler webHandler = new WebHandler();
            saxParser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("WEB-INF/web.xml"), webHandler);

            Map<String, String> servlet = servletContext.getServlet();
            List<Entity> entityList = webHandler.getEntityList();
            for(Entity entity : entityList){
                servlet.put(entity.getServletName(), entity.getServletClassPath());
            }


            Map<String, String> mapping = servletContext.getMapping();
            List<Mapping> mappingList  = webHandler.getMappingList();
            for(Mapping map : mappingList){
                List<String> urlPatterns = map.getUrlPatterns();
                for(String urlPattern : urlPatterns){
                    mapping.put(urlPattern, map.getServletName());
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据请求路径  获取对应的servletName
     * 然后根据servletName  获取 servlet的类路径(包名.类名)
     * 根据反射 返回servlet 实例
     *
     * @param url
     * @return
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static Servlet getServlet(String url) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String servletClassPath = servletContext.getServlet().get(servletContext.getMapping().get(url));
        return (Servlet) Class.forName(servletClassPath).newInstance();
    }
}

8.xml文件解析的处理器类,WebHandler.java

package com.httpServer.server;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class WebHandler  extends DefaultHandler {
    private List<Entity> entityList;
    private List<Mapping> mappingList;
    private Entity entity;
    private Mapping mapping;

    private String tagName;
    private boolean isServletMapping = false;

    @Override
    public void startDocument() throws SAXException {
        entityList = new ArrayList<Entity>();
        mappingList = new ArrayList<Mapping>();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        System.out.println(qName);
        if(qName != null && !qName.trim().equals("")){
            tagName = qName;
            if("servlet".equals(qName)){
                entity = new Entity();
                isServletMapping = false;
            }else if("servlet-mapping".equals(qName)){
                mapping = new Mapping();
                isServletMapping = true;
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String tagContent = new String(ch, start, length);
        System.out.println(tagContent);
        if(tagName != null){
            if(!isServletMapping){
                if("servlet-name".equals(tagName)){
                    entity.setServletName(tagContent);
                }else if("servlet-class".equals(tagName)){
                    entity.setServletClassPath(tagContent);
                }
            }else{
                if("servlet-name".equals(tagName)){
                    Iterator<Mapping> iterator = mappingList.iterator();
                    while(iterator.hasNext()){
                        Mapping map = iterator.next();
                        if(map.getServletName().equals(tagContent)){
                            mapping = map;
                            iterator.remove();
                        }
                    }
                    mapping.setServletName(tagContent);
                }else if("url-pattern".equals(tagName)){
                    mapping.getUrlPatterns().add(tagContent);
                }
            }

        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(qName != null && qName.trim().length() != 0){
            if(!isServletMapping && "servlet".equals(qName)){
                entityList.add(entity);
            }else if(isServletMapping && "servlet-mapping".equals(qName)){
                mappingList.add(mapping);
            }
            tagName = null;
        }
    }

    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }

    public List<Entity> getEntityList() {
        return entityList;
    }

    public void setEntityList(List<Entity> entityList) {
        this.entityList = entityList;
    }

    public List<Mapping> getMappingList() {
        return mappingList;
    }

    public void setMappingList(List<Mapping> mappingList) {
        this.mappingList = mappingList;
    }

    public Entity getEntity() {
        return entity;
    }

    public void setEntity(Entity entity) {
        this.entity = entity;
    }

    public Mapping getMapping() {
        return mapping;
    }

    public void setMapping(Mapping mapping) {
        this.mapping = mapping;
    }

    public String getTagName() {
        return tagName;
    }

    public void setTagName(String tagName) {
        this.tagName = tagName;
    }

    public boolean isServletMapping() {
        return isServletMapping;
    }

    public void setServletMapping(boolean servletMapping) {
        isServletMapping = servletMapping;
    }
}

9.存储servletName与servlet类路径的映射类 Entity.java

package com.httpServer.server;

/**
 * 存放 servletName -> servletNameClassPath的映射
 */
public class Entity {
    private String servletName;
    private String servletClassPath;

    public String getServletName() {
        return servletName;
    }

    public void setServletName(String servletName) {
        this.servletName = servletName;
    }

    public String getServletClassPath() {
        return servletClassPath;
    }

    public void setServletClassPath(String servletClassPath) {
        this.servletClassPath = servletClassPath;
    }
}

10.,存储url与servletName的映射类,Mapping.java

package com.httpServer.server;

import java.util.ArrayList;
import java.util.List;

/**
 * 存放url -> servletName的映射
 */
public class Mapping {
    private List<String> urlPatterns = new ArrayList<String>();
    private String servletName;

    public List<String> getUrlPatterns() {
        return urlPatterns;
    }

    public void setUrlPatterns(List<String> urlPatterns) {
        this.urlPatterns = urlPatterns;
    }

    public String getServletName() {
        return servletName;
    }

    public void setServletName(String servletName) {
        this.servletName = servletName;
    }
}

11.配置文件 web.xml

<?xml version="1.0" encoding="utf-8" ?>
<web-app>
    <servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class>com.httpServer.servlet.LoginServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>loginServlet</servlet-name>
        <url-pattern>/tempLogin</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>loginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
</web-app>

三、业务类
1.登录的业务类 LoginServlet

package com.httpServer.servlet;

import com.httpServer.server.Request;
import com.httpServer.server.Response;
import com.httpServer.server.Servlet;

public class LoginServlet extends Servlet {
    @Override
    public void doGet(Request request, Response response) {

    }

    @Override
    public void doPost(Request request, Response response) {
        String name = request.getParameter("uname");
        String pass = request.getParameter("pass");
        if("欣淡定".equals(name) && "123".equals(pass)){
            response.print("<html><head><title>返回信息</title></head><body>欢迎 "+name +" 登录成功</body></html>");
        }else{
            response.print("用户名或密码错误");
        }
    }
}

四,发送请求的页面html代码:

<html>
	<head>
		<title>登录</title>
	</head>
	
	<body>
		<form method="post" action="http://localhost:8080/tempLogin">
			昵称<input type="text" id="name" name="uname"><br/>
			密码<input type="text" id="password" name="pass"/><br/>
			体育运动:<input type="checkbox" name="sport" value="basketball"> 篮球
				<input type="checkbox" name="sport" value="bennis"> 网球
				<input type="checkbox" name="sport" value="football"> 足球</br>
			<input type="submit" value="提交"/>
			<input type="reset" value="重置"/>
		</form>
	</body>
</html>

五,登录测试:
在这里插入图片描述
六,测试结果
在这里插入图片描述

七、总结
测试的途中遇到的小问题:
1.发送post请求的时候,服务器会受到两个请求
2.页面发送请求的时候,服务端会收到一个url为 /favicon.icon的请求,导致控制台会有个空指针的异常;这个应该与页面标题的icon图标有关

以上就是模拟httpServer的一个完整demo

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

手写httpServer Demo案例 的相关文章

随机推荐

  • 基于STM32的串口通讯

    基于STM32的串口通讯 设备之间通信的方式 串行通信一般是以帧格式传输数据 xff0c 即一帧一帧的传输 xff0c 每一帧都含有起始信号 xff0c 数据信息以及停止信息等 并行通信 数据各个位同时传输 xff0c 速度快 xff0c
  • C语言的艺术之——头文件

    好记性不如烂笔头o o 系列的文章 xff1a C语言的艺术之 头文件 C语言的艺术之 函数 C语言的艺术之 标识符命令与定义 C语言的艺术之 变量 C语言的艺术之 注释 C语言的艺术之 排版与格式 C语言的艺术之 安全性 编码原则 xff
  • 图像高斯分布生成

    给定一些标记点的坐标 xff0c 希望生成其在图像中的高斯分布图 首先 xff0c 上公式 xff1a 不造怎么上公式 嗯稍后学习学习再补充 span class hljs keyword import span numpy span cl
  • 编译原理----词法分析设计

    程序设计实验1 词法分析 一 实验目的 xff1a 通过设计编制调试一个具体的词法分析程序 xff0c 加深对词法分析原理的理解 并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法 二 实验内容 编制一个单词获取程序
  • wwwwwwwwwwwwwwwwwww

    wwwwwwwwwwwwwwwwwwwww
  • C++成员变量的初始化

    类对象的构造顺序是这样的 xff1a 1 分配内存 xff0c 调用构造函数时 xff0c 隐式 xff0f 显示的初始化各数据成员 2 进入构造函数后在构造函数中执行一般计算 1 类里面的任何成员变量在定义时是不能初始化的 2 一般的数据
  • 【BUG解决】使用body-parser失效的实例解决

    前言 最近在使用express框架写Node代码 xff0c 遇到一个问题使用body parser模块失效 整整困在这里一天时间 xff01 xff01 xff01 res send req body 返回结果一直为空 但是代码的书写又看
  • BOCHS问题总结篇

    在官网上下载的bochs 2 4 5 win32版 bochs启动时会读bochsrc bxrc里的配置 xff0c 而bochsrc sample txt则是个sample xff0c 可以在这个sample里阅读相关参数的设置 1 RO
  • 关于Access的左连接

    这篇随笔没有什么深奥的技术要讨论 xff0c 只是自己一个知识上的盲点 xff1a 不知道在Access中如何进行左连接的操作 通过在网上搜索 xff0c 最后在CSDN上找到了自己要的答案 xff0c 因此觉得有必要记录下来 xff1a
  • ubuntu下安装Calibre

    Calibre是电子书管理软件 xff0c 支持Amazon Apple Bookeen Ectaco Endless Ideas Google HTC Hanlin Song设备及格式 xff0c 功能十分强大 ubuntu 有很多包都可
  • 编译Linux内核数

    本文是参考了网上多篇帖子而写的算不上什么原创 唯一值得欣慰的只不过在本机上实现罢了 因为毕竟失败了几次 也因为本人是初学驱动编程 很多简单的问题在我来说是相当的困难的 望有识之士不要笑话 最后 xff0c 希望本文能给刚学驱动而还没开头的人
  • 构造内核源码树

    编写驱动程序时 xff0c 需要内核源码树的支持 内核源码树时从内核源代码编译得到的 下面开始构造内核源代码的步骤 以Ubuntu为例子 1 下载内源代码 xff0c 位置www kernel org 注意 xff1a 源码树内核的版本要和
  • 裁剪图像中感兴趣区域python

    题外话 xff1a 比较全面的缩略图及相应源码 http matplotlib org gallery html http www cnblogs com wei li archive 2012 05 23 2506940 html 题外外
  • Linux设备驱动程序(LDD)中snull的编译问题

    对LDD中snull程序 xff0c 编译的时候会有许多问题 xff0c 鉴于网上还没有合适的解决办法 xff0c 做此总结 xff0c 整理知识 本文在debian6 0上运行通过 xff0c 内核版本为2 6 32 学习LDD中网络驱动
  • 认识(大端--小端)端模式

    span style color 000000 端模式 xff08 Endian xff09 的这个词出自Jonathan Swift书写的 格列佛游记 这本书根据将鸡蛋敲开的方法不同将所有的人分为两类 xff0c 从圆头开始将鸡蛋敲开的人
  • HOW TO install nam for ns2 on debian

    Debian is convinent to install software packages for the tool aptl Like many other packages we can use apt get install n
  • c++ #pragma once和 #ifndef 优缺点对比分析

    pragma once ifndef方式为了避免同一个头文件被包含 xff08 include xff09 多次 pragma once 声明 定义语句 ifndef SOMEFILE H define SOMEFILE H 声明 定义语句
  • roslaunch找不到packge

    roslaunch找不到packge 尝试下面几种做法 1 source bashrc 2 source catkin ws devel setup bash 3 rospack profile 为确保ROS能找到新包 xff0c 常常在发
  • DSP:TMS320C6657 之 UART波特率问题

    6657 设置串口波特率 以614400为例 xff08 1 xff09 根据公式计算分频系数 xff08 2 xff09 1GHz 主频下 UART 输入频率 166666666Hz xff08 1 6 xff09 xff08 3 xff
  • 手写httpServer Demo案例

    相信每一个java程序猿在学习javaWeb的时候 xff0c 或多或少接触了Servlet 或者说通过Servlet来完成页面发送的请求 今天 xff0c 模仿Servlet接受和处理请求实现一个简单的httpServer 该Server