首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Java网络编程从入门到精通(22):兑现HTTP模拟器

2012-09-13 
Java网络编程从入门到精通(22):实现HTTP模拟器在讨论HTTP协议的具体请求和响应头字段之前,让我们先来利用

Java网络编程从入门到精通(22):实现HTTP模拟器

在讨论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??????Java网络编程从入门到精通(22):兑现HTTP模拟器?Java网络编程从入门到精通(22):兑现HTTP模拟器
??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("正在连接服务器Java网络编程从入门到精通(22):兑现HTTP模拟器");
??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所示的界面。
Java网络编程从入门到精通(22):兑现HTTP模拟器图1
测试在HTTP模拟器中输入如下的域名:www.csdn.net在HTTP模拟器中输入如下的HTTP请求消息:GET?/?HTTP/1.1
Host:?www.csdn.net
运行的结果如图2所示。
Java网络编程从入门到精通(22):兑现HTTP模拟器
图2
本文实现的Http模拟器在后面的文章中会经常使用,读者可以从本文的开始部分下载Http模拟器的源代码和.class文件。

热点排行