主要分为五个步骤
- 设置主从线程组
- 设置服务器启动类
- 为服务器设置channel
- 设置启动类的助手类
- 监听和关闭
接下来解释为什么会有这五步:
为什么设置主从线程组
放张图,讲述线程模型
为什么设置服务器启动类
这个服务器启动类,在我看来,相当于管理中心,他负责整体调度,和初始化设置,比如,我们之前创建了主从线程组,他要管理主从线程组,要不然,之前,创建的线程组,放在那里,怎么用,没人管啊!所以,服务器启动类要管理这些线程组。
为服务器设置channel
channel,可以理解为负责IO操作的组件。设置channel是为了说我建立一个什么样的channel,来处理这些请求(设置channel的这个地方,理解的不是很深刻,如果有大神了解的比较深入,请在评论解释一下哈)
设置启动类的助手类
助手类是说,好了请求来了,现在该怎么处理呢,那这就由助手类来决定了
监听和关闭
主要设置使用哪个端口,同步,还是异步。
好了,上代码
package com.example.netty.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @ClassName HelloServer
* @Description 实现客户端发送一个请求,服务器会返回hello netty
* @Author miaoxu
* @Date 2019/7/28 23:32
* @Version 1.0
**/
public class HelloServer {
public static void main(String[] args) throws InterruptedException {
//主线程组,接受客户端的链接,不做任何处理,和老板一样,不做任何事情
EventLoopGroup bossGroup = new NioEventLoopGroup();
//从线程组,老板线程组把任务给他,让手下线程组做任务
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// netty服务器的创建,ServerBootstrap是一个启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup) // 设置主从线程组
.channel(NioServerSocketChannel.class)// 设置nio的双向通道
.childHandler(new HelloServerInitializer());// 设置字处理器,用于处理workerGroup
ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
//监听关闭的channel,设置位同步方式
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
package com.example.netty.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
/**
* @ClassName HelloServerInitializer
* @Description 初始化器,channel注册后,会执行里面的相应的初始化方法
* @Author miaoxu
* @Date 2019/7/28 23:46
* @Version 1.0
**/
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//通过SocketChannel获取对应的管道
ChannelPipeline channelPipeline = socketChannel.pipeline();
//通过管道,添加handler
// HttpServerCodec是由netty自己添加的助手类,可以理解为拦截器
// 当请求到服务端,我们需要做编码,相应到客户端做编码
channelPipeline.addLast("", new HttpServerCodec());
channelPipeline.addLast("customHandler", new CustomHandler());
}
}
package com.example.netty.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.internal.ChannelUtils;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
/**
* @ClassName CustomHandler
* @Description 创建自定义的助手类
* @Author miaoxu
* @Date 2019/7/28 23:53
* @Version 1.0
**/
//对于请求来讲,相当于入站,入境
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject>{
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
// 获取channel
Channel channel = channelHandlerContext.channel();
if (httpObject instanceof HttpRequest)
{
// 显示客户端的远程地址
System.out.println(channel.remoteAddress());
ByteBuf content = Unpooled.copiedBuffer("Hello netty", CharsetUtil.UTF_8);
// 创建http response
FullHttpResponse 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());
//把相应刷到客户端
channelHandlerContext.writeAndFlush(response);
}
}
}