`
liuzhiqiang19890403
  • 浏览: 60970 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Netty实现网络通信

 
阅读更多

原文同步至 http://www.waylau.com/netty-chat/

Netty 是一个 Java NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。Netty 大大简化了网络程序的开发过程比如 TCP 和 UDP 的 socket 服务的开发。更多关于 Netty 的知识,可以参阅《Netty 4.x 用户指南》(https://github.com/waylau/netty-4-user-guide

下面,就基于 Netty 快速实现一个聊天小程序。

准备

  • JDK 7+
  • Maven 3.2.x
  • Netty 4.x
  • Eclipse 4.x

服务端

让我们从 handler (处理器)的实现开始,handler 是由 Netty 生成用来处理 I/O 事件的。

SimpleChatServerHandler.java

public class SimpleChatServerHandler extends SimpleChannelInboundHandler<String> { // (1)

    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {  // (2)
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
        }
        channels.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {  // (3)
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
        }
        channels.remove(ctx.channel());
    }
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { // (4)
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            if (channel != incoming){
                channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + s + "\n");
            } else {
                channel.writeAndFlush("[you]" + s + "\n");
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
        Channel incoming = ctx.channel();
        System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"在线");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
        Channel incoming = ctx.channel();
        System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"掉线");
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (7)
        Channel incoming = ctx.channel();
        System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常");
        // 当出现异常就关闭连接
        cause.printStackTrace();
        ctx.close();
    }
}

1.SimpleChatServerHandler 继承自 SimpleChannelInboundHandler,这个类实现了 ChannelInboundHandler 接口,ChannelInboundHandler 提供了许多事件处理的接口方法,然后你可以覆盖这些方法。现在仅仅只需要继承 SimpleChannelInboundHandler 类而不是你自己去实现接口方法。

2.覆盖了 handlerAdded() 事件处理方法。每当从服务端收到新的客户端连接时,客户端的 Channel 存入 ChannelGroup列表中,并通知列表中的其他客户端 Channel

3.覆盖了 handlerRemoved() 事件处理方法。每当从服务端收到客户端断开时,客户端的 Channel 移除 ChannelGroup 列表中,并通知列表中的其他客户端 Channel

4.覆盖了 channelRead0() 事件处理方法。每当从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel。其中如果你使用的是 Netty 5.x 版本时,需要把 channelRead0() 重命名为messageReceived()

5.覆盖了 channelActive() 事件处理方法。服务端监听到客户端活动

6.覆盖了 channelInactive() 事件处理方法。服务端监听到客户端不活动

7.exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。在大部分情况下,捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之前发送一个错误码的响应消息。

SimpleChatServerInitializer.java

SimpleChatServerInitializer 用来增加多个的处理类到 ChannelPipeline 上,包括编码、解码、SimpleChatServerHandler 等。

public class SimpleChatServerInitializer extends
        ChannelInitializer<SocketChannel> {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
         ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new SimpleChatServerHandler());

        System.out.println("SimpleChatClient:"+ch.remoteAddress() +"连接上");
    }
}

SimpleChatServer.java

编写一个 main() 方法来启动服务端。

public class SimpleChatServer {

    private int port;

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

    public void run() throws Exception {

        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // (3)
             .childHandler(new SimpleChatServerInitializer())  //(4)
             .option(ChannelOption.SO_BACKLOG, 128)          // (5)
             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            System.out.println("SimpleChatServer 启动了");

            // 绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(port).sync(); // (7)

            // 等待服务器  socket 关闭 。
            // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
            f.channel().closeFuture().sync();

        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();

            System.out.println("SimpleChatServer 关闭了");
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new SimpleChatServer(port).run();

    }
}

1.NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,Netty 提供了许多不同的 EventLoopGroup 的实现用来处理不同的传输。在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。如何知道多少个线程已经被使用,如何映射到已经创建的Channel上都需要依赖于 EventLoopGroup 的实现,并且可以通过构造函数来配置他们的关系。

2.ServerBootstrap 是一个启动 NIO 服务的辅助启动类。你可以在这个服务中直接使用 Channel,但是这会是一个复杂的处理过程,在很多情况下你并不需要这样做。

3.这里我们指定使用 NioServerSocketChannel 类来举例说明一个新的 Channel 如何接收进来的连接。

4.这里的事件处理类经常会被用来处理一个最近的已经接收的 Channel。SimpleChatServerInitializer 继承自ChannelInitializer 是一个特殊的处理类,他的目的是帮助使用者配置一个新的 Channel。也许你想通过增加一些处理类比如 SimpleChatServerHandler 来配置一个新的 Channel 或者其对应的ChannelPipeline 来实现你的网络程序。当你的程序变的复杂时,可能你会增加更多的处理类到 pipline 上,然后提取这些匿名类到最顶层的类上。

5.你可以设置这里指定的 Channel 实现的配置参数。我们正在写一个TCP/IP 的服务端,因此我们被允许设置 socket 的参数选项比如tcpNoDelay 和 keepAlive。请参考 ChannelOption 和详细的 ChannelConfig 实现的接口文档以此可以对ChannelOption 的有一个大概的认识。

6.option() 是提供给NioServerSocketChannel 用来接收进来的连接。childOption() 是提供给由父管道 ServerChannel接收到的连接,在这个例子中也是 NioServerSocketChannel。

7.我们继续,剩下的就是绑定端口然后启动服务。这里我们在机器上绑定了机器所有网卡上的 8080 端口。当然现在你可以多次调用 bind() 方法(基于不同绑定地址)。

恭喜!你已经完成了基于 Netty 聊天服务端程序。

客户端

SimpleChatClientHandler.java

客户端的处理类比较简单,只需要将读到的信息打印出来即可

public class SimpleChatClientHandler extends  SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        System.out.println(s);
    }
}

SimpleChatClientInitializer.java

与服务端类似

public class SimpleChatClientInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new SimpleChatClientHandler());
    }
}

SimpleChatClient.java

编写一个 main() 方法来启动客户端。

public class SimpleChatClient {
    public static void main(String[] args) throws Exception{
            new SimpleChatClient("localhost", 8080).run();
        }

        private final String host;
        private final int port;

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

        public void run() throws Exception{
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap  = new Bootstrap()
                        .group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new SimpleChatClientInitializer());
                Channel channel = bootstrap.connect(host, port).sync().channel();
                BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
                while(true){
                    channel.writeAndFlush(in.readLine() + "\r\n");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                group.shutdownGracefully();
            }

        }
    }

}

运行效果

先运行 SimpleChatServer,再可以运行多个 SimpleChatClient,控制台输入文本继续测试

 

分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    Android基于Netty框架实现通信

    在Android开发中,为了实现高效的网络通信,开发者常常会选择使用Netty框架。Netty是一个高性能、异步事件驱动的网络应用程序框架,适用于多种协议的服务器和客户端应用。本篇文章将详细探讨如何在Android环境中利用...

    Netty实现简单的聊天消息群发功能

    这个项目中的NettyDemo应该包含了上述功能的实现代码,你可以通过阅读和学习这些代码,了解如何在实际项目中应用Netty实现网络通信。同时,对于初学者来说,这是一个很好的实践案例,能够帮助理解Netty的事件驱动...

    netty实现sdtp协议

    这可以通过自定义 Netty 的 `MessageToByteEncoder` 和 `ByteToMessageDecoder` 来实现,将 SDTP 消息转换成字节流以便于网络传输,同时将接收到的字节流解码回 SDTP 消息。 2. **创建 ChannelHandler**:Netty 中...

    Android-netty和socket通信的demo

    本示例“Android-netty和socket通信的demo”将展示如何在Android平台上利用Netty进行网络通信,与传统的Socket编程相结合,实现更加灵活和高效的通信机制。 首先,理解Netty的基本概念是非常重要的。Netty是一个...

    SOFABolt 是蚂蚁集团开发的一套基于 Netty 实现的网络通信框架

    SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架。为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,...

    Netty实现Java服务端和C#客户端联通

    标题中的“Netty实现Java服务端和C#客户端联通”是指使用Netty作为Java服务器框架,与C#客户端(使用DotNetty库)进行通信的一种技术实现。这涉及到跨平台的网络通信,以及两个不同编程语言间的交互。 Netty是Java...

    网络通信 netty_io

    【网络通信:Netty、BIO、NIO与AIO详解】 在计算机科学中,网络通信是实现系统间数据交换的关键技术。Java平台提供了多种I/O模型来支持网络通信,包括传统的阻塞I/O(BIO)、非阻塞I/O(NIO)以及异步I/O(AIO)。...

    netty 实现长连接

    标题中的“netty 实现长连接”指的是使用Netty框架构建能够维持长时间连接的网络通信应用。Netty是一个高性能、异步事件驱动的网络应用程序框架,适用于开发服务器和客户端的高性能、高可用性协议库。在传统的HTTP或...

    基于Netty实现的文件上传

    在IT行业中,网络通信是至关重要的一个领域,特别是在分布式系统和高性能应用中。Netty作为一个高性能、异步事件驱动的网络应用程序框架,被广泛应用于Java领域。本篇将深入探讨如何利用Netty实现文件上传功能,并...

    用netty实现文件传输

    总结,利用 Netty 实现文件传输是一个涉及网络编程、多线程、I/O 操作以及内存管理的综合实践。通过 Netty 的强大功能,我们可以构建高效、可靠的文件传输系统。在实际应用中,还需要考虑到性能优化、安全性以及异常...

    基于Netty实现的UDP通信.zip

    在IT行业中,网络通信是至关重要的一个领域,TCP(传输控制协议)和UDP(用户数据报协议)作为两种主要的传输层协议,各有其特点和应用场景。本项目以"基于Netty实现的UDP通信"为主题,旨在通过源码和教程帮助开发者...

    netty实现的聊天代码

    在这个“netty实现的聊天代码”中,我们可以深入理解如何使用 Netty 框架来构建简单的聊天应用。这个 demo 包括了 server 和 client 两部分,都是基于 Java 语言编写的,因此也涉及到了 Java 编程。 首先,让我们从...

    netty框架各种通信C/S端

    在这个"Netty框架各种通信C/S端"的资源中,包含了使用Netty实现客户端(Client)和服务器(Server)通信的实例。 1. **TCP通信**: TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的...

    Netty实现简单的客户端服务端通信示例

    在本文中,我们将深入探讨如何使用 Netty 实现简单的客户端-服务端通信,这对于初学者来说是一个很好的起点。 首先,理解 Netty 的核心概念至关重要。Netty 的设计基于 NIO(非阻塞 I/O),它利用了 Java 提供的 ...

    Netty UDP协议网络打洞实例

    1. **创建Netty Channel**: 首先,我们需要创建一个基于UDP的Channel,这是Netty处理网络通信的基本单元。通过`NioDatagramChannel`,我们可以监听和发送UDP数据包。 2. **配置Bootstrap**: 使用`Bootstrap`类来...

    基于Netty框架的网络通信项目.zip

    本项目通过多个示例代码,展示了如何使用Netty实现不同类型的网络通信,包括HTTP服务器、WebSocket服务器、Protobuf编解码、Thrift服务等。 项目的主要特性和功能 1. HTTP服务器 实现了一个简单的HTTP服务器,...

    netty实现SSL/TSL双向加密认证示例

    总结,Netty 提供了强大而灵活的工具来实现 SSL/TLS 安全通信,包括双向认证。通过正确配置 `SslContext`、处理通道处理器和管理证书,我们可以构建安全的网络应用。这个示例项目为理解和实践这些概念提供了很好的...

    Netty4编写服务器客户端,自定义编解码,发送自定义消息

    这个项目不仅展示了如何使用 Netty 实现网络通信,还演示了自定义编解码器的重要性,这对于处理各种不同协议和数据格式的网络应用来说是非常关键的。通过这种方式,我们能够灵活地适应不同的消息结构,从而实现高度...

    基于netty的即时通信

    在即时通信领域,Netty作为一个高性能、异步事件驱动的网络应用框架,广泛应用于各种复杂的通信系统。本项目“基于Netty的即时通信”旨在演示如何利用Netty构建一个简单的聊天应用,它包括服务端和客户端,实现了多...

    spring boot 整合的netty 实现的socket的服务端和客户端

    Spring Boot 是一个流行的Java开发框架,它...通过定义自定义的处理器和编码解码器,我们可以灵活地处理网络通信中的各种业务逻辑。同时,利用Spring Boot的自动配置和管理特性,可以使整个系统的集成更加简洁、高效。

Global site tag (gtag.js) - Google Analytics