程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java網絡編程從入門到精通(22):實現HTTP模擬器

Java網絡編程從入門到精通(22):實現HTTP模擬器

編輯:關於JAVA

在討論HTTP協議的具體請求和響應頭字段之前,讓我們先來利用以前所學的知識來實現一個HTTP模擬器。所謂HTTP模擬器就是可以在用戶輸入HTTP的請求消息後,由這個模擬器將HTTP請求發送給相應的服務器,再接收服務器的響應消息。這個HTTP模擬器有幾下特點:

1.可以手工輸入HTTP請求,並向服務器發送。

2.接收服務器的響應消息。

3.消息頭和實體內容分段顯示,也就是說,並不是象Telnet等客戶端一樣將HTTP響

應消息全部顯示,而是先顯示消息頭,然後由用戶決定是否顯示實體內容。

4.集中發送請求。這個HTTP模擬器和Telnet不同的是,並不是一開始就連接服務器,

而是將域名、端口以及HTTP請求消息都輸完後,才連接服務器,並將這些請求發送給服務器。這樣做的可以預防服務器提前關閉網絡連接的現象。

5.可以循環做上述的操作。

從以上的描述看,要實現這個HTTP模擬器需要以下五步:

1.建立一個大循環,在循環內部是一個請求/響應對。這樣就可以向服務器發送多次請求/響應以了。下面的四步都是被包括在循環內部的。

2.從控制台讀取域名和端口,這個功能可以由readHostAndPort(...)來完成。

3.從控制台讀取HTTP請求消息,這個功能由readHttpRequest(...)來完成。

4.向服務器發送HTTP請求消息,這個功能由sendHttpRequest()來完成。

5.讀取服務器回送的HTTP響應消息,這個功能由readHttpResponse(...)來完成。

下面我們就來逐步實現這五步:

一、建立一個大循環

在建立這個循環之前,先建立一個中叫HttpSimulator的類,並在這個類中定義一個run方法用來運行這個程序。實現代碼如下:

001  package http;
002
003  import java.net.*;
004  import java.io.*;
005
006  public class HttpSimulator
007  {
008      private Socket socket;
009      private int port = 80;
010      private String host = "localhost";
011      private String request = ""; // HTTP請求消息
012      private boolean isPost, isHead;
013
014      public void run() throws Exception
015      {
016          BufferedReader reader = new BufferedReader(new InputStreamReader(
017                  System.in));
018          while (true)  // 開始大循環
019          {
020              try
021              {
022                  if (!readHostAndPort(reader))
023                      break;
024                  readHttpRequest(reader);
025                  sendHttpRequest();
026                  readHttpResponse(reader);
027              }
028              catch (Exception e)
029              {
030                  System.out.println("err:" + e.getMessage());
031              }
032          }
033      }
034      public static void main(String[] args) throws Exception
035      {
036          new HttpSimulator().run();
037      }
038  }

從上面的代碼可以看出,第022、024、025和026分別調用了上述的四個方法。這些方法的具體實現將在後面討論。上面的代碼除了調用這四個核心方法外,還做了一些准備工作。在008至012行定義了一些以後要用到的變量。在016和017行使用控制台的輸入流建立了BufferedReader對象,通過這個對象,可以直接從控制台讀取字符串,而不是一個個地字節。

二、readHostAndPort(...)方法的實現

這個方法的主要功能是從控制台讀取域名和端口。域名和端口通過":"隔開,":"和域名以及端口之間不能有空格。當從控制台讀取一個"q"時,這個函數返回false,表示程序可以退出了,否則返回true,表示輸入的域名和端口是正確的。這個方法的實現代碼如下:

001  private boolean readHostAndPort(BufferedReader consoleReader)
002          throws Exception
003  {
004      System.out.print("host:port>");
005      String[] ss = null;
006      String s = consoleReader.readLine();
007      if (s.equals("q"))
008          return false;
009      else
010      {
011          ss = s.split("[:]");
012          if (!ss[0].equals(""))
013              host = ss[0];
014          if (ss.length > 1)
015              port = Integer.parseInt(ss[1]);
016          System.out.println(host + ":" + String.valueOf(port));
017          return true;
018      }
019  }

第001行:這個方法有一個BufferedReader類型的參數,這個參數的值就是在HttpSimulator.java中的第016和017行根據控制台輸入流建立的BufferedReader對象。

第 004 行:這輸出HTTP模擬器的控制符,就象Windows的控制台的"C:">"一樣。

第 006 行:從控制台讀取一行字符串。

第 011 行:通過字符串的split方法和響應的正則表示式("[:]")將域名和端口分開。域名的默認值是localhost,端口的默認值是80。

三、readHttpRequest(...)方法的實現

這個方法的主要功能是從控制台讀取HTTP請求消息,如果輸入一個空行,表示請求消息頭已經輸完;如果使用的是POST方法,還要輸入POST請求的實體內容。這個方法的實現代碼如下:

001  private void readHttpRequest(BufferedReader consoleReader) 
002          throws Exception
003  {
004      System.out.println("請輸入HTTP請求:");
005      String s = consoleReader.readLine();
006      request = s + "\r\n";
007      boolean isPost = s.substring(0, 4).equals("POST");
008      boolean isHead = s.substring(0, 4).equals("HEAD");
009      while (!(s = consoleReader.readLine()).equals(""))
010          request = request + s + "\r\n";
011      request = request + "\r\n";
012      if (isPost)
013      {
014          System.out.println("請輸入POST方法的內容:");
015          s = consoleReader.readLine();
016          request = request + s;
017      }
018  }

第 005 行:讀入HTTP請求消息的第一行。

第 007、008行:確定所輸入的請求方法是不是POST和HEAD。

第 009、010行:讀入HTTP請求消息的其余行。

第012、017行:如果HTTP請求使用的是POST方法,要求用戶繼續輸入HTTP請求的實體內容。

四、sendHttpRequest()方法的實現

這個方法的功能是將request變量中的HTTP請求消息發送到服務器。下面是這個方法的實現代碼:

001      private void sendHttpRequest() throws Exception
002      {
003          socket = new Socket();
004          socket.setSoTimeout(10 * 1000);
005          System.out.println("正在連接服務器");
006          socket.connect(new InetSocketAddress(host, port), 10 * 1000);
007          System.out.println("服務器連接成功!");
008          OutputStream out = socket.getOutputStream();
009          OutputStreamWriter writer = new OutputStreamWriter(out);
010          writer.write(request);
011          writer.flush();
012      }

第004行:設置讀取數據超時為10秒。

第006行:連接服務器,並設置連接超時為10秒。

五、readHttpResponse(...)方法的實現

這個方法的主要功能是從服務器讀取返回的響應消息。首先讀取了響應消息頭,然後要求用戶輸入Y或N以確定是否顯示響應消息的實體內容。這個程序之所以這樣做,主要有兩個原因:

(1) 為了研究HTTP協議。

(2) 由於本程序是以字符串形式顯示響應消息的,因此,如果用戶請求了一個二進制Web資源,如一個rar文件,那麼實體內容將會顯示亂碼。所以在顯示完響應消息頭後由用戶決定是否顯示實體內容。

這個方法的實現代碼如下:

001  private void readHttpResponse(BufferedReader consoleReader)
002  {
003      String s = "";
004      try
005      {
006          InputStream in = socket.getInputStream();
007          InputStreamReader inReader = new InputStreamReader(in);
008          BufferedReader socketReader = new BufferedReader(inReader);
009          System.out.println("---------HTTP頭---------");
010          boolean b = true; // true: 未讀取消息頭 false: 已經讀取消息頭
011          while ((s = socketReader.readLine()) != null)
012          {
013              if (s.equals("") && b == true && !isHead)
014              {
015                  System.out.println("------------------------");
016                  b = false;
017                  System.out.print("是否顯示HTTP的內容(Y/N):");
018                  String choice = consoleReader.readLine();
019                  if (choice.equals("Y") || choice.equals("y"))
020                  {
021                      System.out.println("---------HTTP內容---------");
022                      continue;
023                  }
024                  else
025                      break;
026              }
027              else
028                  System.out.println(s);
029          }
030      }
031      catch (Exception e)
032      {
033          System.out.println("err:" + e.getMessage());
034      }
035      finally
036      {
037          try
038          {
039              socket.close();
040          }
041          catch (Exception e)
042          {
043          }
044      }
045      System.out.println("------------------------");
046  }

在上面的代碼中013行是最值得注意的。其中s.equals("")表示讀入一個空行(表明消息頭已經結束);由於在實體內容中也可以存在空行,因此,b == true來標記消息頭是否已經被讀過,當讀完消息頭後,將b設為false,如果以後再遇到空行,就不會當成消息頭來處理了。當HTTP請求使用HEAD方法時,服務器只返回響應消息頭;因此,使用!isHead來保證使用HEAD發送請求時不顯示響應消息的內容實體。

現在我們已經實現了這個HTTP模擬器,下面讓我們來運行並測試它。

運行

運行如下的命令

java http.HttpSimulator  

運行以上的命令後,將顯示如圖1所示的界面。

圖1

測試

在HTTP模擬器中輸入如下的域名:

www.csdn.net

在HTTP模擬器中輸入如下的HTTP請求消息:

GET / HTTP/1.1

Host: www.csdn.net

運行的結果如圖2所示。

圖2

本文實現的Http模擬器在後面的文章中會經常使用,讀者可以從本文的開始部分下載Http模擬器的源代碼和.class文件。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved