Java网络编程:实现HTTP模拟器

发布时间:2011-4-4 18:13    发布者:1640190015
关键词: 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所示的界面。

2011040215145020.jpg   
    图1
    测试
    在HTTP模拟器中输入如下的域名:
    www.csdn.net
    在HTTP模拟器中输入如下的HTTP请求消息:
    GET / HTTP/1.1
    Host: www.csdn.net
    运行的结果如图2所示。  

2011040215140271.jpg
    本文实现的Http模拟器在后面的文章中会经常使用,读者可以从本文的开始部分下载Http模拟器的源代码和。class文件。
本文地址:https://www.eechina.com/thread-61114-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
sw9518 发表于 2011-4-21 09:25:08
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

相关视频

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表