netty-socket
socket实现
server
netty服务类
public class NettySocketServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup(8);
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup).option(ChannelOption.SO_BACKLOG, 1024)
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) {
channel.pipeline().addLast(new IdleStateHandler(3, 0, 0, TimeUnit.SECONDS));
channel.pipeline().addLast(new NettySocketHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
netty业务处理类
public class NettySocketHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext chc, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
SocketAddress socketAddress = chc.channel().remoteAddress();
System.out.println("收到客户端:" + socketAddress + "的消息:" + buf.toString(CharsetUtil.UTF_8));
ByteBuf sendBuf = Unpooled.copiedBuffer("收到".getBytes(CharsetUtil.UTF_8));
chc.writeAndFlush(sendBuf);
}
int num = 0;
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
IdleStateEvent event = (IdleStateEvent)evt;
if (event.state().equals(IdleState.READER_IDLE)) {
num++;
}
if (num > 3) {
System.out.println("3次未收到心跳");
ctx.channel().close();
}
}
}
client
netty客户端
public class NettySocketClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup workGroup = new NioEventLoopGroup(8);
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) {
channel.pipeline().addLast(new NettySocketClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9000).sync();
Scanner scanner = new Scanner(System.in);
while (true) {
String msg = scanner.nextLine();
ByteBuf sendBuf = Unpooled.copiedBuffer(msg.getBytes(CharsetUtil.UTF_8));
channelFuture.channel().writeAndFlush(sendBuf);
}
} finally {
workGroup.shutdownGracefully();
}
}
}
netty客户端业务处理类
public class NettySocketClientHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext chc, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
System.out.println("收到服务端发送的消息:" + buf.toString(CharsetUtil.UTF_8));
}
}
netty-websocket
用netty实现websocket
server
websocket服务端
public class NettyWebSocketServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup(8);
try {
ServerBootstrap serverBootstrap =
new ServerBootstrap().group(bossGroup, workGroup).option(ChannelOption.SO_BACKLOG, 1024)
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel channel) throws Exception {
channel.pipeline().addLast(new HttpRequestDecoder());
channel.pipeline().addLast(new HttpResponseEncoder());
channel.pipeline().addLast(new HttpObjectAggregator(8192));
channel.pipeline().addLast(new WebSocketServerProtocolHandler("/ws"));
channel.pipeline().addLast(new NettyWebSocketHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
websocket业务处理类
public class MyHttpChannelHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext chc, Object msg) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=utf-8");
response.content().writeBytes("你好".getBytes());
chc.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
client
在线websocket客户端测试
可以使用上面网站进行测试,连接地址写ws://localhost:9000/ws
netty-http
用netty实现http服务器
server
http服务
public class HttpNettyService {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup(8);
try {
ServerBootstrap serverBootstrap = new ServerBootstrap()
// 设置工作组
.group(bossGroup, workGroup)
// 设置管道类型
.channel(NioServerSocketChannel.class)
// 设置参数
.option(ChannelOption.SO_BACKLOG, 1024)
// 设置handler
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(new HttpRequestDecoder());
channel.pipeline().addLast(new HttpResponseEncoder());
channel.pipeline().addLast(new MyHttpChannelHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
http业务处理类
public class MyHttpChannelHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext chc, Object msg) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=utf-8");
response.content().writeBytes("你好".getBytes());
chc.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
client
浏览器访问:http://localhost:9000
总结
- netty底层是对nio的封装,使用户只需关系具体业务实现,操作更加简单
- BossGroup(NioEventLoop线程组)用来处理连接请求
- WorkerGroup(NioEventLoop线程组,默认逻辑核数*2)用来处理读写请求
- netty默认使用零拷贝技术,接收到的数据放到直接内存中(DirectByteBuffer,堆外内存)零拷贝技术减少用户态到内核态的拷贝io,使读写效率更快(但是申请内存空间效率差,所以netty使用池化技术,提前申请DirectByteBuffer内存池,使用时直接从池中获取)
- 解决nio的空轮训bug:selector.select()方法 nio会有bug,有时候没有事件发生也会触发,并且永远都不会获得事件,cpu会飙升到100%
netty 会记录次数,超过512次后会重新new一个新的select替换掉旧的