1. 导包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.50.Final</version>
</dependency>
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
2. 测试类
package com.springCloud.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 实现客户端发送请求, 服务器给予响应
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NettyApplication.class)
public class NettyTest {
@Test
public void test() {
// 创建一组线程组
// 主线程: 用于接受客户端的请求链接, 不做任何处理
EventLoopGroup group_main = new NioEventLoopGroup();
// 从线程: 主线程池会把任务交给它, 让其做任务
EventLoopGroup group_from = new NioEventLoopGroup();
// 创建服务器启动类
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group_main, group_from) // 设置主从线程
.channel(NioServerSocketChannel.class) // 设置 NIO 的双向频道
.childHandler(new NettyServerInitializer()); // 添加子处理器, 用于处理从线程池的任务
try {
// 启动服务并设置端口号, 同时启动方式为同步
ChannelFuture sync = serverBootstrap.bind(8009).sync();
// 监听关闭 Channel, 设置为同步方式.
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. 初始化器
package com.springCloud.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
/**
* 初始化器, channel 注册之后, 会执行里面的相应的初始化方法
*/
public class NettyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 通过 SocketChannel 去获取对应的管道
ChannelPipeline pipeline = socketChannel.pipeline();
// 通过管道添加 handler, HttpServerCode: 是由 netty 自己提供的助手类, 可以理解为拦截器, 当请求到服务器, 我们需要解码, 响应到客户端做解码
pipeline.addLast("HttpServerCodec", new HttpServerCodec());
// 添加自定义助手类, 给客户端浏览器渲染内容
pipeline.addLast("CustomHandler", new CustomHandler());
}
}
3. 自定义助手类
package com.springCloud.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.handler.codec.http.*;
import io.netty.util.CharsetUtil;
/**
* 自定义助手类
*/
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {
/**
*
* @param channelHandlerContext channelHandlerContext
* @param httpObject httpObject
* @throws Exception Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
// 获取 channel
Channel channel = channelHandlerContext.channel();
// 在控制台打印远程地址
System.out.println(channel.remoteAddress());
// 定义向客户端发送的数据内容
ByteBuf content = Unpooled.copiedBuffer("Hello netty", CharsetUtil.UTF_8);
// 定义 Http response
DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
// 为响应增加数据类型和长度
defaultFullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
defaultFullHttpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
// 把响应渲染到 Html 客户端页面上
channelHandlerContext.writeAndFlush(defaultFullHttpResponse);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
System.out.println("Channel 注册");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
System.out.println("Channel 移除");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("Channel 活跃");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
System.out.println("Channel 不活跃");
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
System.out.println("Channel 读取完毕");
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
System.out.println("用户事件触发");
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
super.channelWritabilityChanged(ctx);
System.out.println("Channel 可写更改");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
System.out.println("助手类的添加");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
System.out.println("捕获到异常");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
System.out.println("助手类的移除");
}
}
4. 浏览效果
浏览器
控制台