<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
    數(shù)據(jù)加載中……

    Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器

    本文為原創(chuàng),如需轉(zhuǎn)載,請注明作者和出處,謝謝!

    上一篇:Java網(wǎng)絡(luò)編程從入門到精通(21):HTTP消息的格式

    源代碼和.class文件下載

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

    1.  可以手工輸入HTTP請求,并向服務(wù)器發(fā)送。

    2.  接收服務(wù)器的響應(yīng)消息。

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

    應(yīng)消息全部顯示,而是先顯示消息頭,然后由用戶決定是否顯示實體內(nèi)容。

    4.  集中發(fā)送請求。這個HTTP模擬器和Telnet不同的是,并不是一開始就連接服務(wù)器,

    而是將域名、端口以及HTTP請求消息都輸完后,才連接服務(wù)器,并將這些請求發(fā)送給服務(wù)器。這樣做的可以預(yù)防服務(wù)器提前關(guān)閉網(wǎng)絡(luò)連接的現(xiàn)象。

        5. 可以循環(huán)做上述的操作。

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

    1.  建立一個大循環(huán),在循環(huán)內(nèi)部是一個請求/響應(yīng)對。這樣就可以向服務(wù)器發(fā)送多次請求/響應(yīng)以了。下面的四步都是被包括在循環(huán)內(nèi)部的。

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

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

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

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

    下面我們就來逐步實現(xiàn)這五步:

    一、建立一個大循環(huán)

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

      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)  // 開始大循環(huán)
      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分別調(diào)用了上述的四個方法。這些方法的具體實現(xiàn)將在后面討論。上面的代碼除了調(diào)用這四個核心方法外,還做了一些準(zhǔn)備工作。在008至012行定義了一些以后要用到的變量。在016和017行使用控制臺的輸入流建立了BufferedReader對象,通過這個對象,可以直接從控制臺讀取字符串,而不是一個個地字節(jié)。

    二、readHostAndPort(...)
    方法的實現(xiàn)

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

      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類型的參數(shù),這個參數(shù)的值就是在HttpSimulator.java中的第016和017行根據(jù)控制臺輸入流建立的BufferedReader對象。

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

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

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

    三、readHttpRequest(...)
    方法的實現(xiàn)

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

      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(04).equals("POST");
      
    008      boolean isHead = s.substring(04).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方法的內(nèi)容:");
      
    015          s = consoleReader.readLine();
      
    016          request = request + s;
      
    017      }
      
    018  }

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

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

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

    第012 ? 017行:如果HTTP請求使用的是POST方法,要求用戶繼續(xù)輸入HTTP請求的實體內(nèi)容。

    四、sendHttpRequest()
    方法的實現(xiàn)

        這個方法的功能是將request變量中的HTTP請求消息發(fā)送到服務(wù)器。下面是這個方法的實現(xiàn)代碼:

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

    第004行:設(shè)置讀取數(shù)據(jù)超時為10秒。

    第006行:連接服務(wù)器,并設(shè)置連接超時為10秒。

    五、readHttpResponse(...)
    方法的實現(xiàn)

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

    (1) 為了研究HTTP協(xié)議。

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

    這個方法的實現(xiàn)代碼如下:

      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: 已經(jīng)讀取消息頭
      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的內(nèi)容(Y/N):");
      
    018                  String choice = consoleReader.readLine();
      
    019                  if (choice.equals("Y"|| choice.equals("y"))
      
    020                  {
      
    021                      System.out.println("---------HTTP內(nèi)容---------");
      
    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("")表示讀入一個空行(表明消息頭已經(jīng)結(jié)束);由于在實體內(nèi)容中也可以存在空行,因此,b == true來標(biāo)記消息頭是否已經(jīng)被讀過,當(dāng)讀完消息頭后,將b設(shè)為false,如果以后再遇到空行,就不會當(dāng)成消息頭來處理了。當(dāng)HTTP請求使用HEAD方法時,服務(wù)器只返回響應(yīng)消息頭;因此,使用!isHead來保證使用HEAD發(fā)送請求時不顯示響應(yīng)消息的內(nèi)容實體。

    現(xiàn)在我們已經(jīng)實現(xiàn)了這個HTTP模擬器,下面讓我們來運行并測試它。

     運行

    運行如下的命令

    java http.HttpSimulator   

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

    圖1

    測試

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

    www.csdn.net

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

    GET / HTTP/1.1
    Host: www.csdn.net

    運行的結(jié)果如圖2所示。


    圖2

    本文實現(xiàn)的Http模擬器在后面的文章中會經(jīng)常使用,讀者可以從本文的開始部分下載Http模擬器的源代碼和.class文件。

    下一篇:Java網(wǎng)絡(luò)編程從入門到精通(23):HTTP消息頭字段



    Android開發(fā)完全講義(第2版)(本書版權(quán)已輸出到臺灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2009-06-09 12:16 銀河使者 閱讀(4678) 評論(11)  編輯  收藏 所屬分類: java 原創(chuàng)網(wǎng)絡(luò)編程

    評論

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    http是建立在scoket通信之上的?
    2009-06-09 12:58 | 銀河猛男

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    http是建立在TCP基礎(chǔ)上的,socket只是通信框架,并不是協(xié)議。
    2009-06-09 13:04 | 銀河使者

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    比如ie的一個表單提交,產(chǎn)生get/post請求傳給服務(wù)器。
    這中間數(shù)據(jù)的傳輸?shù)倪^程有誰來完成,是通過socket么,
    ie調(diào)用winsocket,服務(wù)端tomcat通過java socket接受?
    2009-06-09 15:28 | 銀河猛男

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    是的,ie調(diào)用了windows的socket api,java的socket在windows下也是調(diào)用了windows的api。
    2009-06-09 15:34 | 銀河使者

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    比如request.getparmater()在servlet容器會被翻譯成socket通信?
    2009-06-09 16:05 | 銀河猛男

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    到request.getParameter已經(jīng)和socket沒關(guān)系了,servlet容器會分析http請求消息,并將分析的結(jié)果保存在HttpServletRequest對象中。而getParameter方法就是從HttpServletRequest對象中獲得相應(yīng)的值。
    2009-06-09 16:14 | 銀河使者

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    servlet容器的監(jiān)聽器通過jvm調(diào)用win32 api監(jiān)聽http請求,通信是用的是socket
    2009-06-09 16:48 | 銀河猛男

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    還自己寫啊, 不能用WEB容器嗎?
    我的博客搬到新家了 http://www.javaly.cn, 順便給你推薦一個導(dǎo)航網(wǎng)站 http://www.510gougou.com
    2009-06-09 17:16 | 找個美女做老婆

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    如果是我寫程序的話我會把
    private String request = ""; // HTTP請求消息
    定義為StringBuffer類型,不知博主覺得怎樣?

    我的目的是不要創(chuàng)建那么多的String類
    2009-09-23 20:16 | 學(xué)生

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    如果只是幾個變量,定義成什么都可以,如果是很多String變量,例如在循環(huán)中產(chǎn)生的,這就需要定義成StringBuffer或StringBuilder。
    2009-09-23 21:53 | 銀河使者

    # re: Java網(wǎng)絡(luò)編程從入門到精通(22):實現(xiàn)HTTP模擬器  回復(fù)  更多評論   

    本例只是為了演示Socket,并未考慮性能和資源問題。還有就是如果在循環(huán)中追加字符串,不是產(chǎn)生多個String類,而是產(chǎn)生多個StringBuffer或StringBuilder類
    2009-09-23 21:57 | 銀河使者
    主站蜘蛛池模板: 国产精品1024永久免费视频| 久久久久久久综合日本亚洲| 69国产精品视频免费| h片在线播放免费高清| 亚洲国产成人久久精品大牛影视 | 亚洲国产成人综合| 亚洲午夜久久久久久久久久| 国产亚洲精品免费| 99视频在线精品免费观看6| 91在线手机精品免费观看| 中文字幕a∨在线乱码免费看| 特级毛片A级毛片免费播放| 亚洲午夜精品久久久久久app| 亚洲精品在线免费观看视频| 亚洲国产精品成人精品无码区| 亚洲福利精品一区二区三区| 日本一线a视频免费观看| 免费a级毛片高清视频不卡| 亚洲一区免费在线观看| 日韩精品内射视频免费观看 | 亚洲高清专区日韩精品| 亚洲午夜爱爱香蕉片| 亚洲国产成人影院播放| 免费国产高清视频| 免费永久在线观看黄网站| 国产一区二区视频免费| 国产中文字幕免费观看| 国产免费拔擦拔擦8x| 国产乱人免费视频| 亚洲国产av一区二区三区| 丁香亚洲综合五月天婷婷| 又粗又黄又猛又爽大片免费| 免费一级肉体全黄毛片| 免费又黄又爽的视频| 亚洲日韩在线观看| 国产av无码专区亚洲av果冻传媒| 国产精品亚洲综合专区片高清久久久 | 女人张腿给男人桶视频免费版| 久久午夜免费视频| 成人毛片免费播放| 日韩免费无砖专区2020狼|