Java网络编程:实现HTTP模拟器
发布时间:2011-4-4 18:13
发布者:1640190015
在讨论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所示。 ![]() 本文实现的Http模拟器在后面的文章中会经常使用,读者可以从本文的开始部分下载Http模拟器的源代码和。class文件。 |
网友评论