Netty 入门实战

简单介绍Netty的核心概念,这个很新概念就是学习Netty是如何拦截和处理异常,对于刚开始学习netty的读者,利用netty的异常拦截机制来调试程序问题很有帮助。本章还会介绍其他一些核心概念,如服务器和客户端的启动以及分离通道的处理程序。本章学习一些基础以便后面章节的深入学习。本章中将编写一个基于netty的服务器和客户端来互相通信,我们首先来设置netty的开发环境。

编写一个应答服务器

写一个Netty服务器主要由两部分组成:
• 配置服务器功能,如线程、端口
• 实现服务器处理程序,它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么

启动服务器
通过创建ServerBootstrap对象来启动服务器,然后配置这个对象的相关选项,如端口、线程模式、事件循环,并且添加逻辑处理程序用来处理业务逻辑。

package cn.org.july.netty.one.demo.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Created with IntelliJ IDEA.
 * User:  wanghongjie
 * Date:  2019/4/26 - 22:43
 * <p>
 * Description:
 */
public class EchoServer {
    /**
     * 服务端口号
     */
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void start() throws InterruptedException {
        EventLoopGroup executors = new NioEventLoopGroup();
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(executors, group).channel(NioServerSocketChannel.class).localAddress(port)
                    .childHandler(new ChannelInitializer<Channel>() {
                        protected void initChannel(Channel channel) throws Exception {
                            channel.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            ChannelFuture f = serverBootstrap.bind().sync();
            System.out.println(EchoServer.class.getName() + "started and listen on “" + f.channel().localAddress());
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            executors.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) {
        EchoServer echoServer = new EchoServer(5210);
        try {
            echoServer.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

实现服务器业务逻辑
Netty使用futures和回调概念,它的设计允许你处理不同的事件类型,更详细的介绍将再后面章节讲述,但是我们可以接收数据。你的channel handler必须继承ChannelInboundHandlerAdapter并且重写channelRead方法,这个方法在任何时候都会被调用来接收数据,在这个例子中接收的是字节。

package cn.org.july.netty.one.demo.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.io.UnsupportedEncodingException;

/**
 * Created with IntelliJ IDEA.
 * User:  wanghongjie
 * Date:  2019/4/26 - 22:51
 * <p>
 * Description:
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] tempBuf = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(tempBuf);
        System.out.println("Server received: " + new String(tempBuf, "UTF-8"));
        ctx.writeAndFlush(Unpooled.copiedBuffer(tempBuf));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws UnsupportedEncodingException {
        System.out.println("channelActive:" + ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

编写应答程序的客户端
服务器写好了,现在来写一个客户端连接服务器。应答程序的客户端包括以下几步:
• 连接服务器
• 写数据到服务器
• 等待接受服务器返回相同的数据
• 关闭连接
引导客户端
引导客户端启动和引导服务器很类似,客户端需同时指定host和port来告诉客户端连接哪个服务器。

package cn.org.july.netty.one.demo.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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 java.net.InetSocketAddress;

/**
 * Created with IntelliJ IDEA.
 * User:  wanghongjie
 * Date:  2019/4/26 - 23:08
 * <p>
 * Description:
 */
public class EchoClient {
    private final int port;
    private final String host;

    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture f = bootstrap.connect().sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient("localhost", 5210).start();
    }

}

实现客户端的业务逻辑

package cn.org.july.netty.one.demo.client;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

/**
 * Created with IntelliJ IDEA.
 * User:  wanghongjie
 * Date:  2019/4/27 - 09:42
 * <p>
 * Description:
 */
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {

        System.out.println(byteBuf.toString(CharsetUtil.UTF_8));
        byte[] tempBuf = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(tempBuf);
        System.out.println("netty client send msg is : " + new String(tempBuf, "UTF-8"));
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Netty !",
                CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}

在这里使用的是SimpleChannelInboundHandler而不使用ChannelInboundHandlerAdapter,主要原因是ChannelInboundHandlerAdapter在处理完消息后需要负责释放资源。在这里将调用ByteBuf.release()来释放资源。SimpleChannelInboundHandler会在完成channelRead0后释放消息,这是通过Netty处理所有消息的ChannelHandler实现了ReferenceCounted接口达到的。
为什么在服务器中不使用SimpleChannelInboundHandler呢?因为服务器要返回相同的消息给客户端,在服务器执行完成写操作之前不能释放调用读取到的消息,因为写操作是异步的,一旦写操作完成后,Netty中会自动释放消息。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Netty入门实战: 仿写微信IM即时通讯系统》是一本关于网络编程框架Netty的实践指南。本书以仿写微信IM即时通讯系统为例,通过实际的项目案例引导读者学习和掌握Netty的使用。 Netty是一款基于Java的网络编程框架,提供了高性能、异步、事件驱动的特性。在本书中,作者基于Netty框架,通过分析微信IM即时通讯系统的架构和功能需求,逐步引入Netty的概念和使用方法。 首先,本书介绍了Netty的基本概念和工作原理,解释了Netty的事件驱动模型以及异步IO操作的优势。接着,读者会学习到如何使用Netty构建网络服务器和客户端,以及如何处理网络通信中的数据包、编解码、心跳检测等问题。同时,本书也强调了Netty在高并发情况下的性能优势,示范了如何使用Netty进行性能优化和扩展。 通过跟随本书的实例代码,读者将逐步了解和掌握Netty的各项功能和使用方法。同时,通过仿写微信IM即时通讯系统的实践项目,读者也能够更好地理解Netty框架在实际项目中的应用场景和解决方案。 总而言之,《Netty入门实战: 仿写微信IM即时通讯系统》通过实际案例的方式,帮助读者深入理解Netty框架的使用和原理,并将其应用于实际的项目中。无论是对于新手还是有一定经验的开发者来说,本书都是一个很好的学习和实践指南,能够帮助读者快速入门和提升自己在网络编程领域的技能水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Julywhj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值