老规矩,先给个需求:浏览器向服务端提交一个GET请求,服务端向服务器响应一张图片,并展示在浏览器上。


在编写程序之前,首先分析一下浏览器做了什么,服务器又要做什么。
1、浏览器向服务器提交了一个GET请求的请求报文,请求的url是图片的路径。
2、服务器读到请求之后,拆解请求报文的首行,获得请求报文中的url,找到对应的图片响应给浏览器,并展示出来。

至于怎么用HTTP手写一个服务器可以参考我先前写的文章:
手写一个简易的HTTP服务器。
https://segmentfault.com/a/1190000043443755

1、浏览器向服务器提交了一个GET请求的请求报文,请求的url是图片的路径。
这里的填写自己服务器所存储图片的路径
localhost:12345/getPicture/src/webapp/1.png

2、服务器读到请求之后,拆解请求报文的首行,获得请求报文中的url,找到对应的图片。

public class Server {
    /*
    * 抛出异常
    * */
    private  static  class DemoApplication extends RuntimeException{
        public DemoApplication() {
            super();
        }

        public DemoApplication(String message) {
            super(message);
        }

        public DemoApplication(String message, Throwable cause) {
            super(message, cause);
        }

        public DemoApplication(Throwable cause) {
            super(cause);
        }

        protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    /*
    * 启动监听端口
    * */
    public Server(int port) throws IOException {
        if (port < 1 || port >65535){
            throw new DemoApplication("端口错误");
        }
        ServerSocket serverSocket = new ServerSocket(port);
        ExecutorService pool = Executors.newFixedThreadPool(50);
        System.out.println("已经启动,开始监听端口"+port);
        while (true){
            Socket clientSocket = serverSocket.accept();
            if (!clientSocket.isClosed()){
                //首先服务端输出内容到客户端的输入流
                Runnable r = () -> {
                    try {
                        acceptToClient(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                };
                pool.submit(r);
            }
        }
    }


    private void acceptToClient(Socket clientSocket) throws IOException {
        //使用 InputStream 来读取浏览器的请求报文
        InputStream clientIn = clientSocket.getInputStream();
        //使用 BufferedReader 将InputStream强转成 BufferedReader。(InputStream读取到的是字节,BufferedReader读取到的是字符,我们需要的是字符,所以强转成BufferedReader)
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
        System.out.println("进入服务器");
        OutputStream clientOut = clientSocket.getOutputStream();
        String content = "";
        String cookieName = "";
        String cookieValue = "";
        String cookieTime = "";
        String type = "";

        /*
        * 读取第一行要进行特殊处理,从中获取到请求报文中的 方法 和 URL
        * */
        String firstLine = bufferedReader.readLine();
        System.out.println("读取到的第一行是"+firstLine);
        String requestUri = firstLine.split(" ")[1];
        System.out.println("读取数组中的URL"+requestUri);
        String method = firstLine.split(" ")[0];
        System.out.println("这次请求的方法是"+method);
        int length = 0;
        
        /*
        * 读取图片
        * */
        System.out.println(requestUri.split("/")[1]);
        if(requestUri.split("/")[1].equals("getPicture"))
            if(method.equals("GET")){
                System.out.println("进入update方法");
                int a = 1;
                type ="bytes";
                String path = requestUri.split("/")[3];//webapp
                System.out.println(path);
                if(!path.equals("webapp")){
                    System.out.println("访问的图片不存在");
                    clientOut.close();
                    return;
                }
                //lastIndexOf 返回此字符串中指定子字符串的最后一次出现的索引。空字符串""的最后一次出现被认为出现在索引值
                //substring 返回子字符串,子字符串从索引处开始,扩展到末尾              
                String fileName = requestUri.substring(requestUri.lastIndexOf("/") + 1);//1.png
                System.out.println(fileName);             
                InputStream inputStream = new FileInputStream("src/webapp/" +fileName);           
                byte[] bytes = new byte[1024];
                int len = 0;
                clientOut.write("HTTP/1.1 200 OK\r\n".getBytes());//响应成功的响应头
                clientOut.write("content-type:image/jpeg\r\n".getBytes());//告诉浏览器,我这边返回的是一个图片
                clientOut.write("\r\n".getBytes());
                while ((len = inputStream.read(bytes)) != -1) {
                    clientOut.write(bytes,0,len);//图片的内容
                }
                System.out.println("执行成功");
            }        

        //判断是否带路径搜索,没带路径的默认搜索ajax-demo
        String resourcePath = requestUri.equals("/") ? "ajax-demo" : requestUri.substring(1);
        System.out.println(resourcePath);
        //打印获取的html
        String a = "";
        String htmlStr = "";
        try {
            BufferedReader in = new BufferedReader(new FileReader("src/webapp/"+resourcePath +".html"));
            while ((a = in.readLine()) != null) {
                htmlStr  =htmlStr + "\n" +a;
            }
        } catch (IOException e) {
            System.out.print("错误");
        }
        //读取资源的内容
        System.out.println(htmlStr);
        //找不到资源直接返回404
        if (htmlStr == null){
            writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,404,"Not Found","<h1>404 FILE NOT FOUND</h1>",type,length);
            return;
        }
        content = htmlStr;
        writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content,type,length);
    }
    /*
    * 封装响应报头(报文)
    * */
    private void  writeToClient(OutputStream clientOut, String ContentType ,String cookieName,String cookieValue,String cookieTime, int responseCode, String responseDes, String content,String type,int length) throws IOException {
        clientOut.write(("HTTP/1.1 "+responseCode+ " " +responseDes+ "\r\n").getBytes());
        clientOut.write(("Date: " + (new Date()).toString() + "\r\n").getBytes());
        clientOut.write(("Content-Type: "+ ContentType +"; charset=UTF-8\r\n").getBytes());
        clientOut.write(("Set-Cookie: "+cookieName+"="+cookieValue+";"+"max-age="+cookieTime+"\r\n").getBytes());
        clientOut.write(("Accept-Ranges: "+type+"\r\n").getBytes());
        clientOut.write(("Content-Length: "+length+"\r\n").getBytes());
        clientOut.write("\r\n".getBytes());//空行
        clientOut.write(content.getBytes());
        clientOut.flush();
        clientOut.close();
    }
    public static void main(String[] args) throws IOException {
        new Server(12345);
    }

}

执行试试!

执行成功!
那能不能每隔提交同一个url来请求服务器,获取到新的图片。
咱来试试。

也就是说,每刷新一次,获取图片的路径就要换一个,但要求提交的url是同一个。没错,通过更改服务器获取到的图片的路径来改变获取到的图片。

public class Server {
    //这里多定义了一个全局变量
    int i =1 ;//<<<<<<<这里多定义了一个全局变量

    /*
    * 抛出异常
    * */

    private  static  class DemoApplication extends RuntimeException{
        public DemoApplication() {
            super();
        }

        public DemoApplication(String message) {
            super(message);
        }

        public DemoApplication(String message, Throwable cause) {
            super(message, cause);
        }

        public DemoApplication(Throwable cause) {
            super(cause);
        }

        protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    /*
    * 启动监听端口
    * */
    public Server(int port) throws IOException {
        if (port < 1 || port >65535){
            throw new DemoApplication("端口错误");
        }
        ServerSocket serverSocket = new ServerSocket(port);
        ExecutorService pool = Executors.newFixedThreadPool(50);
        System.out.println("已经启动,开始监听端口"+port);
        while (true){
            Socket clientSocket = serverSocket.accept();
            if (!clientSocket.isClosed()){
                //首先服务端输出内容到客户端的输入流
                Runnable r = () -> {
                    try {
                        acceptToClient(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                };
                pool.submit(r);
            }
        }
    }
    private void acceptToClient(Socket clientSocket) throws IOException {
        //使用 InputStream 来读取浏览器的请求报文
        InputStream clientIn = clientSocket.getInputStream();
        //使用 BufferedReader 将InputStream强转成 BufferedReader。(InputStream读取到的是字节,BufferedReader读取到的是字符,我们需要的是字符,所以强转成BufferedReader)
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
        System.out.println("进入服务器");
        OutputStream clientOut = clientSocket.getOutputStream();
        String content = "";
        String cookieName = "";
        String cookieValue = "";
        String cookieTime = "";
        String type = "";
        /*
        * 读取第一行要进行特殊处理,从中获取到请求报文中的 方法 和 URL
        * */
        String firstLine = bufferedReader.readLine();
        System.out.println("读取到的第一行是"+firstLine);
        String requestUri = firstLine.split(" ")[1];
        System.out.println("读取数组中的URL"+requestUri);
        String method = firstLine.split(" ")[0];
        System.out.println("这次请求的方法是"+method);
        int length = 0;
        /*
        * 读取图片
        * */
        System.out.println(requestUri.split("/")[1]);
        if(requestUri.split("/")[1].equals("getPicture"))
            if(method.equals("GET")){
                System.out.println("进入update方法");
                int a = 1;
                type ="bytes";
                String path = requestUri.split("/")[3];
                System.out.println(path);
                if(!path.equals("webapp")){
                    System.out.println("访问的图片不存在");
                    clientOut.close();
                    return;
                }
                //lastIndexOf 返回此字符串中指定子字符串的最后一次出现的索引。空字符串""的最后一次出现被认为出现在索引值
                //substring 返回子字符串,子字符串从索引处开始,扩展到末尾

                String fileName = requestUri.substring(requestUri.lastIndexOf("/") + 2);//.png 这里将索引的位置后移了一位
                System.out.println(fileName);
                
                i++;
                //定义一共有多少张图片,到达数量返回到第一张
                if(i>5){
                    i = 1;
                }

                InputStream inputStream = new FileInputStream("src/webapp/"+i +fileName);

                byte[] bytes = new byte[1024];
                int len = 0;
                clientOut.write("HTTP/1.1 200 OK\r\n".getBytes());
                clientOut.write("content-type:image/jpeg\r\n".getBytes());//告诉浏览器,我这边返回的是一个图片
                clientOut.write("\r\n".getBytes());
                while ((len = inputStream.read(bytes)) != -1) {
                    clientOut.write(bytes,0,len);
                }
                System.out.println("执行成功");
            }
        String resourcePath = requestUri.equals("/") ? "ajax-demo" : requestUri.substring(1);
        System.out.println(resourcePath);
        //打印获取的html
        String a = "";
        String htmlStr = "";
        try {
            BufferedReader in = new BufferedReader(new FileReader("src/webapp/"+resourcePath +".html"));
            while ((a = in.readLine()) != null) {
                htmlStr  =htmlStr + "\n" +a;
            }
        } catch (IOException e) {
            System.out.print("错误");
        }
        //读取资源的内容
        System.out.println(htmlStr);
        //找不到资源直接返回404
        if (htmlStr == null){
            writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,404,"Not Found","<h1>404 FILE NOT FOUND</h1>",type,length);
            return;
        }
        content = htmlStr;
        writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content,type,length);
    }
    /*
    * 封装响应报头(报文)
    * */
    private void  writeToClient(OutputStream clientOut, String ContentType ,String cookieName,String cookieValue,String cookieTime, int responseCode, String responseDes, String content,String type,int length) throws IOException {
        clientOut.write(("HTTP/1.1 "+responseCode+ " " +responseDes+ "\r\n").getBytes());
        clientOut.write(("Date: " + (new Date()).toString() + "\r\n").getBytes());
        clientOut.write(("Content-Type: "+ ContentType +"; charset=UTF-8\r\n").getBytes());
        clientOut.write(("Set-Cookie: "+cookieName+"="+cookieValue+";"+"max-age="+cookieTime+"\r\n").getBytes());
        clientOut.write(("Accept-Ranges: "+type+"\r\n").getBytes());
        clientOut.write(("Content-Length: "+length+"\r\n").getBytes());
        clientOut.write("\r\n".getBytes());//空行
        clientOut.write(content.getBytes());
        clientOut.flush();
        clientOut.close();
    }
    public static void main(String[] args) throws IOException {
        new Server(12345);
    }
}

同一个url请求不同的图片也完成了,那能不能每隔10秒钟,同一个url才能请求到不同的图片。
隔一段时间图片的路径才进行变更,这时就可以用到 Spring Boot的定时任务:@Scheduled
表示每隔多久执行一次方法。如果@Scheduled(fixedDelay = 5000),那就是每隔5秒执行一次方法。我们来试试。

要想成功启用这个注解,需要Spring Boot的依赖注入。所以程序的启动入口就不能是Server的main()方法了,我们需要重新定义一个类作为程序的启动入口。

1、定义一个Application类作为程序的启动入口。

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

@EnableScheduling 可确保创建后台任务执行程序(用来开启定时任务的)

2、再写一个类,作为定时任务

@Component
public class AddMethod {
    //定义一个成员变量server
    private Server server;

    {
        try {
            server = new Server(12345);
            System.out.println(server);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    @Scheduled(fixedDelay = 10000)
    public void add(){
        //调用server类的add()方法
        server.add();
    }

}

3、在Server类中添加add方法


public class Server {
    int i =1 ;


    /*
    * 抛出异常
    * */
    int add(){
        i++;
        if(i>5){
            i=1;
        }
        System.out.println("i="+i);
        return i;
    }
    private  static  class DemoApplication extends RuntimeException{
        public DemoApplication() {
            super();
        }

        public DemoApplication(String message) {
            super(message);
        }

        public DemoApplication(String message, Throwable cause) {
            super(message, cause);
        }

        public DemoApplication(Throwable cause) {
            super(cause);
        }

        protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    /*
    * 启动监听端口
    * */
    public Server(int port) throws IOException {
        if (port < 1 || port >65535){
            throw new DemoApplication("端口错误");
        }
        ServerSocket serverSocket = new ServerSocket(port);
        ExecutorService pool = Executors.newFixedThreadPool(50);
        System.out.println("已经启动,开始监听端口"+port);
        while (true){
            Socket clientSocket = serverSocket.accept();
            if (!clientSocket.isClosed()){
                //首先服务端输出内容到客户端的输入流
                Runnable r = () -> {
                    try {
                        acceptToClient(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                };
                pool.submit(r);
            }
        }
    }

    private void acceptToClient(Socket clientSocket) throws IOException {
        //使用 InputStream 来读取浏览器的请求报文
        InputStream clientIn = clientSocket.getInputStream();
        //使用 BufferedReader 将InputStream强转成 BufferedReader。(InputStream读取到的是字节,BufferedReader读取到的是字符,我们需要的是字符,所以强转成BufferedReader)
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
        System.out.println("进入服务器");
        OutputStream clientOut = clientSocket.getOutputStream();
        String content = "";
        String cookieName = "";
        String cookieValue = "";
        String cookieTime = "";
        String type = "";

        /*
         * 读取第一行要进行特殊处理,从中获取到请求报文中的 方法 和 URL
         * */
        String firstLine = bufferedReader.readLine();
        System.out.println("读取到的第一行是"+firstLine);
        String requestUri = firstLine.split(" ")[1];
        System.out.println("读取数组中的URL"+requestUri);
        String method = firstLine.split(" ")[0];
        System.out.println("这次请求的方法是"+method);
        int length = 0;


        /*
         * 读取图片
         * */
        System.out.println(requestUri.split("/")[1]);
        if(requestUri.split("/")[1].equals("getPicture"))
            if(method.equals("GET")){
                System.out.println("进入update方法");
                int a = 1;
                type ="bytes";
                String path = requestUri.split("/")[3];
                System.out.println(path);
                if(!path.equals("webapp")){
                    System.out.println("访问的图片不存在");
                    clientOut.close();
                    return;
                }
                //lastIndexOf 返回此字符串中指定子字符串的最后一次出现的索引。空字符串""的最后一次出现被认为出现在索引值
                //substring 返回子字符串,子字符串从索引处开始,扩展到末尾
                String fileName = requestUri.substring(requestUri.lastIndexOf("/") + 2);//1.png
                System.out.println(fileName);

                System.out.println(i);
                InputStream inputStream = new FileInputStream("src/webapp/"+i +fileName);
                System.out.println("src/webapp/"+ i +fileName);
                byte[] bytes = new byte[1024];
                int len = 0;
                clientOut.write("HTTP/1.1 200 OK\r\n".getBytes());
                clientOut.write("content-type:image/jpeg\r\n".getBytes());//告诉浏览器,我这边返回的是一个图片
                clientOut.write("\r\n".getBytes());
                while ((len = inputStream.read(bytes)) != -1) {
                    clientOut.write(bytes,0,len);
                }
                System.out.println("执行成功");
            }

        
        String resourcePath = requestUri.equals("/") ? "ajax-demo" : requestUri.substring(1);
        System.out.println(resourcePath);
        //打印获取的html
        String a = "";
        String htmlStr = "";
        try {
            BufferedReader in = new BufferedReader(new FileReader("src/webapp/"+resourcePath +".html"));
            while ((a = in.readLine()) != null) {
                htmlStr  =htmlStr + "\n" +a;
            }
        } catch (IOException e) {
            System.out.print("错误");
        }
        //读取资源的内容
        System.out.println(htmlStr);
        //找不到资源直接返回404
        if (htmlStr == null){
            writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,404,"Not Found","<h1>404 FILE NOT FOUND</h1>",type,length);
            return;
        }
        content = htmlStr;
        writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content,type,length);
    }
    /*
    * 封装响应报头(报文)
    * */
    private void  writeToClient(OutputStream clientOut, String ContentType ,String cookieName,String cookieValue,String cookieTime, int responseCode, String responseDes, String content,String type,int length) throws IOException {
        clientOut.write(("HTTP/1.1 "+responseCode+ " " +responseDes+ "\r\n").getBytes());
        clientOut.write(("Date: " + (new Date()).toString() + "\r\n").getBytes());
        clientOut.write(("Content-Type: "+ ContentType +"; charset=UTF-8\r\n").getBytes());
        clientOut.write(("Set-Cookie: "+cookieName+"="+cookieValue+";"+"max-age="+cookieTime+"\r\n").getBytes());
        clientOut.write(("Accept-Ranges: "+type+"\r\n").getBytes());
        clientOut.write(("Content-Length: "+length+"\r\n").getBytes());
        clientOut.write("\r\n".getBytes());//空行
        clientOut.write(content.getBytes());
        clientOut.flush();
        clientOut.close();
    }
}

执行看看,嚯,没效果。debug一下,看看发生了什么。
程序卡在 server = new Server(12345); 没有继续往下执行了,这是为啥?

 /*
    * 启动监听端口
    * */
    public Server(int port) throws IOException {
        if (port < 1 || port >65535){
            throw new DemoApplication("端口错误");
        }
        ServerSocket serverSocket = new ServerSocket(port);
        ExecutorService pool = Executors.newFixedThreadPool(50);
        System.out.println("已经启动,开始监听端口"+port);
        while (true){
            Socket clientSocket = serverSocket.accept();
            if (!clientSocket.isClosed()){
                //首先服务端输出内容到客户端的输入流
                Runnable r = () -> {
                    try {
                        acceptToClient(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                };
                pool.submit(r);
            }
        }
    }

因为为了监听浏览器发出的请求,在Server()方法中,我写了一个死循环来确保接收浏览的连接,所以这里要做出改动。

public Server(int port) throws IOException {
        if (port < 1 || port >65535){
            throw new DemoApplication("端口错误");
        }
        ServerSocket serverSocket = new ServerSocket(port);
        ExecutorService pool = Executors.newFixedThreadPool(50);
        System.out.println("已经启动,开始监听端口"+port);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Socket clientSocket = serverSocket.accept();
                        if (!clientSocket.isClosed()) {
                            //首先服务端输出内容到客户端的输入流
                            Runnable r = () -> {
                                try {
                                    acceptToClient(clientSocket);
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                }
                            };
                            pool.submit(r);
                        }} catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();
    }

定义一个新的线程,就能解决这个问题。
再次执行,就能看到每隔10秒刷新,浏览就能收到请求到一张不一样的图片。


爱摇头的电风扇
7 声望2 粉丝