• 模拟web服务器 (小项目) 搭建+部署


     模拟web服务器:可以从浏览器中访问到自己编写的服务器中的资源,将其资源显示在浏览器中。

    技术选型:

    • corejava

      • 线程池 同任务并发执行

      • IO流 传递数据 客户端也会像服务端发送数据, 服务器像客户端发送数据也是流

      • 网络(Socket编程) 通过网络获取客户端的数据以及通过网络给客户端返回数据

    • ubuntu

      • 软件都是部署Linux(ubuntu)

    使用c/s架构,只编写服务器端,客户端由浏览器代替,浏览器向服务器发送一个请求Request,服务器给浏览器返回一个响应Response。

    使用的是HTTP协议。

    项目整体的文件结构:

    应用包appliction,包含启动类

    封装包pojo,包含了两个封装类 ,request类(请求类) response类(响应类)为了防止后序有对象信息的传输,所以提前给两个类实现Serializable接口(序列化、反序列化)。

    工具包util,包含请求的文件类型FileTpye,线程池工具PoolUtil,Socket转换成Request对象,和Request转换成Response对象的工具类SocketTrans,装载着状态码信息的一个接口类 StateCodeEnum。

    所需的文件资源放在了source文件下。

    GET请求方式:
    浏览器向服务器发请求:http://127.0.0.1:80/index.html
    请求数据为:

    //请求行包括:请求方式+空格+/+请求资源+空格+协议版本+会车换行符
    GET /index.html HTTP/1.1 
    //请求方式为GET 请求资源为index.html 协议版本为HTTP/1.1 回车换行符为
    
    //请求头:请求行下的都是请求头 包含客户端的一些基本信息
    Host: 127.0.0.1:80 //客户端的ip和端口 都是以空格隔开
    Connection: keep-alive //客户端的连接状态
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    //客户端的基本信息
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
    //客户端可以接收的数据类型
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Sec-Fetch-User: ?1
    Sec-Fetch-Dest: document
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9

    发送一个GET请求由几部分组成:

    请求行(请求方式,/请求资源名,协议名字/版本号)会车符 换行符
    请求头 key:value 回车符 换行符

    POST请求方式:由Postman软甲配合完成  选择POST方式  输入ip 端口 访问的资源

    http://127.0.0.1:9999/login

    从Body中输入key value参数

    输入结束后连接即可。因为POST方式是向指定的资源提交要被处理的数据 ,查询字符串POST方式是在POST请求的HTTP主体中发送,而GET方式查询字符串是在GET请求的URL中发送,所以使用Postman方式进行模拟。

    Request:需要封装请求行中的请求方式,用来根据不同的请求方式做不同的处理(GET没有请求体,而POST有请求体)、封装请求行中的请求资源,用来根据不同的请求资源发送不同的响应体、封装请求行中的协议版本(协议版本一般固定)、封装请求头(请求头中的内容都是key value值,所以使用一个map进行封装)、封装请求体。

    import java.io.Serializable;
    import java.util.HashMap;
    import java.util.Map;
    
    public class Request implements Serializable{
    
        private static final long serialVersionUID = 1L;
        
        //请求方式
        private String requestMethod;
        //请求资源
        private String requestSource;
        //协议版本
        private String agreement;
        //请求头
        private Map<String, String> requestHead = new HashMap<String, String>();
        //请求体
        private String requestBody;
        
        public String getRequestSource() {
            return requestSource;
        }
        public void setRequestSource(String requestSource) {
            this.requestSource = requestSource;
        }
        
        public String getAgreement() {
            return agreement;
        }
        public void setAgreement(String agreement) {
            this.agreement = agreement;
        }
        public Map<String, String> getRequestHead() {
            return requestHead;
        }
        public void setRequestHead(Map<String, String> requestHead) {
            this.requestHead = requestHead;
        }
        public String getRequestBody() {
            return requestBody;
        }
        public void setRequestBody(String requestBody) {
            this.requestBody = requestBody;
        }
        public String getRequestMethod() {
            return requestMethod;
        }
        public void setRequestMethod(String requestMethod) {
            this.requestMethod = requestMethod;
        }
        @Override
        public String toString() {
            return "Request [requestMethod=" + requestMethod + ", requestSource=" + requestSource + ", agreement="
                    + agreement + ", requestHead=" + requestHead + ", requestBody=" + requestBody + "]";
        }
        
        
    }

    Response:和请求对应,响应有响应行、响应头、响应体。响应行中有版本信息,状态码信息。响应头中包含的是key value类型的数据,用一个map封装,响应体用Object类型修饰,因为不知道响应的是什么类型的数据。

    import java.io.Serializable;
    import java.util.HashMap;
    import java.util.Map;
    
    import com.briup.server.util.StateCodeEnum;
    
    public class Response implements Serializable{
    
        private static final long serialVersionUID = 1L;
        //响应行的版本信息
        private String agreement = "HTTP/1.1";
        //响应行的状态码信息
        private StateCodeEnum stateCode;
        //key值代表响应头的key value代表响应头的value
        private Map<String, String> responseHeader = new HashMap<String, String>();
        //响应体
        private Object responseBody;
        public String getAgreement() {
            return agreement;
        }
        public void setAgreement(String agreement) {
            this.agreement = agreement;
        }
        public StateCodeEnum getStateCode() {
            return stateCode;
        }
        public void setStateCode(StateCodeEnum stateCode) {
            this.stateCode = stateCode;
        }
        public Map<String, String> getResponseHeader() {
            return responseHeader;
        }
        public void setResponseHeader(Map<String, String> responseHeader) {
            this.responseHeader = responseHeader;
        }
        public Object getResponseBody() {
            return responseBody;
        }
        public void setResponseBody(Object responseBody) {
            this.responseBody = responseBody;
        }
        @Override
        public String toString() {
            return "Response [agreement=" + agreement + ", stateCode=" + stateCode + ", responseHeader=" + responseHeader
                    + ", responseBody=" + responseBody + "]";
        }
        
    }

    FileType:响应头里的各种信息,根据key值得到响应的value值进行对响应头信息的封装。

    import java.util.HashMap;
    import java.util.Map;
    
    public class FileType {
    public static final Map<String, String> TYPE;
    static {
        TYPE=new HashMap<>();
        TYPE.put("txt","text/html;charset=tf-8");
        TYPE.put("html","text/html;charset=tf-8");
        TYPE.put("jpg","image/jpeg;charset=tf-8");
        TYPE.put("jpeg","images/jpeg;charset=tf-8");
        TYPE.put("png","image/png;charset=tf-8");
        TYPE.put("mp4","video/mpeg4;charset=tf-8");
        TYPE.put("pdf","application/pdf;charset=tf-8");
        
    }
    }

    PoolUtil:

    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.net.Socket;
    import java.util.Map.Entry;
    import java.util.Set;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import javax.print.attribute.ResolutionSyntax;
    
    import com.briup.server.pojo.Request;
    import com.briup.server.pojo.Response;
    
    public class PoolUtil {
    
        public static final ExecutorService pool;
        static {
            pool = Executors.newCachedThreadPool();
        }
        private static void writeToBrowser(Socket socket,Response response) throws IOException {
            BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
            StringBuilder builder = new StringBuilder();
            builder.append(response.getAgreement()).append(" ").append(response.getStateCode().getCode()).append(" ").append(response.getStateCode().getMsg()).append("
    ");
            bos.write(builder.toString().getBytes());
            //删除builder里面的数据 从0开始到最后
            builder.delete(0, builder.length());
            
            Set<Entry<String,String>> set = response.getResponseHeader().entrySet();
            for (Entry<String, String> entry : set) {
                builder.append(entry.getKey()).append(" ").append(entry.getValue()).append("
    ");
            }
            //响应头
            bos.write(builder.toString().getBytes());
            //空行
            bos.write("
    ".getBytes());
            //响应体
            //如果是文件就写回文件,
            if(response.getResponseBody() instanceof File) {
                //将资源从本地磁盘读取 
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream((File)response.getResponseBody()));
                int count=-1;
                byte []bytes = new byte[1024];
                while((count=bis.read(bytes))!=-1) {
                    bos.write(bytes, 0, count);
                }
                bos.flush();
                socket.shutdownOutput();
                
            }
            
        }
        private static void doGet(Request request,Response response,Socket socket) throws IOException {
            //通过request对象获取requestBody
            String string = request.getRequestBody();
            if (string!=null &&!"".equals(string)) {
                String[] infos = string.split("[&]");
                if ("username=123".equals(infos[0]) && "password=123".equals(infos[1])) {
                    response.setStateCode(StateCodeEnum.OK);
                    response.setResponseBody(new File("source/success.html"));
    
                } else {
                    response.setStateCode(StateCodeEnum.LOGIN_FAIL);
                    response.setResponseBody(new File("source/fail.html"));
                } 
            }
            //将数据写回给浏览器
            writeToBrowser(socket,response);
        }
       private static void doPost(Request request,Response response,Socket socket) throws IOException{
           doGet(request, response, socket);
           writeToBrowser(socket,response);
        }
    public static void service( Request request,Response response,Socket socket) {
        pool.execute(()->{
            
            try {
                if(("GET").equals(request.getRequestMethod())) {
                    PoolUtil.doGet(request, response, socket);
                }
                else if(("POST").equals(request.getRequestMethod())){
                    PoolUtil.doPost(request, response, socket);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        });
        
        
    }
    }

    SocketTrans:

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    import com.briup.server.pojo.Request;
    import com.briup.server.pojo.Response;
    
    public class SocketTrans {
    public static  Request socketToRequest(Socket socket) throws IOException  {
        if(socket ==null) {
            throw new RuntimeException("参数为空");
            
        }
        Request request = new Request();
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //得到请求行
        String line = reader.readLine();
        String[] split = line.split(" ");
        request.setAgreement(split[2]);
        request.setRequestMethod(split[0]);
        String[] split2 = split[1].split("[?]");
        request.setRequestSource(split2[0]);
        //数组长度大于1 代表后面有数据
        if(split2.length>1) {
            request.setRequestBody(split2[1]);
        }
        //得到请求头
    
        while((line = reader.readLine())!=null&&!"".equals(line)) {
            String[] herderInfo = line.split(":");
            request.getRequestHead().put(herderInfo[0], herderInfo[1].trim());
        }
        //判断请求头里有没有Content-Length,有则代表有请求体
        if (request.getRequestHead().containsKey("Content-Length")) {
             request.setRequestBody(reader.readLine());
        }
        //封装完后 input流使用完毕了
        socket.shutdownInput();
        return request;
        
    }
    public static Response getResponse(Request request) {
        
        if(request==null) {
            throw new RuntimeException("参数为空");
        }
        Response response = new Response();
        //设置协议版本
        response.setAgreement(request.getAgreement());
        //判断资源是否存在
        String fileName = request.getRequestSource();
        File file = new File("source",fileName);
        //设置响应体
        //得到请求资源的后缀名
        fileName= fileName.substring(fileName.lastIndexOf(".")+1);
        //文件存在后缀名正确 不存在后缀名可能正确 也可能不正确
        if(file.exists()) {
            response.setStateCode(StateCodeEnum.OK);
            response.setResponseBody(file);
            response.getResponseHeader().put("Content-Type:", FileType.TYPE.get(fileName));
        }else {
            response.setStateCode(StateCodeEnum.NOT_FOUND);
            response.setResponseBody(new File("source/404.html"));
            response.getResponseHeader().put("Content-Type:", FileType.TYPE.get("html"));
        }
        return response;
    }
    }

    StateCodeEnum :

    public enum StateCodeEnum {
        OK(200,"OK"),NOT_FOUND(404,"NOT_FOUND"),LOGIN_FAIL(401,"UNAUTHORIZED");
    
    private int code;
    private String msg;
    private StateCodeEnum(int code,String msg) {
        this.code = code;
        this.msg = msg;
        
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    }

    项目的代码基本结束,可以实现从浏览器端向服务器端。

    将项目打包,右击项目,选择Export ,选择jarFile,选择要打包的地点。

    将项目部署到Ubuntu的一个镜像中,方便测试,

    在Ubuntu中,点击文件,点击打开,选中server.ovf(镜像文件),将其导入。

    为方便远程操作服务器,使用一个远程连接工具srct814-x64软件。

    在Ubuntu中查看ip  使用ifconfig  ,在srct814-x64中添加此ip并连接。

    在srct-x64中安装jdk,配置环境变量,使其具备Java环境。

    在srct-x64中点击file,打开connect SFTP session ,将打包好的server.jar文件和资源文件夹source拖进这个窗口。

    在该控制台窗口中整理文件,将source和jar包放在一个文件夹下,此时项目就算部署好了,在该窗口运行jar包

    Java -jar server.jar

    打开浏览器输入服务器的ip和端口号和你要访问的资源就可以访问到了。

    如我的Ubuntu中ip是192.168.235.129 端口号为9999

    在浏览器中输入 192.168.235.129:9999/1.png  

    就可以访问到服务器中的1.png文件  

  • 相关阅读:
    六、Redis主从复制 
    五、AOF持久化
    四、RDB持久化
    三、数据类型
    二、redis的配置文件介绍
    第八章、堆
    九、补充
    八、Filter
    七、监听器和国际化
    六、JDBC
  • 原文地址:https://www.cnblogs.com/jamers-rz/p/13662825.html
Copyright © 2020-2023  润新知