webServer

2017.09.08:webServer服务器练习
要求:在浏览器上输入URL,实现读取本机html文件
主要目的:学习掌握HTTP超文本协议原理及浏览器与服务器数据交换过程实现
主要用的到的知识点:
1. HTTP协议:
2. html
3. tcp协议
4. 多线程:线程池
5. IO流
6. 配置文件
7. map集合
8. xml文件读写
一、浏览器与服务器之间的交互
这里写图片描述

1.1请求报文格式
这里写图片描述
HTTP请求由三部分组成:请求行,消息头,消息正文
请求行格式:
Method Request-URI HTTP-Version CRLF
例如:
GET /index.html HTTP/1.1CRLF
CR:回车 LF:换行

实体头(消息头)格式:
name:value CRLF
例如:
CRLF(消息头每一项信息后面都会有CRLF,当消息头所
信息发送完毕后,会单独发送一个CRLF。)

响应体(消息)正文:
实际随请求发送过来的数据(POST请求用,GET请求该项为空)

1.2 响应报文格式
这里写图片描述
HTTP响应的格式:
响应包含三部分:状态行,响应头,响应正文

状态行的格式:
HTTP-Version Status-Code Reason-PhraseCRLF
协议版本 , 状态值 , 状态描述
例如:
HTTP/1.1 200 OKCRLF

响应头格式:
name:value
例如:
Content-Type:text/htmlCRLF
Content-Length:35CRLF
CRLF
所有响应头信息完毕后也会单独发送一个CRLF

响应正文为服务端实际回应客户端的资源数据。
1.3 交互过程详解:

1.3.1浏览器和服务器建立连接:
a. 浏览器通过URL(统一资源定位)解析到服务器地址,若果URL地址是一个域名,不是IP地址,,则通过DNS(Domain Name System,域名系统)将域名解析成IP地址。
URL中不包括端口号,则使用协议的默认端口号。如http协议的默认端口号为80。
b. TCP三次握手建立连接:http应用层协议是建立在TCP传输层协议之上的。在浏览器发送http请求之前,会先通过三次握手建立TCP连接,提供可靠传输。

1.3.2 浏览器发送请求:浏览器对服务器发送的一系列消息,希望可以在服务器端获取到自己想要的数据。而这些消息就是浏览器对服务器发出的请求,一般包括请求的类型,请求的数据,请求的浏览器信息等。

1.3.3 服务器响应请求:服务器在接到浏览器发出的请求后,会对这些请求做出处理,并向浏览器进行反馈。反馈的数据包括浏览器的请求是否成功,状态码,浏览请请求所需要的数据信息等(一个html页面,图片等等)。

二、运行结果图:
这里写图片描述

三、代码实现:
1 服务端代码

package com.tedu.core;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * @author S-miles 2017-09-10
 */
public class webServer {
    // 浏览器与服务器建立连接
    private ServerSocket server;
    // 创建线程池处理客户端请求任务
    private ExecutorService threadPool;

    /**
     * 构造方法初始化,定义服务器端口
     * 
     * @throws IOException
     */
    public webServer() throws IOException {
        try {
            server = new ServerSocket(8088);
            threadPool = Executors.newFixedThreadPool(100);
        } catch (IOException e) {
            throw e;
        }
    }

    /**
     * start() 程序执行方法
     */
    public void start() {
        try {
            System.out.println("等在连接...");
            Socket socket = server.accept();
            System.out.println("已成功连接!");
            // 创建一个客户端请求对象
            ClientHandle handle = new ClientHandle(socket);
            // 把客户端对象任务交给线程池处理
            threadPool.execute(handle);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 主方法程序入口,启动程序
     * 
     * @param args
     */
    public static void main(String[] args) {
        try {
            webServer server = new webServer();
            server.start();
        } catch (Exception e) {
            System.out.println("服务器启动失败!");
        }
    }
}

2 客户端代码

package com.tedu.core;

/**
 * @author S-miles 2017-09-10
 * 
 */
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import com.tedu.common.HttpContext;
import com.tedu.http.HttpRequest;
import com.tedu.http.HttpResponse;

public class ClientHandle implements Runnable {
    private Socket socket;

    public ClientHandle(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        // 要处理的任务
        // 1 输入输出流要处理异常
        try {
            // 获取客户端请求 输入的流
            InputStream input = socket.getInputStream();
            // 封装一个请求对象
            HttpRequest request = new HttpRequest(input);
            // 服务端做出响应 ,获取服务端的输出流
            OutputStream output = socket.getOutputStream();
            // 封装一个响应对象
            HttpResponse response = new HttpResponse(output);
            // 获取请求的url路径
            String url = request.getUrl();
            // 创建一个文件对象
            File file = new File("webapp" + File.separator + url);
            if (file.exists()) {
                System.out.println("找到资源" + file.length());
                responseServer(HttpContext.STATUS_CODE_OK, file, response);
            } else {
                System.out.println("您访问的页面不存在!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private String responseContentType(File file) {
        String name = file.getName();
        int index = name.lastIndexOf(".");
        String ext = name.substring(index + 1);
        String contentType = HttpContext.contentTypeMapping.get(ext);
        return contentType;
    }

    private void responseServer(Integer Statue, File file, HttpResponse response) throws Exception {
        response.setStatus(Statue);
        response.setContentType(responseContentType(file));
        response.setContentLength((int) file.length());
        response.setEntity(file);
        response.flush();
    }
}

2.1 封装的请求对象

package com.tedu.http;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import com.tedu.common.HttpContext;

/**
 * 
 * @author S-miles 2017-09-10
 *
 */
public class HttpRequest {
    private InputStream in;
    private String methon;
    private String url;
    private String verson;
    private String name;
    private String value;
    private Map<String, String> map;

    public HttpRequest(InputStream input) throws Exception {
        this.in = input;
        try {
            // 解析请求行
            parseRequestLine();
            // 解析消息头
            parseRequestHeader();
        } catch (Exception e) {
            throw e;
        }
    }

    // 解析一行
    private String parseReadLine() throws Exception {
        StringBuilder builder = new StringBuilder();
        int c1 = -1, c2 = -1;
        while ((c1 = in.read()) != -1) {
            if (c1 == HttpContext.LF && c2 == HttpContext.CR) {
                break;
            }
            c2 = c1;
            builder.append((char) c1);
        }
        String line = builder.toString().trim();
        System.out.println(line);
        return line;
    }

    // 解析请求行
    private void parseRequestLine() {
        try {
            String line = parseReadLine();
            String[] data = line.split("\\s");
            methon = data[0];
            url = data[1];
            verson = data[2];
            System.out.println("解析请求行完毕:" + methon + " " + url + " " + verson);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 解析消息头
    private void parseRequestHeader() {
        String line = null;
        try {
            map = new HashMap<String, String>();
            while (true) {
                line = parseReadLine();
                if (line.length() == 0) {
                    break;
                }
                name = line.substring(0, line.indexOf(":"));
                value = line.substring(line.indexOf(":"));
                map.put(name, value);
                // System.out.println(line);
            }
            System.out.println("消息头解析完毕:" + line);
        } catch (Exception e) {
            System.out.println("异常发生了");
            e.printStackTrace();
        }

    }

    public String getMethon() {
        return methon;
    }

    public String getUrl() {
        return url;
    }

    public String getVerson() {
        return verson;
    }

}

2.2 封装的响应对象

package com.tedu.http;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import com.tedu.common.HttpContext;

/**
 * 
 * @author S-miles 2017-09-10
 *
 */
public class HttpResponse {
    private OutputStream output;
    // 响应状态描述集合
    private Map<Integer, String> statusMap;
    // 响应状态
    private int status;
    // 响应资源类型
    private String contentType;
    // 响应资源大小
    private int contentLength = -1;;
    // 响应实体
    private File entity;

    public HttpResponse(OutputStream output) {
        this.output = output;
        statusMap = new HashMap<Integer, String>();
        statusMap.put(HttpContext.STATUS_CODE_OK, HttpContext.STATUS_REASON_OK);
    }

    // 响应请求资源
    public void flush() throws Exception {
        try {
            // 发送响应行(状态 状态描述)
            responseStatus();
            // 响应头(资源类型,资源大小)
            responseHeader();
            // 响应实体(资源)
            responseEntity();
        } catch (Exception e) {
            System.out.println("响应客户段失败!");
            throw e;
        }

    }

    // 发送一行
    private void println(String line) throws Exception {
        try {
            output.write(line.getBytes("ISO8859-1"));
            output.write(HttpContext.CR);
            output.write(HttpContext.LF);
        } catch (Exception e) {
            throw e;
        }
    }
    // 发送响应行(状态 状态描述)

    private void responseStatus() throws Exception {
        try {
            String line = "HTTP/1.1" + " " + status + " " + statusMap.get(status);
            System.out.println(line);
            println(line);
        } catch (Exception e) {
            throw e;
        }

    }

    // 响应头(资源类型,资源大小)
    private void responseHeader() throws Exception {
        try {
            if (contentType.length() != 0) {
                String line = contentType;
                println(line);
            }
            if (contentLength >= 0) {
                String line = contentType;
                println(line);
            }
            println("");
        } catch (Exception e) {
            throw e;
        }
    }

    // 响应实体(资源)
    private void responseEntity() throws Exception {
        BufferedInputStream bis = null;
        try {
            FileInputStream fis = new FileInputStream(entity);
            bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(output);
            int d = -1;
            while ((d = bis.read()) != -1) {
                bos.write(d);
            }
            bos.flush();
        } catch (Exception e) {
            throw e;
        } finally {
            if (bis != null) {
                try {

                    bis.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }

        }
    }

    public int getStatus() {
        return status;
    }

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

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public int getContentLength() {
        return contentLength;
    }

    public void setContentLength(int contentLength) {
        this.contentLength = contentLength;
    }

    public File getEntity() {
        return entity;
    }

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

}

3 HTTP相关配置文件

package com.tedu.common;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * HTTP协议相关信息定义
 * 
 * @author adminitartor
 *
 */
public class HttpContext {
    public static final int CR = 13;
    public static final int LF = 10;

    /*
     * 状态代码定义
     */
    // 状态码-接受成功
    public static final int STATUS_CODE_OK = 200;
    // 状态描述-接受成功
    public static final String STATUS_REASON_OK = "OK";

    // 状态码-资源未发现
    public static final int STATUS_CODE_NOTFOUND = 404;
    // 状态描述-资源未发现
    public static final String STATUS_REASON_NOTFOUND = "Not Found";

    // 状态码-服务器发生未知错误
    public static final int STATUS_CODE_ERROR = 500;
    // 状态描述-服务器发生未知错误
    public static final String STATUS_REASON_ERROR = "Internal Server Error";

    /*
     * 状态码-状态描述 对应的Map
     */
    public static final Map<Integer, String> statusMap = new HashMap<Integer, String>();

    /*
     * Content-Type映射Map key:资源类型(资源文件的后缀名) value:对应该资源在HTTP协议中规定的ContentType
     * 
     * 例如:index.html 那么这个文件在该map中对应key应当是html value对应的值就是text/html
     */
    public static final Map<String, String> contentTypeMapping = new HashMap<String, String>();

    static {
        /*
         * 根据配置文件初始化相关信息 /conf/web.xml
         * 
         */
        // 1初始化ContentType映射
        initContentTypeMapping();

        // 2初始化状态码-状态描述
        initStatus();
    }

    private static void initStatus() {
        statusMap.put(STATUS_CODE_OK, STATUS_REASON_OK);
        statusMap.put(STATUS_CODE_NOTFOUND, STATUS_REASON_NOTFOUND);
        statusMap.put(STATUS_CODE_ERROR, STATUS_REASON_ERROR);
    }

    private static void initContentTypeMapping() {
        /*
         * 将web.xml配置文件中<type-mappings>中 的每一个<type-mapping>进行解析,将
         * 其中属性ext的值作为key,将type属性的 值作为value存入到contentTypeMapping 这个Map中
         */
        System.out.println("初始化ContentType");
        try {
            SAXReader reader = new SAXReader();
            Document doc = reader.read(new File("conf" + File.separator + "web.xml"));
            Element root = doc.getRootElement();
            Element mappingsEle = root.element("type-mappings");
            List<Element> mappingList = mappingsEle.elements();
            for (Element mapping : mappingList) {
                String ext = mapping.attribute("ext").getValue();
                String type = mapping.attribute("type").getValue();
                contentTypeMapping.put(ext, type);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // public static void main(String[] args) {
    // initContentTypeMapping();
    // System.out.println(contentTypeMapping);
    // }
}

人生如棋,不悔!!!

  • 14
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值