How Tomcat Works中文版02

1.A Simple Web Server

1.1The HypertextTransfer Protocol

HTTP是一个协议,允许浏览器和服务端进行数据交互。是一份关于request和response的协议。客户端发送请求文件,然后服务端响应它。HTTP协议用可靠的TCP连接,默认端口号是80。HTTP协议的第一个版本是HTTP/0.9,后来是HTTP/1.0,现最新的版本是HTTP/1.1,是RFC2616定义的。

注意:这章只会简短地涉及HTTP/1.1,如果你想要了解更多,去读RFC2616。

在HTTP协议中,永远是客户端开启一个事务,通过建立连接并发送HTTP请求。服务端没有办法联系到客户端或者是回联客户端。同时,服务端和客户端必有一方提前终止连接。比如当你下载文件的时候,可以点击停止按键马上终止与服务端的连接。


HTTP请求

       一个HTTP请求由三个部分组成:

[if !supportLists]1.      [endif]方法——URI——协议版本

[if !supportLists]2.      [endif]请求头

[if !supportLists]3.      [endif]请求主体

一个标准的HTTP请求看起来是这样子的:

[if !vml]

[endif]其中POST是请求的方法,/example/default.jsp是URI,HTTP/1.1则是版本号。

每一个HTTP请求可以使用HTTP标准方法其中的一种,一共有七种:GET、POST、HEAD、OPTIONS、PUT、DELETE、TRACE。GET和POST是最为常用的。

URI完全定位了资源所在的位置,URI通常是指相对于服务器根目录的定位。所以它必须是以/开头,URL则是URI的一种。协议版本号则指明了当前用的HTTP协议版本。

请求头则是关于客户端和请求主体的一些有用信息,比如,它包含了客户端所用的语言,请求体的长度等等。每个请求头之间以回车分隔。

在请求头和请求体之间必须有一个回车,这是HTTP请求的重要规范。它告诉了服务器请求体从哪儿开始。甚至在有些书中,把这个回车当作了HTTP请求的第四个组成部分。

上面这个请求的请求体十分简单:lastName=Franks&firstName=Michael

在典型的HTTP请求头请求体往往会比这个长的多。

HTTP响应

       与HTTP请求相似,响应也由三个部分组成:

[if !supportLists]1.      [endif]协议——状态码——描述

[if !supportLists]2.      [endif]响应头

[if !supportLists]3.      [endif]响应体

以下是一个标准的HTTP响应:

[if !vml]

[endif]

状态码200代表一切都非常顺利。

       同样,响应头与响应体之前也必须有一个回车符。

1.2The Socket Class

       Socket是网络连接的一个端点。Socket使得应用可以向网络读写数据。两台计算机上的两个应用之间可以通过收发网络连接字节流来交互。当你发消息的时候,你不仅要知道对方的IP地址,还要知道对方app的socket的端口号。在JAVA中,socket用java.net.Socket来表示。

       创建一个Socket,你可以用Socket类提供的许多构造方法。其中有一个方法:[if !vml]

[endif]

参数是host的名字和端口号,host可以是远程机器名字也可以是IP地址。端口号是远程app的端口号。比如连接yahoo.com的80端口,如上。

       一旦你实例化出来一个Socket对象,你可以用它来发送或者接收字节流了。发送字节流,你要先用getOutputStream方法得到一个OutputStream对象。为了向远程app发送文本,你还需要一个PrintWriter对象。接收字节流的时候,你要用getInputStream得到一个InputStream对象。

       下面的代码创建了一个可以与本地HTTP服务交互的socket,用了一个StringBuffer对象去接收响应并打印到控制台:

[if !vml]

[endif]

       注意,为了得到正确的响应,你必须发送符合规范的HTTP请求。如果你前半部分好好学了,你肯定能看懂上面的请求是如何发送出去的。

1.3The ServerSocketClass

       客户端socket用Socket类来表示,那么服务端呢,当你想要连接远程网络服务的时候,你需要一个Socket类的实例。这时,如果你想要实现服务端呢,比如一个HTTP服务或者一个FTP服务,你需要不同的方法了。你的服务端必须要一直待命,因为它不知道客户端什么时候会连接它,为了让你的服务能够一直待命,你需要java.net.ServerSocket类,这是服务端socket的一种实现。

       ServerSocket和Socket不同的是,服务端总是等待着连接的那个。一旦服务端socket收到连接请求,马上会创建一个Socket实例去负责与客户端的通信。

       创建一个服务端socket,你可以用ServerSocket提供的四种构造方法之一。你需要确定IP地址和服务监听的端口号。通常来说,IP地址是127.0.0.1,意味着这个服务端socket监听本地这台机器。这个IP地址也被称为绑定地址。服务端socket还有一个重要属性叫backlog,它是服务端能接收请求队列的最大长度,超过这个长度,服务端会拒绝连接。

[if !vml]

[endif]

       第一个参数是端口号,第二个是backlog,第三个参数地址必须是InetAddress类,这个getByName是个静态方法,用来得到InetAddress。

       一旦你有了一个ServerSocket实例,你可以让它监听并等待请求了。通过ServerSocket类的accept方法。这个方法只有在连接请求的时候会返回一个Socket类的实例。这个Socket类的实例可以用来收发字节流,从我们的客户端。

1.4The Application

       我们的web应用由三个类组成:

       HttpServer、Request、Response

       我们应用的主方法在HttpServer类中。主方法创建一个HttpServer实例,并调用它的await方法,正如其名,这个方法会监听指定端口的HTTP请求,并且处理它们,并把响应发回客户端。它会一直工作直到下达关闭命令。

       这个应用只能够发送静态资源,如存在于特定目录的HTML文件和图片文件,它同样在控制台打印每个HTTP请求的字节流,然而它并不会返回任何cookie给浏览器。

       我们在下面的章节详细介绍这三个类。

1.5The HttpServerClass

       HttpServer类是我们写的一个表示web服务的类。

[if !vml]

[endif]       [if !vml]

[endif]

这个web服务可以为WEB_ROOT目录下的静态资源文件提供访问服务,你可以用这些静态资源文件来测试这个web服务。目录下还有几个servlet也可以供你测试,为请求一个静态资源,你可以在浏览器输入以下地址访问:[if !vml]

[endif]

       你可以输入特定地址来关闭这个web服务,这个特定地址就是SHOWDOWN_COMMAND,这个await方法之所以叫await,是因为wait名字被占了,wait是java中多线程编程的一个重要方法。

       await方法创建了一个ServerSocket类实例,然后进入一个while循环。While循环中的代码会阻塞在ServerSocket类的accept方法,直到有人请求时才会返回值,程序才会继续往下执行。当收到HTTP请求的时候,accept方法会返回一个Socket对象,这个Socket对象会提供OutputStream对象和InputStream对象。然后await方法会构造一个Request对象,然后调用Request对象的parse方法格式化请求数据。

       之后,await方法会构造一个Response对象,把Request对象放进去,然后调用其sendStaticResource方法。

       最后,调用Socket的close()方法关闭Socket对象,同时检查是否是关闭命令,如果是,把shutdown变量变成true,终止while循环。

1.6The Request Class

       Request类表示一个HTTP请求。这个类的一个实例可以通过一个InputStream对象创建,InputStream对象是从Socket对象中拿过来的,Socket对象维持着与客户端的通信。你可以调用InputStream的read方法的一种,来拿到HTTP请求的原始数据。

       以下是代码:

[if !vml]

[endif][if !vml]

[endif][if !vml]

[endif]

Parse方法负责解析HTTP请求中的原始数据,其实也没做太多事情。它提供的唯一信息就是HTTP请求的URI地址,还是通过调用parseUri方法获得的。ParseUri方法将URI地址存入uri变量。这样,我们就可以通过调用public方法getUri来得到HTTP请求的URI地址。

       注意:第三章我们将讨论如何详细处理HTTP请求的原始数据。

       要明白parse和parseUri方法是如何工作的,你必须对HTTP请求的结构了然于心,就像我们上一节讲的那样。在这里的话,我们将只讨论HTTP请求的第一部分:请求行,请求行由方法名开头,然后是URI地址,然后是协议版本号,然后是回车符。请求行的每个部分之间以空格分隔。举个例子,GET方法请求index.html文件的HTTP请求如下:

[if !vml]

[endif]

       parse方法读取Socket的InputStream对象的所有字节流,并且将它缓存进一个byte数组里面。然后生成一个StringBuffer,去使用这个byte数组里面的byte,然后把StringBuffer转成String。

       parseUri方法从请求行拿到时URI地址,它会搜索第一个空格和第二个空格之间的字符串,然后拿到它。

       1.7TheResponse Class

       [if !vml]

[endif][if !vml]

[endif]

       Response类的构造方法接收一个OutputStream类型的参数。一个Response对象会在HttpServer类的await方法中被实例化,Socket将为它提供OutputStream。

       Response类有两个public方法:setRequest和sendStaticResource。setRequest用于向Response对象传递Request对象。

       sendStaticResource方法用来发送静态资源,比如HTML文件。它通过全路径(根目录+相对目录)实例化了一个java的File类。

       然后它会检查文件是否真的存在,如果存在的话,通过刚才的java.io.File对象实例化一个FileInputStream对象。然后调用FileInputStream的read方法,调用OutputStream的write方法。需要注意的是,在此情形下,发送给浏览器的静态资源文件将是原始数据。

       如果文件不存在的话,sendStaticResource方法向浏览器发送一个错误消息。

1.8Runningthe Application

       在当前目录,输入以下命令来运行app.

[if !vml]

[endif]

       在浏览器访问以下地址来测试app.

[if !vml]

[endif]

你会看到index.html在你的浏览器成功显示。

总结

       在这一章你看到了一个web服务是如何工作的。这一章做的app也是个非常好的学习工具。下一章,我们将讨论如何处理动态内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容