手写静态服务器,其实挺简单的 2021-03-26

思路

1 写服务器之前先了解一下,服务器和浏览器之间的交互过程。
当你输入网址回车时,浏览器向目标服务器发送请求。服务器解析url,将你想要的资源发送给浏览器(响应)。
浏览器与服务之间交互的过程2 了解交互过程,我们知道服务器主要做两个事
1)监听端口,获取请求(说白了就是服务器想知道你要我给你那些东西)
2)将浏览器想要的东西拼成浏览器看的懂得报文。

扫一下盲(报文是什么样的)

在这里插入图片描述

知识点,小结(坑)

踩过许多坑,先来说说坑吧。
坑1:中文字符乱码问题
(1)读取字节不完整
utp-8中,一个英文字符占一个字节,中文字符占三个字节
下图就是浏览器读取不完整造成的
解决方案:设置请求头中contextlength的值时,不能使用 data.length(),应使用data.getBytes().length。在这里插入图片描述 (2)字符集不统一
在中国的电脑黑窗口字符集是gbk,而项目是utf-8
解决方案:如图在这里插入图片描述坑2 在拼和拆报文时一定要仔细,个人建议在纸上画图好理解清楚不宜错
我本人就是在草稿纸上画了图。

敲完代码,有写一遍注释,在写这篇文章。实在想不起但是的坑了。可能我已经完全掌握了,O(∩_∩)O哈哈~

源码(注释很详细)

应该没用谁的源码写注释比我的清楚了吧

主类
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @author mzy
 * @date 2021/3/24
 */
public class Http {
    public static void main(String[] args) throws Exception {
        //创建服务器
        ServerSocket server = new ServerSocket();
        //绑定端口
        server.bind(new InetSocketAddress(InetAddress.getByName("192.168.1.200"),4888));
        System.out.println("the server is running , listening port is 4888");
        while(true){
            //监听端口
            Socket accept = server.accept();
            System.out.println("a penson"+accept.getRemoteSocketAddress().toString() +" is comming");
//            new MyTask(accept).start();
            //使用线程池
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            executorService.submit(new MyTask(accept));
        }
    }
}


线程类
// An highlighted block
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @author mzy
 * @date 2021/3/25
 */
public class MyTask implements Runnable{
    private Socket accept =null;
    public MyTask(Socket accept){
        this.accept=accept;
    }
    @Override
    public void run() {
        InputStream inputStream =null;
        OutputStream out = null;
        try{
            StringBuilder sb = new StringBuilder();
            //通过监听获取输入流,将内容读入StringBuilder中
            inputStream= accept.getInputStream();
            byte[] bytes = new byte[512];
            int len;
            while((len = inputStream.read(bytes))!=-1){
                sb.append(new String(bytes,0,len));
                if(len<512){
                    accept.shutdownInput();
                }
            }
            //封装请求 :将请求的报文分解成requset对象的属性。
            //使用静态方法
            Requset requset = Requset.buildRequset(sb.toString());
            //获取输出流
            out = accept.getOutputStream();
            //封装响应:1拼接报文 2报文为response的属性
            Response response = new Response();
            response.setData(HttpUtils.getPage(requset.getUrl()));
            //封装的好处,可以随意添加响应头。
            response.addHeader("a","b");

            //这里write方法是response的方法:用输入流将响应的内容写出去
            response.write(out);


            //用完了,要养成关的习惯。
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(inputStream !=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out !=null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (accept!=null){
                try {
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
请求类
// An highlighted block
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @author mzy
 * @date 2021/3/25
 */
public  class Requset {
    private String type;
    private String url;
    private String protocol;

    private  String contentType;
//只有new  实例化才能调方法
    private Map<String,String> hraders = new HashMap<>(8);
    private Map<String,String> attributes =new HashMap<>(8);

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getProtocol() {
        return protocol;
    }

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

    public String getContentType() {
        return contentType;
    }

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

    public Map<String, String> getHraders() {
        return hraders;
    }

    public void setHraders(Map<String, String> hraders) {
        this.hraders = hraders;
    }

    public Map<String, String> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, String> attributes) {
        this.attributes = attributes;
    }

    @Override
    public String toString() {
        return "Requset{" +
                "type='" + type + '\'' +
                ", url='" + url + '\'' +
                ", protocol='" + protocol + '\'' +
                ", contentType='" + contentType + '\'' +
                ", hraders=" + hraders +
                ", attributes=" + attributes +
                '}';
    }

    public static Requset buildRequset(String requseturl){
        Requset requset = new Requset();

        //拆解请求报文,实例化对象
        String[] split = requseturl.split("\r\n\r\n");
//      请求行 和 请求头
        String[] lineAndHeader = split[0].split("\r\n");
        String[] lines = lineAndHeader[0].split(" ");
        requset.setType(lines[0]);
        requset.setUrl(lines[1]);
        requset.setProtocol(lines[2]);
        for(int i=1;i<lineAndHeader.length;i++){
            String[] header =lineAndHeader[i].split(": ");
            requset.getHraders().put(header[0].trim().toLowerCase(),header[1].trim().toLowerCase());
        }
        //请求体
        if(split.length == 2){
        }
        return requset;
    }

}
响应类
// An highlighted block
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @author mzy
 * @date 2021/3/25
 */
public class Response {
    public Response(String protocol, Integer code, String msg) {
        this.protocol=protocol;
        this.code = code;
        this.msg = msg;

    }
    public Response(){

    }

    private String protocol="HTTP/1.1";
    private Integer code=200;
    private String data;
    private String msg="OK";
    private String ContentType="text/html;charset=utf-8";
    private  String ContentLength;
    private Map<String,String> headers=new HashMap(){{
        put("content-type",ContentType);
    }};

    public String getProtocol() {
        return protocol;
    }

    public String getContentType() {
        return this.getHeaders().get("content-type");
    }

    public void setContentType(String contentType) {
        this.getHeaders().put("content-type",contentType);
    }

    public String getContentLength() {
        return this.getHeaders().get("content-length");
    }

    public void setContentLength(String contentLength) {
        this.getHeaders().put("content-length",this.data.getBytes().length+"");
    }

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

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public String getData() {

        return data;
    }

    public void setData(String data) {
        this.data = data;
        this.setContentLength(data.getBytes().length+"");
    }


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



    //拼报文
    public String bulidResponse(){
        StringBuilder sb = new StringBuilder();
        sb.append(this.getProtocol()).append(" ")
                .append(this.getCode()).append(" ")
                .append(this.getMsg()).append("\r\n");
        for(Map.Entry<String,String> entry : this.getHeaders().entrySet()){
            sb.append(entry.getKey()).append(": ")
                    .append(entry.getValue()).append("\r\n");
        }
        sb.append("\r\n").append(this.getData());
        return sb.toString();
    }

    //写出去,发给客户端。
    public void write(OutputStream os){
        try{
            os.write(bulidResponse().getBytes());
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
工具类
// An highlighted block
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * @author mzy
 * @date 2021/3/25
 */
public class HttpUtils {

    //通过输入一个html文件名 用流 获得它内容的字符串
    public static String getPage(String url)  {
        StringBuilder sb =new StringBuilder();
        if("".equals(url) || "/".equals(url) ||url==null){
            url="index.html";
        }
        try{
            //获取绝对路径
            String path = Test.class.getProtectionDomain().getCodeSource().getLocation().getPath();
            path = path.substring(0,path.lastIndexOf("/"))+"/pages/";
            File file = new File(path + url);
            boolean exists = file.exists();
            if(!exists){
                url="404.html";
            }
            InputStream resource = new FileInputStream(path+url);

            byte[] buf = new byte[512];
            int len;
            while ((len = resource.read(buf)) != -1) {
                sb.append(new String(buf,0,len));
            }
        }catch(Exception e){
            e.printStackTrace();
        }

        return sb.toString();
    }
}

扩展(idea打包 windows命令脚本)

idea打包
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述这就打包完成了。
在这里插入图片描述创建windows命令脚本
在新建一个记事本,写入在cmd中运行jar包的代码,该文件的后缀为.bat即可
在这里插入图片描述

实现的业务展示

1.将写好HTML文件放入pages文件夹中
2.启动服务器,点击xx.bat
在这里插入图片描述

小bug(求大牛解决)

这个bug不会影响运行,就是关闭浏览器时会提示错误信息。
在这里插入图片描述

需要成品的小伙伴可以私聊

制作不宜,要成品的小伙伴,收取1元的支持费。O(∩_∩)O哈哈~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值