功能:
Netty服务器在6668端口监听,浏览器发出请求"http://localhost:6668"
服务器可以恢复消息给浏览器:“hello,我是服务器”,并对特定请求资源进行过滤
目的:
Netty可以做服务器端开发,并且理解handle实例和客户端及其请求的关系.
服务端代码(Server):
package com.liubujun.netty.http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @Author: liubujun
* @Date: 2023/2/11 15:06
*/
public class TestServer {
public static void main(String[] args) throws Exception{
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//创建服务器端的启动对象,配置参数
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)//设置两个线程组
.channel(NioServerSocketChannel.class) //使用nioSocketChannel作为服务器的通道实现
.childHandler(new TestServerInitalizer());
//绑定一个端口并且同步,生成了一个ChannelFuture对象
//启动服务器(并绑定端口)
ChannelFuture cf = serverBootstrap.bind(8080).sync();
//对关联通道进行监听
cf.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
ServerInitalizer:
public class TestServerInitalizer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//向管道加入处理器
//得到管道
ChannelPipeline pipeline = ch.pipeline();
//加入一个netty 提供的HttpServerCodec
//1.HttpServerCodec是netty提供的处理http的编-解码器
pipeline.addLast("MyHttpServerCodec",new HttpServerCodec());
//2.增加一个自定义的handler
pipeline.addLast("MyTestHttpServerHandler",new TestHttpServerHandler());
}
}
HttpServerHandler:
package com.liubujun.netty.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
/**
* @Author: liubujun
* @Date: 2023/2/11 15:07
*/
/**
* 说明:
* 1. SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter
* 2 HttpObject客户端和服务端相互通讯的数据被封装成HttpObject
*/
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
//channelRead0 读取客户端数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//判断msg是不是httpRequest请求
if (msg != null){
System.out.println("msg 类型="+msg.getClass());
System.out.println("客户端地址"+ctx.channel().remoteAddress());
//回复信息给浏览器
ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
//构造一个http响应,即httpresponse
DefaultFullHttpResponse response =
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//将构建好的response返回
ctx.writeAndFlush(response);
}
}
}
启动服务端,浏览器输入:http://localhost:8080/,得到服务端响应数据
但是服务端控制台却输出了如下请求:
我们只请求了一次,但是浏览器却发出了2次请求,并且对这两个请求都做了响应。
那么如何对这个网站图标进行过滤,使服务器对其不做响应呢?
可以在我们的服务端Handle加上如下代码:
HttpRequest httpRequest = (HttpRequest)msg;
URI uri = new URI(httpRequest.uri());
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求了favicon.ico,不做响应");
return;
}
完整版HttpServerHandler代码如下:
package com.liubujun.netty.http;
import com.sun.jndi.toolkit.url.Uri;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.net.URI;
/**
* @Author: liubujun
* @Date: 2023/2/11 15:07
*/
/**
* 说明:
* 1. SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter
* 2 HttpObject客户端和服务端相互通讯的数据被封装成HttpObject
*/
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
//channelRead0 读取客户端数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//判断msg是不是httpRequest请求
if (msg != null){
System.out.println("msg 类型="+msg.getClass());
System.out.println("客户端地址"+ctx.channel().remoteAddress());
HttpRequest httpRequest = (HttpRequest)msg;
URI uri = new URI(httpRequest.uri());
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求了favicon.ico,不做响应");
return;
}
//回复信息给浏览器
ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_16);
//构造一个http响应,即httpresponse
DefaultFullHttpResponse response =
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//将构建好的response返回
ctx.writeAndFlush(response);
}
}
}
再次重新请求发现:已经成功拦截。
注意:
http协议不是一个长连接
每个请求对应的handle和pineline是独立的,不会共享。
对此,可以请求2次并且打印出其hash值进行比对
第一次请求:
第二次请求:
发现连续2次请求所打印的hash值都不一样。