1. 程式人生 > >一個簡單的HTTP請求與應答基於socket

一個簡單的HTTP請求與應答基於socket

  tomcat是一個web容器,網路請求基於HTTP,HTTP底層基於socket抽象層,直接上圖(盜的圖)


  我就簡單實現了一個socket來模擬HTTP請求與應答

package Socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by Jackie on 2017/8/2.
 *
 */
public class HttpSocket {

    private static final String OUTPUT = "<html><head><title>Example</title></head><body><p>Worked!!!</p></body></html>";
    private static final String OUTPUT_HEADERS = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html\r\n" +
            "Content-Length: ";
    private static final String OUTPUT_END_OF_HEADERS = "\r\n\r\n";

    public void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(9002);
            boolean isStop = false;

            while (!isStop) {
                Socket socket = serverSocket.accept();
                InputStream in = socket.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
                String line = null;
                while ((line = bufferedReader.readLine())!=null){
                    System.out.println(line);
                    if (line.isEmpty())
                        break;
                }
                sendResponse(socket);
                socket.close();

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sendResponse(Socket socket) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        bw.write(OUTPUT_HEADERS + OUTPUT.length() + OUTPUT_END_OF_HEADERS + OUTPUT);
        bw.flush();
        bw.close();
    }

    public static void main(String[] args){
        new HttpSocket().start();
    }
}

  程式碼的邏輯非常簡單,先是獲得HTTP請求,列印其HTTP的request部分,然後獲得輸出流,按照HTTP中response的響應格式,輸出一個簡單的html。最終的效果就是,在瀏覽器中輸入http://localhost:9002/ 就會返回一個簡單的html頁面,效果如下圖:

  

  其中有一個好玩的現象,當我在瀏覽器敲入localhost:9002的時候,後臺程式打印出來發現瀏覽器傳送了兩次HTTP請求:

GET / HTTP/1.1
Host: localhost:9002
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: Idea-d1657464=19a93302-2aa0-4496-a546-c42fd7726ea0


GET /favicon.ico HTTP/1.1
Host: localhost:9002
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://localhost:9002/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: Idea-d1657464=19a93302-2aa0-4496-a546-c42fd7726ea0

  其中第一次請求使我們比較熟悉的,但是第二次請求我從來沒注意到過,但是看了下請求的格式,好像大概的能明白請求的意圖從第一行就可以看出來請求的是一個icon,我們每次開啟一個網頁的時候,你有沒有注意到每個網頁標籤頁左上角都會有一個icon小圖示。既然遇到了這個有趣的地方,那麼要去實現這個功能,程式改的也比較簡單,當第二次HTTP請求的時候,我們返回一個帶有圖片檔案的Response。程式如下:

package Socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by Jackie on 2017/8/2.
 *
 */
public class HttpSocket {

    private static final String OUTPUT = "<html><head><title>Example</title></head><body><p>Worked!!!</p></body></html>";

    private static final String OUTPUT_HEADERS = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html\r\n" +
            "Content-Length: ";

    private static final String OUTPUT_HEADERS_IOCN = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: image/*\r\n" +
            "Content-Length: ";

    private static final String OUTPUT_END_OF_HEADERS = "\r\n\r\n";

    private static final String Icon_Location = "/Users/macbookpro/Desktop/resizeApi.png";

    public void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(9002);
            boolean isStop = false;
            boolean isIconRequest = false;
            while (!isStop) {
                Socket socket = serverSocket.accept();
                InputStream in = socket.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
                String line = null;
                while ((line = bufferedReader.readLine())!=null){
                    System.out.println(line);
                    if(line.contains("GET /favicon.ico")){
                        sendIcon(Icon_Location , socket);
                        isIconRequest = true;
                    }
                    if (line.isEmpty())
                        break;
                }

                if (!isIconRequest)
                    sendResponse(socket);

                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sendResponse(Socket socket) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        bw.write(OUTPUT_HEADERS + OUTPUT.length() + OUTPUT_END_OF_HEADERS + OUTPUT);
        bw.flush();
        bw.close();
    }

    public void sendIcon(String iconLocation , Socket socket) throws IOException {
        File file = new File(iconLocation);
        if (!file.exists())
            throw new FileNotFoundException();

        BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream fileOut = new BufferedOutputStream(socket.getOutputStream());

        fileOut.write(OUTPUT_HEADERS.getBytes());
        fileOut.write(String.valueOf(file.length()).getBytes());
        fileOut.write(OUTPUT_END_OF_HEADERS.getBytes());

        byte[] buf = new byte[1024];
        int size = 0;
        while ((size = fileIn.read(buf))>0){
            fileOut.write(buf , 0 , size);
        }

        fileOut.flush();
        fileOut.close();
        fileIn.close();
    }

    public static void main(String[] args) throws IOException {
        new HttpSocket().start();
    }
}
  傳送圖片的IO流我選擇了位元組流,因為在以前我嘗試用字元去複製二進位制圖片檔案時,複製完成後打不開了,提示我格式有錯誤,然後我選擇了位元組流就解決了這個問題。最終的返回頁面如圖:

  在標籤的左上角就有了一個github小圖示了》