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部分构成,分别是:

  • 请求方法、URI、HTTP的版本
  • 请求头(Request Header)
  • 请求正文(Request Content)

下面是一个HTTP请求的例子:
在这里插入图片描述

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

HTTP请求的第1行包括请求方式、URI和协议版本这3项内容,以空格分开:
在这里插入图片描述
在以上代码中,“POST” 表示请求方式,“/hello”表示URI,“HTTP/1.1”表示HTTP的版本。

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

  • GET:这种请求方式最为常见,客户程序通过这种请求方式访问服务器上的一个文档,服务器把文档发送给客户程序。
  • POST:客户程序可通过这种方式发送大量信息给服务器。在HTTP请求中除了包含要访问的文档的URI,还包括大量的请求正文,这些请求正文中通常会包含大量HTML表单数据。
  • HEAD:客户程序和服务器之间交流一些内部数据,服务器不会返回具体的文档。当使用GET和POST方法时,服务器最后都将特定的文档返回给客户程序。而HEAD请求方式则不同,它仅仅交流一些内部数据,这些数据不会影响用户浏览网页的过程,可以说对用户是透明的。HEAD请求方式通常不单独使用,而是为其他请求方式起辅助作用。一些搜索引擎使用HEAD请求方式来获得网页的标志信息,还有一些HTTP服务器在进行安全认证时,用这个方式来传递认证信息。
  • PUT:客户程序通过这种方式把文档上传给服务器。
  • DELETE:客户程序通过这种方式来删除远程服务器上的某个文档。客户程序可以利用PUT和DELETE请求方式来管理远程服务器上的文档。

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的版本、状态代码、描述
  • 响应头(Response Header)
  • 响应正文(Response Content)

下面是一个HTTP响应的例子:
在这里插入图片描述

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

HTTP响应的第1行包括服务器使用的HTTP的版本、状态代码,以及对状态代码的描述,这3项内容之间以空格分割。在本例中,使用HTTP1.1,状态代码为200,该状态代码表示服务器已经成功地处理了客户端发出的请求。
在这里插入图片描述
状态代码是一个3位整数,以1、2、3、4或5开头:

  • 1xx:信息提示,表示临时的响应。
  • 2xx:响应成功,表明服务器成功的接收了客户端请求。
  • 3xx:重定向。
  • 4xx:客户端错误,表明客户端可能有问题。
  • 5xx:服务器错误,表明服务器由于遇到某种错误而不能响应客户请求。

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

  • 200:响应成功。
  • 400:错误的请求。客户发送的HTTP请求不正确。
  • 404:文件不存在。在服务器上没有客户要求访问的文档。
  • 405:服务器不支持客户的请求方式。
  • 500:服务器内部错误。

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服务器上的网页的步骤如下:

  • (1)建立与远程服务器的连接。
  • (2)发送HTTP请求。
  • (3)接收HTTP响应,断开与远程服务器的连接。
  • (4)展示HTTP响应中的网页内容。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/225836.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

拼多多选品大作战:利用类目榜单找到潜力爆品

想要在激烈的电商竞争中脱颖而出,选品是至关重要的一环。 而拼多多提供的类目榜单数据,为商家们提供了一个寻找热门产品和趋势的利器。本文将详细介绍如何利用拼多多类目榜单进行选品,并帮助您找到畅销产品。 拼多多新手选品核心两要素&…

ARCGIS 中使用 ChatGPT 的 5 种方式

ChatGPT 一度成为最热门的话题。什么是 ChatGPT?谁能比 ChatGPT 本身更好地回答这个问题呢?我们要求它写一个关于 ChatGPT 是什么的简短描述,这是它的回应: ChatGPT 是一个聊天机器人,使用 OpenAI 开发的 GPT-3 语言模…

【AI】VIT Transformer论文学习笔记

论文:Dosovitskiy A, Beyer L, Kolesnikov A, et al. An image is worth 16x16 words: Transformers for image recognition at scale[J]. arXiv preprint arXiv:2010.11929, 2020 1.文章背景 计算机视觉当前最热门的两大基础模型就是Transformer和CNN了。 Transf…

1146-table performance-schema.session_variables don‘t exits打卡navicat连接MySQL报错

navicat连接MySQL时报错: 管理员权限打开cmd 输入下面代码: mysql_upgrade -u root -p --force输入密码 然后就可以正常连接了。 mysql_upgrade检查所有数据库中与mysql服务器当前版本不兼容的所有表。 mysql_upgrade也会升级系统表,以便你…

mysql的组合查询

mysql的组合查询 1、mysql的内连接查询 在 MySQL 中,内连接(INNER JOIN)是一种根据两个或多个表之间的匹配条件,将多个表中的数据进行联接的操作。内连接只返回符合联接条件的行,而不会返回未匹配的行。 内连接的语…

故宫博物院与周大福珠宝集团 战略合作签约仪式在京举行

12月5日上午,故宫博物院与周大福珠宝集团战略合作签约仪式在故宫博物院故宫文化资产数字化应用研究所举行。文化和旅游部党组成员、故宫博物院院长王旭东,国际儒学联合会常务副会长、原文化部副部长丁伟,国际儒学联合会特别顾问、中国国际友好…

【项目】学生信息管理系统

概述 本系统总耗时 6 6 6 天,系统包括 学生发展与数据驱动平台6.2.cpp、学生信息.txt、用户账号.txt、注意事项.txt。由于代码对文件的调用使用的是相对路径,所以要求这 4 4 4 个文件都需要在同一目录。使用代码前先仔细看 注意事项。 如图&#xff1…

数据分析基础之《matplotlib(4)—柱状图》

一、柱状图绘制 1、柱状图要素 有类别 2、需求:对比每部电影的票房收入 电影数据如下图所示: 3、matplotlib.pyplot.bar(x, height, width0.8, bottomNone, *, aligncenter, dataNone, **kwargs) 说明: x:有几个类别 height&am…

ROS小练习——参数设置

目录 一、参数名获取 二、参数修改 1、代码修改 C python 2、命令行修改 3、启动时修改 4、launch文件传参修改 一、参数名获取 rosparam list 二、参数修改 1、代码修改 C #include "ros/ros.h"int main(int argc, char *argv[]) {ros::init(argc,argv,…

接口自动化测试之Yaml数据驱动封装!

一、数据驱动:pytest.mark.parametrize() 首先看个样本: import pytestclass TestData:# parametrize有两个值,一个是args_name:参数名,一个是args_value:参数值,可以有多个,进行数据解包# args_value可以…

智慧农业技术解决方案:PPT全文32页,附下载

关键词:智慧农业解决方案,数字农村,数字农业,智慧农业大数据平台,智慧农业技术方案 一、智慧农业技术平台建设方案 1、全球化和信息化趋势 随着全球化和信息化的加速发展,农业领域也面临着前所未有的挑战…

5分钟搞懂ECN

ECN是通过在IP和TCP头中携带拥塞信息,通知发送方网络拥塞状态,从而采取相应拥塞控制措施。原文: What is ECN(Explicit Congestion Notification)?[1] ECN是Explicit Congestion Notification的缩写,意思是显式拥塞通知算法,和慢…

【LeetCode】2621. 睡眠函数

睡眠函数 Promise异步 题目题解 题目 请你编写一个异步函数,它接收一个正整数参数 millis ,并休眠 millis 毫秒。要求此函数可以解析任何值。 示例 1: 输入:millis 100 输出:100 解释: 在 100ms 后此异步…

Java多线程:代码不只是在‘Hello World‘

Java线程好书推荐 概述01 多线程对于Java的意义02 为什么Java工程师必须掌握多线程03 Java多线程使用方式04 如何学好Java多线程写在末尾: 主页传送门:📀 传送 概述 摘要:互联网的每一个角落,无论是大型电商平台的秒杀…

<JavaEE> 多线程编程中的“等待和通知机制”:wait 和 notify 方法

目录 一、等待和通知机制的概念 二、wait() 方法 2.1 wait() 方法的使用 2.2 超时等待 2.3 异常唤醒 2.4 唤醒等待的方法 三、notify() 方法 四、notifyAll() 方法 五、wait 和 sleep 的对比 一、等待和通知机制的概念 1)什么是等待和通知机制&#xff1f…

若依框架的搭建

若依框架 若依框架的搭建(前后端分离版本)环境要求IDEA拉取Gitee源码Mysql 配置Redis 配置后端启动前端配置问题解决 效果展示 若依框架的搭建(前后端分离版本) 简介 RuoYi-Vue 是一个 Java EE 企业级快速开发平台,基…

线程池(Linux +C)

参考 手写线程池 - C语言版 | 爱编程的大丙 (subingwen.cn) 目录 1.为什么需要线程池? 1)线程问题: 2)如何解决线程问题(线程池的优势): 2.线程池是什么? 1)线程的…

无公网IP环境Windows系统使用VNC远程连接Deepin桌面

🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,…

Hadoop学习笔记(HDP)-Part.19 安装Kafka

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

使用pandas制作图表

数据可视化对于数据分析的重要性不言而喻,一个优秀的图表有足以一眼就看出关键所在。pandas利用matplotlib实现绘图。能够提供各种各样的图表功能,包括: 单折线图多折线图柱状图叠加柱状图水平叠加柱状图直方图拆分直方图箱型图区域块图形散点图饼图多子…