自己動(dòng)手寫(xiě)HTTP服務(wù)器

這是我的第一篇文章。和眾多程序員一樣,非常希望去分享自己的知識(shí)。但是萬(wàn)事開(kāi)頭難,也可能是我自己水平的問(wèn)題,真是想不到要從什么內(nèi)容開(kāi)始。于是趁著有時(shí)間,就自己搗鼓了一下HTTP服務(wù)器。后來(lái)想想干脆就以自己手寫(xiě)一個(gè)簡(jiǎn)單的HTTP服務(wù)器的教程來(lái)開(kāi)始我自己的寫(xiě)作吧。本文中不足和錯(cuò)誤的地方請(qǐng)各位看客多多指教。下面就開(kāi)始吧。

HTTP簡(jiǎn)介


當(dāng)用戶在瀏覽器中輸入一個(gè)指向特定網(wǎng)頁(yè)的URL地址時(shí),瀏覽器就會(huì)生成一個(gè)HTTP請(qǐng)求,因?yàn)闉g覽器需要將HTTP請(qǐng)求發(fā)送的服務(wù)器,所以這時(shí)瀏覽器會(huì)與服務(wù)器建立TCP連接,當(dāng)TCP可靠連接建立之后,瀏覽器會(huì)將生成的HTTP請(qǐng)求發(fā)送到服務(wù)器端。這時(shí)服務(wù)器程序接收到了信息將要去識(shí)別這個(gè)信息的內(nèi)容。服務(wù)器程序識(shí)別之后是一個(gè)HTTP的請(qǐng)求。就調(diào)用相應(yīng)的服務(wù)程序,經(jīng)過(guò)服務(wù)程序的分析和處理之后服務(wù)器端知道需要返回什么內(nèi)容給瀏覽器了。當(dāng)服務(wù)器返回了內(nèi)容給瀏覽器后,這時(shí)瀏覽器與服務(wù)器之間的數(shù)據(jù)交換完畢,這時(shí)TCP可靠連接就會(huì)斷開(kāi)。如果用戶希望訪問(wèn)新的網(wǎng)頁(yè),瀏覽器就必須再次建立與服務(wù)器的連接了。

httpServer.jpg

開(kāi)始之前我們準(zhǔn)備好工具

  • java環(huán)境(JDK1.7)
  • eclipse
  • chrome瀏覽器

開(kāi)始

使用eclipse建立項(xiàng)目結(jié)構(gòu)如圖
HTTPServer.java為程序代碼
abc.html為需要請(qǐng)求的html頁(yè)面


Paste_Image.png

HTTPServer.java 代碼

package server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class HTTPServer {
    public static void main(String[] args) {
        int port;
        ServerSocket serverSocket;
        try{
            port=Integer.parseInt(args[0]);
        }catch(Exception e){
            System.out.println("port=8080(默認(rèn))");
            port=8080;
            
        }
        
        try {
            serverSocket=new ServerSocket(port);
            System.out.println("服務(wù)器正在監(jiān)聽(tīng)端口:"+serverSocket.getLocalPort());
            while(true){
                Socket socket=serverSocket.accept(); //沒(méi)有時(shí)會(huì)阻塞 程序停在此處
                System.out.println("建立了與客戶的一個(gè)新的TCP連接,該客戶的地址為:"+socket.getInetAddress()+":"+socket.getPort());
                try {
                    service(socket);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        
    }
    
    public static void service(Socket socket) throws Exception{
        InputStream socketIn=socket.getInputStream();
        Thread.sleep(500);   
        int size=socketIn.available();
        byte[] buffer=new byte[size];
        socketIn.read(buffer);
        String request=new String(buffer);
        System.out.println(request);
        //獲取HTTP請(qǐng)求的第一行
        String firstLineOfRequest=request.substring(0,request.indexOf("\r\n"));
        System.out.println("http請(qǐng)求的第一行:"+firstLineOfRequest);
        //解析HTTP請(qǐng)求的第一行
        String[] parts=firstLineOfRequest.split(" ");
        //獲取HTTP請(qǐng)求中的URI
        String uri=parts[1];
        /*決定HTTP響應(yīng)正文的類型*/
        String contentType;
        if(uri.indexOf("html")!=-1||uri.indexOf("htm")!=-1)
            contentType="text/html";
        else if(uri.indexOf("jpg")!=-1||uri.indexOf("jpeg")!=-1)
            contentType="image/jpeg";
        else if(uri.indexOf("gif")!=-1)
            contentType="image/gif";
        else
            contentType="application/octet-stream"; //字節(jié)流類型
        
        /*創(chuàng)建HTTP響應(yīng)結(jié)果*/
        //HTTP響應(yīng)的第一行
        String responseFirstLine="HTTP/1.1 200 OK\r\n";
        
        String responseHeader="Content-type:"+contentType+"\r\n\r\n";
        
        InputStream in=HTTPServer.class.getResourceAsStream("/server"+uri);
        
        /*發(fā)送HTTP響應(yīng)結(jié)果*/
        OutputStream socketOut=socket.getOutputStream();
        
        socketOut.write(responseFirstLine.getBytes());
        socketOut.write(responseHeader.getBytes());
        
        //發(fā)送正文
        int len=0;
        buffer=new byte[128];
        while((len=in.read(buffer))!=-1)
            socketOut.write(buffer,0,len);
        
        Thread.sleep(1000);
        socket.close();
        
    }

}

abc.html

this Html

程序默認(rèn)監(jiān)聽(tīng)端口8080
在一般情況下accept()函數(shù)是阻塞式的,當(dāng)沒(méi)有socket連接的時(shí)候本線程是停在此處的。
當(dāng)有一個(gè)TCP請(qǐng)求建立連接時(shí)這時(shí)服務(wù)器程序的accept()返回一個(gè)Socket對(duì)象。這個(gè)Socket對(duì)象就是在本程序中充當(dāng)客戶端的角色對(duì)象??梢詮拇藗€(gè)對(duì)象中獲取輸入流讀取信息進(jìn)來(lái),也可以通過(guò)輸出流返回信息給客戶端。

這里我們通過(guò)瀏覽器進(jìn)行測(cè)試

將這個(gè)程序跑起來(lái)
我們使用瀏覽器進(jìn)行請(qǐng)求http://localhost:8080/abc.html 請(qǐng)求自己服務(wù)器的abc.html

Paste_Image.png

查看控制臺(tái)打印出來(lái)的內(nèi)容

port=8080(默認(rèn))
服務(wù)器正在監(jiān)聽(tīng)端口:8080
建立了與客戶的一個(gè)新的TCP連接,該客戶的地址為:/0:0:0:0:0:0:0:1:52085
GET /abc.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8


http請(qǐng)求的第一行:GET /abc.html HTTP/1.1

從控制臺(tái)打印結(jié)果中我們可以看到HTTP協(xié)議的大致結(jié)構(gòu)

  1. 請(qǐng)求行
  2. 消息報(bào)頭
  3. 請(qǐng)求正文

請(qǐng)求行中包括了本次HTTP協(xié)議是什么方式,以及uri定位地址在哪里 和一些關(guān)于HTTP規(guī)范版本的信息。

結(jié)語(yǔ)


文章內(nèi)容比較簡(jiǎn)單基礎(chǔ),但這是關(guān)于底層的基礎(chǔ)內(nèi)容。我想這些機(jī)理還是很重要的,需要牢記于心,以便于掌控。這樣才能在碰到問(wèn)題的時(shí)候迅速解決問(wèn)題。

哈哈
謝謝大家閱讀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容