【How Tomcat Works】第一章——一个简易的java web服务实现(上)

来源:互联网 发布:淘宝宝贝无线链接地址 编辑:程序博客网 时间:2024/05/22 07:59

        HTTP知识深入讲解作为本章的开头,甚至是实质意义上本书的开头,鲜有其他Tomcat技术书能够做到,因为这样做非常“冒险的”,冒险的原因在于既没有立马对Tomcat庖丁解牛式的讲解,亦不能让我们看完之后快速上手开发Web应用。如果以前放在我的面前,我也是几乎不会碰的。不过面对这样一个“冒险”开头又是英文版的书,我能痴之入迷完全得归功于另一本非常优秀的Tomcat相关的书——《Tomcat与Java Web开发技术详解》。一本中文书,作者是孙卫琴,一个重质不重量的技术作者。这本书的前几章把我带到了一个不同的世界,犹如儒勒·凡尔纳将我带到了令人惊叹的海底两万里。看完这边书后,有一次在百度知道上回答了一个网友的提问,得到了一个受惊若宠的好评。这个好评一方面让我很有成就感,而另一方面“惊”的就是,在内心深处我明白并没有真正的到达海底的两万里,孙卫琴的书只能帮我安全潜入水,继续修行还得靠个人。


开篇 

        本章比较系统的讲解了Java web 服务器的运作详情(这种系统的讲解除了Java层面,如果有计算机网络方面的知识会更为有共鸣感)Web服务器又可称作为超文本协议(HTTP)服务器,它用到了HTTP协议和客户端交互,客户端包括但不限于我们熟悉的web浏览器。对于基于Java实现的web服务程序,就不得不提以下两个类:java.net.Socket和java.net.ServerSocket。所以自然而然,本章将会以对HTTP协议和这两个类的探讨为开头,继而会讲解如何实现一个简单的web服务程序。 


HTTP家族的生活

         本章的HTTP知识内容包括HTTP协议本身、HTTP请求、HTTP响应。当然HTTP家族成员当然不限于上述,比如大名鼎鼎的网页设计、CSSTCP/IP等等,都是它的内亲外戚。 


HTTP协议一瞥

        在互联网中,web服务器程序和浏览器程序交换需要发送和接受数据,但由于互联网异构属性,传输的数据要受到一个格式的要求,以便屏蔽掉这种存在的异构,否则就会像两个哑巴对话一样,各说各的语言,彼此觉得还听懂了对方。为此,HTTP协议应运而生,这是一种请求/响应类型协议。所谓请求,是客户端的请求,就是客户端请求一个文件(显然,现在用“资源”一词更好);而所谓响应,是服务器的响应,也就是根据上述请求做出的回应。HTTP是基于可靠的TCP连接——默认TCP端口为80HTTP经历了从HTTP/0.9到现在的HTTP/1.0的发展历程,详情可参考W3C发布的标准说明文档。

        上述讲到,请求是客户端的请求,响应是服务器的响应,意思就是在HTTP中表现为由客户端建立连接发起交互,并发送请求,而服务器端是绝不可能主动和客户端通信或者是回拨连接(莫非FTP或者其他协议不是这样的??)。而无论服务端还是客户端都有权主动终端连接。比如你下载一个文件,如果你点击了网页的取消按钮,将会关闭与web服务器的HTTP连接。


HTTP Request的内心独白

        RequestResponse是我们在jsp或者是servlet编程接触最多的一个东东,通常的标题是作为jsp/servlet一个重要的内置对象,然后跟着一大堆Request的方法讲解。下面要真正走进Request的内心世界。本内容还可参考孙卫琴的《Tomcat与Java Web开发技术详解》一书。 

        HTTP请求通常由三部分组成

               1、请求方法——统一资源定位符(URI)——协议/版本

               2、请求头

               3、请求实体

       其中请求实体以一空行区别于请求头,以下面的一个请求为例,对Request进行详谈。

POST /examples/default.jsp HTTP/1.1Accept: text/plain; text/htmlAccept-Language: en-gbConnection: Keep-AliveHost: localhostUser-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)Content-Length: 33Content-Type: application/x-www-form-urlencodedAccept-Encoding: gzip, deflate lastName=Franks&firstName=Michael


        POST /examples/default.jsp HTTP/1.1对应就是part1了。其中POST是请求方法,对于请求方法除了我们熟悉的POSTGET这一对孪生兄弟,还有HEADOPTIONPUTDELETE等等;/examples/default.jsp HTTP/1.1是请求资源的URI,一般体现为相对服务器根目录的地址;HTTP/1.1对应是协议/版本,对于这个的存在,就像当初http发明者描述的让全世界的人都费手筋打了多余的这4个字母,也是多余的,不过https的出现让这个字段有了存在的价值。

请求头包含了浏览器语言环境、请求实体长度等重要信息,并以键值对的形似出现。(请求头是我们编程时的盲区,基本接触不多)

       lastName=Franks&firstName=Michael对应请求实体。实际中,一个请求实体远比例子给出的长。 


HTTP Response的独自等待

       作为Request的孪生兄弟,Response长得也和它差不多,由三部分组成

               1、协议——状态码——描述

               2、响应头

               3、响应实体

       同样,来一份Request示例。

HTTP/1.1 200 OKServer: Microsoft-IIS/4.0Date: Mon, 5 Jan 2004 13:13:33 GMTContent-Type: text/htmlLast-Modified: Mon, 5 Jan 2004 13:13:12 GMTContent-Length: 112 <html><head><title>HTTP Response Example</title></head><body>Welcome to Brainy Software</body></html>

       对照Request,基本上不用对Response多加解释,开头就是告诉你使用的是HTTP1.1协议版本,请求成功,一切就绪。着重是200这个状态码,对于状态码其实我再熟悉不过了,像404页面就是源于状态码,200表示成功,404表示找不到所请求的资源。


Socket编程

       在计算机网络课程中,我们HTTP的本质是基于TCP/IP网络传输的上层应用,其实现是套接字编程,也就是Socket,对应Java中的两大类Socket和ServerSocket 


Socket类

       创建一个socket常用的构造方法:

public Socket(java.lang.String host, int port);

       创建后,可以通过该实例和远程主机通信。发送字节流通过调用Socket.getOutputStream获得一个OutputStream实例;发送字符文本,从返回的OutputStream实例中构造一个PrintWriter对象;接收字节流通过调用Socket.getIutputStream获得一个OutputStream实例。

       下面代码创建了一个socket,通过该socket可与本地HTTP服务器(127.0.0.1表示本地主机)通信,发送一个HTTP请求,并接收返回的响应。这里使用BufferedReader作为缓冲,用StringBuffer保存返回的对象。

Socket socket = new Socket("127.0.0.1", "8080");OutputStream os = socket.getOutputStream();boolean autoflush = true;PrintWriter out = new PrintWriter(  socket.getOutputStream(), autoflush);BufferedReader in = new BufferedReader(  new InputStreamReader( socket.getInputStream() ));  // send an HTTP request to the web serverout.println("GET /index.jsp HTTP/1.1");out.println("Host: localhost:8080");out.println("Connection: Close");out.println();// read the responseboolean loop = true;StringBuffer sb = new StringBuffer(8096);while (loop) {  if ( in.ready() ) {      int i=0;      while (i!=-1) {          i = in.read();          sb.append((char) i);      }      loop = false;  }  Thread.currentThread().sleep(50);}// display the response to the out consoleSystem.out.println(sb.toString());socket.close();


 

       需要指出的是,要从服务器得到正确的响应,所发送的HTTP请求必须遵循HTTP协议(按照该协议,服务器那又发生了什么,参见下节)。这也就是前文要介绍HTTP知识的原因,如果有看HTTP Request一节,就不难明白上面的代码(特别是// send an HTTP request to the web server下面那一大段print) 


ServerSocket

       要实现服务程序,如HTTP服务器或是FTP服务器,就要用到有别于Socket的其他方式。这是因为服务器并不知道客户端什么时候会发送连接,所有必须时刻处于待命状态,也就是必须用到ServerSocket类。一个ServerSocket等待连接请求,连接请求一旦被接收,即创建一个Socket实例和客户端交互。

       ServerSocket编程较为复杂。首先要绑定一个IP地址(一般是本机127.0.0.1),注册端口号(默认是80)并监听,指定backlog数,ServerSocket一个重要属性,用以限定最大连接数。常用的构造方法如下:

public ServerSocket(int port, int backLog, InetAddress bindingAddress);

       注意到在这个构造方法中,IP地址的绑定用到了一个InetAddress的对象,所以可以用静态方法InetAddress.getByName获得一个该对象,实现如下:

InetAddress.getByName("127.0.0.1");
原创粉丝点击