1.2 如何发起一个请求

如何发起一个HTTP请求?这个问题似乎既简单又复杂,简单是指当你在浏览器里输入一个URL时,按回车键后这个HTTP请求就发起了,很快你就会看到这个请求的返回结果。复杂是指能否不借助浏览器也能发起请求,这里的“不借助”有两层含义,一是指能不能自己组装一个符合HTTP协议的数据包,二是处理浏览器还有哪些方式也能简单地发起一个HTTP请求。下面就按照这两层含义来解释如何发起一个HTTP请求。

如何发起一个HTTP请求和如何建立一个Socket连接区别不大,只不过outputStream. write写的二进制字节数据格式要符合HTTP协议。浏览器在建立Socket连接之前,必须根据地址栏里输入的URL的域名DNS解析出IP地址,再根据这个IP地址和默认80端口与远程服务器建立Socket连接,然后浏览器根据这个URL组装成一个get类型的HTTP请求头,通过outputStream.write发送到目标服务器,服务器等待inputStream.read返回数据,最后断开这个连接。

当然,不同浏览器在如何使用这个已经建立好的连接,以及根据什么规则来管理连接,有各种不同的实现方法。一句话,发起一个HTTP请求的过程就是建立一个Socket通信的过程。

既然发起一个HTTP连接本质上就是建立一个Socket连接,那么我们完全可以模拟浏览器来发起HTTP请求,这很好实现,也有很多方法实现,如HttpClient就是一个开源的通过程序实现的处理HTTP请求的工具包。当然如果你对HTTP协议的数据结构非常熟悉,你完全可以自己再实现另外一个HttpClient,甚至可以自己写个简单的浏览器。

下面是一个基本的HttpClient的调用示例:

    HttpClient httpClient = createHttpClient();
          PostMethod postMethod;
          String domainName = Switcher.domain;
          postMethod = new PostMethod(domainName);
          postMethod.addRequestHeader("Content-Type", "application/x-www-form-
urlencoded; charset=GBK");
          for (FilterData filterData : filterDatas) {
              postMethod.addParameter("ip", filterData.ip);
              postMethod.addParameter("count", String.valueOf(filterData.count));
          }
          try {
              httpClient.executeMethod(postMethod);
              postMethod.getResponseBodyAsString();
          } catch (Exception e) {
              logger.error(e);
          }

处理Java中使用非常普遍的HttpClient还有很多类似的工具,如Linux中的curl命令,通过curl + URL就可以简单地发起一个HTTP请求,非常方便。

例如,curl "http://item.taobao.com/item.htm?id=1264" 可以返回这个页面的HTML数据,如图1-2所示。

图1-2 HTTP请求返回的HTML数据

也可以查看这次访问的HTTP协议头的信息,加上-I选项,如图1-3所示。

图1-3 HTTP协议头的信息

还可以在访问这个URL时增加HTTP头,通过-HI选项实现,如图1-4所示。

图1-4 访问URL时增加HTTP头

因为缺少Cookie信息,所以上面的访问返回302状态码,必须增加Cookie才能正确访问该链接,如下所示:

    [junshan@v101055.sqa.cm4 admin]$ curl -I  "http://switch.taobao.com:9999/
repository.htm" -H "Cookie:cna=sd0/BjeZulwCAfIdAHkzZZqC; _t_track=121.0.29.
242.1320938379988839;"
    HTTP/1.1200 OK
    Date: Sat, 25 Feb 2012 08:41:20 GMT
    Server: Apache
    Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT
    Vary: Accept-Encoding
    Content-Type: text/html;charset=GBK