本文共 9506 字,大约阅读时间需要 31 分钟。
HTTP是一个属于应用层的面向对象的协议,其主要特点如下。
HTTP请求由三部分组成,分别为:HTTP请求行、HTTP请求头、HTTP请求正文。
以一个方法符开头,以空格分开,后面跟着请求的URI和协议的版本,格式为:
Method Request-URI HTTP-Version CRLF其中Method表示请求方法(GET/POST/DELETE等),Request-URI是一个统一资源标识符,HTTP-Version表示请求的HTTP协议版本,CRLF表示回车和换号
包含表示请求的各种条件和属性的各类首部信息
这部分内容是可选的,请求正文通常都是应该被发送的数据
完整示例:
HTTP响应也是由三个部分组成,分别是状态行、消息报头、响应正文。
包含表明响应结果的状态码、原因短语和HTTP版本
包含表示请求的各种条件和属性的各类首部
即服务端发送给客户端的数据,可以是文本,xml,json等。
完整示例:
在HTTP工作开始之前,客户端首先要通过网络与服务器建立连接,HTTP连接是通过TCP来完成的,HTTP是比TCP更高层次的应用层协议,根据规则,只有底层协议建立之后,才能进行高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80
#5、客户端解析html代码,并请求html代码中的资源
浏览器拿到html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载
一般情况下,一旦服务器向客户单返回了请求数据,它就要关闭TCP连接,但是如果客户端或者服务器在其头信息中加入了这行代码 Conneciton:keep-alive,TCP连接在发送后将仍然保持打开状态,于是,客户端可以继续通过相同的连接发送请求,也就是说前面的3到6步,可以反复进行。保持连接节省了为每个请求建立新连接所需要的时间,还节约了网络带宽。
将 HttpRequest、HttpContent 和 LastHttpContent 消息编码为字节
将 HttpResponse、HttpContent 和 LastHttpContent 消息编码为字节
将字节解码为 HttpRequest、HttpContent 和 LastHttpContent 消息
将字节解码为 HttpResponse、HttpContent 和 LastHttpContent 消息
如图所示:一个HTTP 请求/响应可能由多个数据部分组成,FullHttpRequest 和FullHttpResponse 消息是特殊的子类型,分别代表了完整的请求和响应。所有类型的 HTTP 消息(FullHttpRequest、 LastHttpContent 等等)都实现了 HttpObject 接口。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(); }}
除了浏览器之外,当然也可以自己构建客户端
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/