`

转的别人写的模拟器(只是拿来看看)

 
阅读更多

 在讨论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文件。

 

/*

    HTTP模拟器
    设计者:李宁
    设计日期:2009-06-09
    网名:银河使者
    blog:http://nokiaguy.blogjava.net
    email:techcast@126.com
    msn:asklining@hotmail.com

*/
package http;

import java.net.*;
import java.io.*;

public class HttpSimulator
{
	private Socket socket;
	private int port = 80;
	private String host = "localhost";
	private String request = ""; // HTTP请求消息
	private boolean isPost, isHead;

	// 从控制台读取host和port,当输入字符串"q"时,返回false,否则,返回true
	private boolean readHostAndPort(BufferedReader consoleReader)
			throws Exception
	{
		System.out.print("host:port>");
		String[] ss = null;
		String s = consoleReader.readLine();
		if (s.equals("q"))
			return false;
		else
		{
			ss = s.split("[:]");
			if (!ss[0].equals(""))
				host = ss[0];
			if (ss.length > 1)
				port = Integer.parseInt(ss[1]);
			System.out.println(host + ":" + String.valueOf(port));
			return true;
		}
	}
	// 从控制台读取HTTP请求消息,并将请求消息保存在request中
	private void readHttpRequest(BufferedReader consoleReader) throws Exception
	{
		System.out.println("请输入HTTP请求:");
		String s = consoleReader.readLine();
		request = s + "\r\n";
		boolean isPost = s.substring(0, 4).equals("POST");
		boolean isHead = s.substring(0, 4).equals("HEAD");
		while (!(s = consoleReader.readLine()).equals(""))
			request = request + s + "\r\n";
		request = request + "\r\n";
		if (isPost)
		{
			System.out.println("请输入POST方法的内容:");
			s = consoleReader.readLine();
			request = request + s;
		}
	}
	// 向服务器发送HTTP请求消息
	private void sendHttpRequest() throws Exception
	{
		socket = new Socket();
		socket.setSoTimeout(10 * 1000);
		System.out.println("正在连接服务器...");
		socket.connect(new InetSocketAddress(host, port), 10 * 1000);
		System.out.println("服务器连接成功!");
		OutputStream out = socket.getOutputStream();
		OutputStreamWriter writer = new OutputStreamWriter(out);
		writer.write(request);
		writer.flush();
	}
	// 读取从服务器返回的HTTP响应消息
	private void readHttpResponse(BufferedReader consoleReader)
	{
		String s = "";
		try
		{
			InputStream in = socket.getInputStream();
			InputStreamReader inReader = new InputStreamReader(in);
			BufferedReader socketReader = new BufferedReader(inReader);
			System.out.println("---------HTTP头---------");
			boolean b = true; // true: 未读取消息头 false: 已经读取消息头
			while ((s = socketReader.readLine()) != null)
			{
				if (s.equals("") && b == true && !isHead)
				{
					System.out.println("------------------------");
					b = false;
					System.out.print("是否显示HTTP的内容(Y/N):");
					String choice = consoleReader.readLine();
					if (choice.equals("Y") || choice.equals("y"))
					{
						System.out.println("---------HTTP内容---------");
						continue;
					}
					else
						break;
				}
				else
					System.out.println(s);
			}
		}
		catch (Exception e)
		{
			System.out.println("err:" + e.getMessage());
		}
		finally
		{
			try
			{
				socket.close();
			}
			catch (Exception e)
			{
			}
		}
		System.out.println("------------------------");
	}
	public void run() throws Exception
	{
		BufferedReader reader = new BufferedReader(new InputStreamReader(
				System.in));
		while (true)
		{
			try
			{
				if (!readHostAndPort(reader))
					break;
				readHttpRequest(reader);
				sendHttpRequest();
				readHttpResponse(reader);
			}
			catch (Exception e)
			{
				System.out.println("err:" + e.getMessage());
			}
		}
	}
	public static void main(String[] args) throws Exception
	{
		new HttpSimulator().run();
	}
}

 

分享到:
评论

相关推荐

    Scratch3.0 红蓝方块战争模拟器(只是拿来看的)

    和你的朋友猜猜那个颜色会赢吧

    C#怎么傻瓜式几句代码连接上夜神安卓模拟器,并且随心所欲地读和存文件

    然而很多时候大家并不只是想拿来用,而是想怎么搞的,如何适应我的代码.这时那些繁复冗长复杂的控件代码反而是累赘.所以我写了个自己适合的代码. 它采用的github上最新的COOLAdb工具(我没用它的dll 引用,而是自己修改...

    C#中最最简单浅显易懂的通过最新的ADB 工具 傻瓜 连接安卓模拟器或手机互相拷贝文件

    然而很多时候大家并不只是想拿来用,而是想怎么搞的,如何适应我的代码.这时那些繁复冗长复杂的控件代码反而是累赘.所以我写了个自己适合的代码. 它采用的github上最新的COOLAdb工具(我没用它的dll 引用,而是自己修改...

    Windows+CE+6.0搭建开发环境及模拟器的使用.pdf

    1、先装 Visual Studio 2005, 我拿到的是 Professional Edition 英文版的。 需要使用虚拟光驱软件加载后再安装,公司目前存在的另一个以散装文件的方式 提供的安装后无法安装升级,最后在编译和下载内核的时候会出现...

    ios-陌陌的点点动画 和卡片左右滑动切换效果.zip

    公司最近不忙,自己又近期学了core animation,就想找个好玩的动画实现一下,无意中看到陌陌的点点感觉还可以,就拿来分析实现了。代码写的不够精简,大家就不要喷了。主要看逻辑吧,如果想让本码农再写篇代码简介的...

    dos另外一台电脑

    不过话虽这样说,但我个人认为这些文章讲解的并不详细,对于第一次接触ipc$的菜鸟来说,简单的罗列步骤并不能解答他们的许多迷惑(你随便找一个hack论坛搜一下ipc,看存在的疑惑有多少). 因此我写了这篇相当于解惑的教程....

    类似知乎的图文编辑器

    辛苦了几天,应公司要求搞了个类似知乎功能那种编辑器【功能文件:PTEditor.m 和.h】,【就是可以插入图片,图片会单独现实在一行】。代码中插入的图片被缩小过(不缩小模拟器自带的...有需要的亲们可以拿来参考下,

    IDA Pro权威指南

     当然,拿到书后,它的实际益处需要根据读者能够利用它做什么来评估。最基本的是你想要从中学会什么,而不是信息本身。当然,名师出高徒,学生学得怎么样,还要看老师教得好不好。 最后同样重要的是,IDA图书的...

    新版Android开发教程.rar

    程序可以采用 JAVA 开发,但是因为它的虚拟机 (Virtual Machine) Dalvik ,是将 JAVA 的 bytecode 转成 自 己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。 对手机制造者的影响 � Android 是款开源的移动计算...

    PT80-NEAT开发指南v1.1

    编译及运行程序(模拟器下) ................................................................................................................ 7 编译及运行程序(PT80) ......................................

Global site tag (gtag.js) - Google Analytics