博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《Netty权威指南 第2版》学习笔记(6)--- HTTP协议开发应用
阅读量:2497 次
发布时间:2019-05-11

本文共 9506 字,大约阅读时间需要 31 分钟。

HTTP协议介绍

HTTP是一个属于应用层的面向对象的协议,其主要特点如下。

  • 支持Client、Server模式
  • 简单—客户向服务器请求服务时,只需要指定服务URL,携带必要的请求参数或者消息体
  • 灵活—HTTP允许传输任意类型的数据对象,传输的内容类型由HTTP消息头中的Content-Type去标记
  • 无状态—HTTP协议是无状态协议,无状态是指协议对于事务处理没有记忆能力,缺少状态意味着如果后续处理需要之前的信息,则它必须重传,这样可能导致每次连接传送的数据量增大,另一方面,在服务器不需要先前信息时它的应答就较快,负载较轻

HTTP请求消息

HTTP请求由三部分组成,分别为:HTTP请求行、HTTP请求头、HTTP请求正文。

请求行

以一个方法符开头,以空格分开,后面跟着请求的URI和协议的版本,格式为:

Method Request-URI HTTP-Version CRLF

其中Method表示请求方法(GET/POST/DELETE等),Request-URI是一个统一资源标识符,HTTP-Version表示请求的HTTP协议版本,CRLF表示回车和换号

请求头

包含表示请求的各种条件和属性的各类首部信息

请求正文

这部分内容是可选的,请求正文通常都是应该被发送的数据

完整示例:

在这里插入图片描述

HTTP响应消息

HTTP响应也是由三个部分组成,分别是状态行、消息报头、响应正文。

状态行

包含表明响应结果的状态码、原因短语和HTTP版本

消息报头

包含表示请求的各种条件和属性的各类首部

响应正文

即服务端发送给客户端的数据,可以是文本,xml,json等。

完整示例:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

HTTP请求过程

1、首先进行DNS域名解析(本地浏览器缓存、操作系统缓存或者DNS服务器)

  • 首先会搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存)
  • 如果浏览器自身的缓存里面没有找到,那么浏览器会搜索系统自身的DNS缓存
  • 如果还没找到,那么尝试从hosts文件里面去找
  • 在前面三个过程都没获取到的情况下,就去域名服务器查找

2、三次握手建立TCP连接

在HTTP工作开始之前,客户端首先要通过网络与服务器建立连接,HTTP连接是通过TCP来完成的,HTTP是比TCP更高层次的应用层协议,根据规则,只有底层协议建立之后,才能进行高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80

3、客户端发起HTTP请求

4、服务器响应HTTP请求

#5、客户端解析html代码,并请求html代码中的资源

浏览器拿到html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载

6、客户端渲染展示内容

7、关闭TCP连接

一般情况下,一旦服务器向客户单返回了请求数据,它就要关闭TCP连接,但是如果客户端或者服务器在其头信息中加入了这行代码 Conneciton:keep-alive,TCP连接在发送后将仍然保持打开状态,于是,客户端可以继续通过相同的连接发送请求,也就是说前面的3到6步,可以反复进行。保持连接节省了为每个请求建立新连接所需要的时间,还节约了网络带宽。

Netty提供的HTTP编解码器

HttpRequestEncoder

将 HttpRequest、HttpContent 和 LastHttpContent 消息编码为字节

HttpResponseEncoder

将 HttpResponse、HttpContent 和 LastHttpContent 消息编码为字节

HttpRequestDecoder

将字节解码为 HttpRequest、HttpContent 和 LastHttpContent 消息

HttpResponseDecoder

将字节解码为 HttpResponse、HttpContent 和 LastHttpContent 消息

在这里插入图片描述

在这里插入图片描述

如图所示:一个HTTP 请求/响应可能由多个数据部分组成,FullHttpRequest 和FullHttpResponse 消息是特殊的子类型,分别代表了完整的请求和响应。所有类型的 HTTP 消息(FullHttpRequest、 LastHttpContent 等等)都实现了 HttpObject 接口。

构建基于Netty的HTTP服务端

import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.http.HttpContentCompressor;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpRequestDecoder;import io.netty.handler.codec.http.HttpResponseEncoder;public class HttpServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup(); try {
ServerBootstrap b = new ServerBootstrap(); b.group(group) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer
() {
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); //对Response 响应报文进行编码 pipeline.addLast("encoder",new HttpResponseEncoder()); //对Request 请求报文进行解码 pipeline.addLast("decoder",new HttpRequestDecoder()); /* 聚合http为一个完整的报文, 它可以将多个消息部分合并为FullHttpRequest或者FullHttpResponse消息, 1*1024*1024规定了聚合内容的最大长度为1M,如果超过这个长度将会返回:413 Request Entity Too Large */ pipeline.addLast("aggregator",new HttpObjectAggregator(1*1024*1024)); /* 当使用HTTP时,建议开启压缩功能以尽可能多的减少传输数据的大小,特别是对文本数据来说,虽然压缩会带来一些CPU上的开销。 */ pipeline.addLast("compressor",new HttpContentCompressor()); //业务handler pipeline.addLast(new HttpServerHandler()); } }); ChannelFuture future = b.bind(8088).sync(); future.channel().closeFuture().sync(); } finally {
group.shutdownGracefully(); } }}
import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.codec.http.*;import io.netty.util.CharsetUtil;public class HttpServerHandler extends ChannelInboundHandlerAdapter {
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端连接信息:" + ctx.channel().remoteAddress()); } private void response(ChannelHandlerContext ctx, String context, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8)); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8"); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
FullHttpRequest httpRequest = (FullHttpRequest) msg; try {
String uri = httpRequest.uri(); String content = httpRequest.content().toString(CharsetUtil.UTF_8); HttpMethod method = httpRequest.method(); System.out.println("httpRequest uri:" + uri); System.out.println("httpRequest method: " + method); System.out.println("httpRequest content: " + content); if (!"/test".equals(uri)) {
response(ctx, "非法请求!" + uri, HttpResponseStatus.BAD_REQUEST); return; } response(ctx, "hello client", HttpResponseStatus.OK); return; } finally {
httpRequest.release(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace(); }}

请求结果

在这里插入图片描述

除了浏览器之外,当然也可以自己构建客户端

HTTP客户端

import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.http.HttpContentDecompressor;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpRequestEncoder;import io.netty.handler.codec.http.HttpResponseDecoder;public class HttpClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup(); try {
Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer
() {
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); //和服务端正好相反,对Response 响应报文进行解码 pipeline.addLast(new HttpResponseDecoder()); //对Request 请求报文进行编码 pipeline.addLast(new HttpRequestEncoder()); //聚合http pipeline.addLast(new HttpObjectAggregator(1*1024*1024)); //解压缩 pipeline.addLast(new HttpContentDecompressor()); //业务handler pipeline.addLast(new HttpClientHandler()); } }); ChannelFuture future = b.connect("127.0.0.1", 8088).sync(); future.channel().closeFuture().sync(); } finally {
group.shutdownGracefully(); } }}
import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.codec.http.*;import io.netty.util.CharsetUtil;import java.net.URI;public class HttpClientHandler extends ChannelInboundHandlerAdapter {
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception {
URI uri = new URI("/test"); //构建http请求 FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toString(), Unpooled.copiedBuffer("你好,服务端!", CharsetUtil.UTF_8)); httpRequest.headers().set(HttpHeaderNames.HOST, "127.0.0.1"); httpRequest.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, httpRequest.content().readableBytes()); // 发送http请求 ctx.writeAndFlush(httpRequest); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
FullHttpResponse httpResponse = (FullHttpResponse) msg; System.out.println("httpResponse status: " + httpResponse.status()); System.out.println("httpResponse header: " + httpResponse.headers()); System.out.println("httpResponse content: " + httpResponse.content().toString(CharsetUtil.UTF_8)); httpResponse.release(); }}

请求结果

客户端

在这里插入图片描述
服务端
在这里插入图片描述

转载地址:http://lqlrb.baihongyu.com/

你可能感兴趣的文章
vnpy学习_04回测评价指标的缺陷
查看>>
ubuntu终端一次多条命令方法和区别
查看>>
python之偏函数
查看>>
vnpy学习_06回测结果可视化改进
查看>>
读书笔记_量化交易如何建立自己的算法交易01
查看>>
设计模式03_工厂
查看>>
设计模式04_抽象工厂
查看>>
设计模式05_单例
查看>>
设计模式06_原型
查看>>
设计模式07_建造者
查看>>
设计模式08_适配器
查看>>
设计模式09_代理模式
查看>>
设计模式10_桥接
查看>>
设计模式11_装饰器
查看>>
设计模式12_外观模式
查看>>
设计模式13_享元模式
查看>>
设计模式14_组合结构
查看>>
设计模式15_模板
查看>>
海龟交易法则01_玩风险的交易者
查看>>
CTA策略02_boll
查看>>