java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java 非阻塞HTTP服务器

Java创建非阻塞的HTTP服务器的实现

作者:布道师小羊

本文主要介绍了Java创建非阻塞的HTTP服务器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

HTTP(Hypertext Transfer Protocol,超级文本传输协议)是网络应用层的协议,建立在TCP/IP基础上。HTTP使用可靠的TCP连接,默认端口是80端口。HTTP的第1个版本是HTTP/0.9,后来发展到了HTTP/1,现在最新的版本是HTTP/2。值得注意的是,在目前的实际运用中,HTTP/2并没有完全取代HTTP/1,而是这两种协议在网络上并存,也就是说,许多Web服务器和浏览器之间既可以通过HTTP/1通信,也可以通过HTTP/2通信。

HTTP/1.1对应的RFC文档为RFC2616,它对HTTP/1.1做了详细的阐述。HTTP/2对应的RFC文档为RFC7540,它对HTTP/2协议做了详细的阐述。

HTTP基于客户/服务器模式,客户端主动发出HTTP请求,服务器接收HTTP请求,返回HTTP响应结果。HTTP对HTTP请求以及响应的格式做了明确的规定。

1、Htpp简介

当用户打开浏览器,输入一个URL地址,就能接收到远程HTTP服务器发送过来的网页。浏览器就是最常见的HTTP客户程序。如下图所示,HTTP客户程序必须先发出一个HTTP请求,然后才能接收到来自HTTP服务器的响应。

在这里插入图片描述

HTTP客户程序和HTTP服务器分别由不同的软件开发商提供,它们都可以用任意的编程语言编写。用VC编写的HTTP客户程序能否与用Java编写的HTTP服务器顺利通信呢?答案是肯定的。HTTP严格规定了HTTP请求和HTTP响应的数据格式,只要HTTP服务器与客户程序都遵守HTTP,就能彼此看得懂对方发送的消息。

1.1、HTTP请求格式

HTTP规定,HTTP请求由3部分构成,分别是:

下面是一个HTTP请求的例子:

在这里插入图片描述

1.请求方式、URI、HTTP的版本

HTTP请求的第1行包括请求方式、URI和协议版本这3项内容,以空格分开:

在这里插入图片描述

在以上代码中,“POST” 表示请求方式,“/hello”表示URI,“HTTP/1.1”表示HTTP的版本。

根据HTTP,HTTP请求可以使用多种请求方式,主要包括以下几种:

GET和POST请求方式最常用,而PUT和DELETE请求方式并不常用,因而不少HTTP服务器并不支持PUT和DELETE请求方式。

统一资源定位符(Universal Resource Identifier,URI)用于标识要访问的网络资源。在HTTP请求中,通常只要给出相对于服务器的根目录的相对目录即可,因此以“/”开头。

HTTP请求的第1行的最后一部分内容为客户程序使用的HTTP的版本。

2.请求头(Request Header)

请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器的类型、所用的语言、请求正文的类型,以及请求正文的长度等。

在这里插入图片描述

3.请求正文(Request Content)

HTTP规定,请求头和请求正文之间必须以空行分割(即只有CRLF符号的行),这个空行非常重要,它表示请求头已经结束,接下来是请求正文。请求正文中可以包含客户以POST方式提交的表单数据。

在这里插入图片描述

在以上HTTP请求例子中,请求正文只有一行内容。在实际应用中,HTTP请求的正文可以包含更多的内容。

下面是一个简单的HTTP客户程序,它发送的HTTP请求信息就严格遵守上述规范。

在这里插入图片描述

1.2、HTTP响应格式

和HTTP请求相似,HTTP响应也由3部分构成,分别是:

下面是一个HTTP响应的例子:

在这里插入图片描述

1.HTTP的版本、状态代码、描述

HTTP响应的第1行包括服务器使用的HTTP的版本、状态代码,以及对状态代码的描述,这3项内容之间以空格分割。在本例中,使用HTTP1.1,状态代码为200,该状态代码表示服务器已经成功地处理了客户端发出的请求。

在这里插入图片描述

状态代码是一个3位整数,以1、2、3、4或5开头:

以下是一些常见的状态代码:

2.响应头(Response Header)

响应头也和请求头一样包含许多有用的信息,例如服务器类型、正文类型和正文长度等。

在这里插入图片描述

3.响应正文(Response Content)

响应正文就是服务器返回的具体的文档,最常见的是HTML网页。

在这里插入图片描述

HTTP请求头与请求正文之间必须用空行分割,同样,HTTP响应头与响应正文之间也必须用空行分隔。

2、创建非阻塞的HTTP服务器

HTTP服务器的主要任务就是接收HTTP请求,然后发送HTTP响应。下图是我们要介绍的非阻塞的HTTP服务器范例的模型。

在这里插入图片描述

在这个对象模型中,HttpServer类是服务器主程序,由它启动服务器。AcceptHandler负责接收客户连接,RequestHandler负责接收客户的HTTP请求,对其解析,然后生成相应的HTTP响应,再把它发送给客户。Request类表示HTTP请求,Response类表示HTTP响应,Content类表示HTTP响应的正文。

2.1、服务器主程序:HttpServer类

HttpServer类是服务器的主程序,仅启用了单个主线程,采用非阻塞模式来接收客户连接,以及收发数据。下面是HttpServer类的源程序。

在这里插入图片描述

在HttpServer类的service()方法中,当ServerSocketChannel向Selector注册接收连接就绪事件时,设置了一个AcceptHandler附件:

在这里插入图片描述

AcceptHandler类的handle()方法负责处理接收连接就绪事件。当某种事件发生时,HttpServer类的service()方法从SelectionKey中获得Handler附件,然后调用它的handle()方法:

在这里插入图片描述

2.2、具有自动增长的缓冲区的ChannelIO类

自定义的ChannelIO类对SocketChannel进行了包装,增加了自动增长缓冲区容量的功能。当调用socketChannel.read(ByteBuffer buffer)方法时,如果buffer已满(即position=limit),那么即使通道中还有未接收的数据,read方法也不会读取任何数据,而是直接返回0,表示读到了零字节。

为了能读取通道中的所有数据,必须保证缓冲区的容量足够大。在ChannelIO类中,有一个requestBuffer变量,它用来存放客户的HTTP请求数据,当requestBuffer剩余容量已经不足5%,并且还有HTTP请求数据未接收时,ChannelIO会自动扩充requestBuffer的容量,该功能由resizeRequestBuffer()方法完成。

下面是ChannelIO类的源程序,它的read()方法和write()方法利用SocketChannel来接收和发送数据,并且它还提供了实用方法transferTo(),该方法能把文件中的数据发送到SocketChannel中。

在这里插入图片描述

2.3、负责处理各种事件的Handler接口

Handler接口负责处理各种事件,它的定义如下:

在这里插入图片描述

Handler接口有AcceptHandler和RequestHandler两个实现类。AcceptHandler负责处理接收连接就绪事件,RequestHandler负责处理读就绪和写就绪事件。更确切地说,RequestHandler负责接收客户的HTTP请求,以及发送HTTP响应。

2.4、负责处理接收连接就绪事件的AcceptHandler类

AcceptHandler负责处理接收连接就绪事件。它获得与客户连接的SocketChannel,然后向Selector注册读就绪事件,并且创建了一个RequestHandler,把它作为SelectionKey的附件。当读就绪事件发生时,将由这个RequestHandler来处理该事件。

在这里插入图片描述

在以上AcceptHandler的handle()方法中,还创建了一个ChannelIO,RequestHandler与它关联。RequestHandler会利用ChannelIO来接收和发送数据。

2.5、负责接收HTTP请求和发送HTTP响应的RequestHandler类

RequestHandler先通过ChannelIO来接收HTTP请求,当接收到了HTTP请求的所有数据后,就对HTTP请求数据进行解析,创建相应的Request对象,然后依据客户的请求内容,创建相应的Response对象,最后发送Response对象中包含的HTTP响应数据。为了简化程序,RequestHandler仅仅支持GET和HEAD这两种请求方式。

在这里插入图片描述

2.6、代表HTTP请求的Request类

RequestHandler通过ChannelIO读取HTTP请求数据时,这些数据被放在requestByteBuffer中。当HTTP请求的所有数据接收完毕,就要对requestByteBuffer中的数据进行解析,然后创建相应的Request对象。Request对象就表示特定的HTTP请求。

本范例仅支持GET和HEAD请求方式,在这两种方式下,HTTP请求没有正文部分,并且以“\r\n\r\n”结尾。Request类有3个成员变量:action、uri和version,它们分别表示HTTP请求中的请求方式、URI和HTTP的版本。下面是Request类的源程序。

在这里插入图片描述

2.7、代表HTTP响应的Response类

Response类表示HTTP响应。它有3个成员变量:code、headerBuffer和content,它们分别表示HTTP响应中的状态代码、响应头和正文。Response类的prepare()方法负责准备HTTP响应的响应头和正文内容,send()方法负责发送HTTP响应的所有数据。下面是Response类的源程序:

在这里插入图片描述

2.8、代表响应正文的Content接口及其实现类

Response类有一个成员变量content,表示响应正文,它被定义为Content类型。

在这里插入图片描述

Content接口表示响应正文,它的定义如下:

在这里插入图片描述

Content接口继承了Sendable接口,Sendable接口表示服务器端可发送给客户的内容,它的定义如下:

在这里插入图片描述

Content接口有StringContent和FileContent两个实现类。StringContent表示字符串形式的正文,FileContent表示文件形式的正文。例如在RequestHandler类的build()方法中,如果HTTP请求方式不是GET和HEAD,就创建一个包含StringContent的Response对象,否则就创建一个包含FileContent的Response对象。

在这里插入图片描述

下面主要介绍FileContent类的实现。FileContent类有一个成员变量fileChannel,它表示读文件的通道。FileContent类的send()方法把fileChannel中的数据发送到ChannelIO的SocketChannel中,如果文件中的所有数据发送完毕,send()方法就返回false。下面是FileContent类的源程序。

在这里插入图片描述

2.9、运行HTTP服务器

运行命令“java HttpServer”,就启动了HTTP服务器。在本范例的root目录下存放了各种供浏览器访问的文档,比如login.htm、hello.htm和data.rar文件等。打开IE浏览器,输入URL:http://localhost/login.htm或者http://localhost/data.rar,就能接收到服务器发送过来的相应文档。如果浏览器按照POST方式访问hello.htm,服务器就会返回HTTP405错误,因为本服务器不支持POST方式。

3、总结

HTTP是目前使用非常广泛的应用层协议,它规定了在网络上传输文档(主要是HTML格式的网页)的规则。HTTP的客户程序主要是浏览器。浏览器访问一个远程HTTP服务器上的网页的步骤如下:

HTTP服务器必须接收HTTP请求,对它进行解析,然后返回相应的HTTP响应结果。本章创建了一个非阻塞的HTTP服务器,它首先读取HTTP请求,把它们存放在字节缓冲区内,当缓冲区的容量不够时,会扩充它的容量,以保证容纳HTTP请求的所有数据。接着,程序把字节缓冲区内的字节转换为字符串,对其进行解析,获得HTTP请求中的请求方式、URI和协议版本等信息,然后创建相应的HTTP响应,把它发送给客户程序。

到此这篇关于Java创建非阻塞的HTTP服务器的实现的文章就介绍到这了,更多相关Java 非阻塞HTTP服务器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文