Netty 实现聊天功能

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();

		// Broadcast a message to multiple Channels
		channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");

		channels.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {  // (3)
		Channel incoming = ctx.channel();

		// Broadcast a message to multiple Channels
		channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");

		// A closed Channel is automatically removed from ChannelGroup,
		// so there is no need to do "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,控制台输入文本继续测试

源码

见 https://github.com/waylau/netty-4-user-guide-demos

时间: 2024-08-05 23:41:35

Netty 实现聊天功能的相关文章

MINA 实现聊天功能

原文同步至 http://www.waylau.com/mina-chat/ 在<MINA 快速入门>一文中,我们介绍了如何利用 MINA 快速构建一个 Time Server(时间服务器).在<Netty 实现聊天功能>一文,我们也介绍了如何用 Netty 实现聊天功能.由于 MINA 和 Netty 是同一个作者,架构类似,如果你掌握其中一个,学习另外一个也不是难事.现在我们就用 MINA 来实现聊天功能. 准备 JDK 7+ Maven 3.2.x MINA 2.x Ecli

[Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

一.引言 在前一篇文章已经详细介绍了SignalR了,并且简单介绍它在Asp.net MVC 和WPF中的应用.在上篇博文介绍的都是群发消息的实现,然而,对于SignalR是为了实时聊天而生的,自然少了不像QQ一样的端对端的聊天了.本篇博文将介绍如何使用SignalR来实现类似QQ聊天的功能. 二.使用SignalR实现端对端聊天的思路 在介绍具体实现之前,我先来介绍了使用SignalR实现端对端聊天的思路.相信大家在前篇文章已经看到过Clients.All.sendMessage(name,

我们一起学习WCF 第九篇聊天功能

说到聊天,那么其实就是传输数据,把自己写的东西传给自己想发送的那么人.我总结一下传输有三种方式 1:就是我们常见的数据库传输 2:就是文件(流)传输 3:就是socket传输 今天我们说的wcf实现聊天其实是基于socket的聊天功能(QQ聊天发展到今天肯定是很牛的了,但是最初肯定也是这样的思想) 今天我先说说基于WCF聊天的原理 1:需要一个回调函数(当用户发送的时候会吧信息回调给客户端本身) 2:需要一个委托(把服务器传来的信息显示给前台) 3:需要一个触发点击事件(目的是为了触发把发送信息

安卓平台基于SIP协议实现注册,聊天功能

============问题描述============ 不涉及音频,视频发送,只要实现注册,和聊天功能就行, 网上下了sipdroid的源码,但是XML中配置的<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4"/> 这段没明白,资料里说安卓最低支持SIP协议的API level是9,但是这版本不匹配呀,有大牛指导下么 ============解决方案1============

Java UDP实现聊天功能代码

我以前经常写的是基于TCP的网络编程,由于TCP建立连接鼻血要经过三次握手连接,服务器端需要阻塞式等待客户端的连接.而UDP则是可以直接向目的地址的目的端口上发送数据包,由于它只负责发送出去就好,不管对方是否正确接受到与否,所以当网络性能不好时它容易出现丢包的问题.(注意:UDP是基于数据报为单位进行传输的,而TCP是一种基于流进行传输的) 但是UDP很好的模拟了我们呢平时聊天的方式,可以很好的实现连续多次发送和接受,也就是简单的QQ聊天的功能. 现在来简要介绍Java中有关UDP编程相关的类:

软件工程腾讯QQ (1)优点:聊天功能比较强大。同时提供安全登陆通道,保障了用户信息的安全性。应用的人群范围比较广 缺点:要展示的信息量太多,用户个人信息容易泄露。有一些弄虚作假通过其欺骗用户上当。 (2)有使用需要自己下载 (3)版本更新修复bug,定期更新版本

1.软件工程未来的发展方向是什么? 2.编程基础不好,我能学习好软件工程吗? 3.软件工程跟物联网之间的区别与联系是什么? 4.软件工程在现实生活中有哪些应用? 5.在软件工程的分类中,如何选择一个适合自己的? 6.软件工程的学习中最难克服的阶段是什么? 7.怎样学好软件工程? 腾讯QQ (1)优点:聊天功能比较强大.同时提供安全登陆通道,保障了用户信息的安全性.应用的人群范围比较广 缺点:要展示的信息量太多,用户个人信息容易泄露.有一些弄虚作假通过其欺骗用户上当. (2)有使用需要自己下载 (

在线聊天功能 实现中俄双语实时翻译 原创 加精!!

要做一个在线实时聊天系统,而且还要实时双语翻译(中.俄).头疼 先找了一个php在线客服系统 带源码的.看懂源码后在上面加翻译功能,一共耗时4天弄出来的. 代码如下: if(mb_detect_encoding($fdata,"UTF-8, GBK") != "UTF-8"){     //$fdata = mb_detect_encoding($fdata,"UTF-8, GBK")     $fdata = mb_convert_encodi

提高逼格,给自己的网站加入智能聊天功能

提高逼格,给自己的网站加入智能聊天功能 引言 现在突然发现有很多 QQ 群都开启了群机器人的功能,其中有两个角色,他们分别是:Baby Q 和 QQ 小冰.在 Q 群中,你可以对他们进行任意程度的调戏,不过,遗憾的是鱼和熊掌不可得兼,一个群只能进行二选一.据说 Baby Q 来自图灵公司,而小冰却是出生自微软公司. 无论是Baby Q,还是小冰,向我们展现的都是人工智能技术.这些产品都是利用深度神经网络算法模仿人脑的运算方式,让机器可以持续的自我学习. 目录 简介 机器人看板 简单调用 API

最新的chart 聊天功能( webpack2 + react + router + redux + scss + nodejs + express + mysql + es6/7)

请表明转载链接:http://www.cnblogs.com/zhangkunweb/p/6853728.html 我是一个喜欢捣腾的人,没事总喜欢学点新东西,可能现在用不到,但是不保证下一刻用不到. 我一直从事的是依赖angular.js 的web开发,但是我怎么能一直用它呢?看看最近火的一塌糊涂的reactjs ,我的天啊,不学会它,怎么能睡好觉. 今天我分享一个依赖最新版本的webpack + react + router + redux + scss + nodejs + mysql +