热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Netty结合webSocket做聊天案例

Netty的介绍就不在这里阐述了,前面也写过关于Netty的文章:Netty(一)springboot整合Netty做心跳检测Netty(二)springboot整合netty编写

Netty的介绍就不在这里阐述了,前面也写过关于Netty的文章:

Netty(一) springboot整合Netty做心跳检测

Netty(二) springboot 整合netty编写时间服务器

Netty(三) Netty 之传输文件

这里不做过多的介绍,代码里有相应的注释,

首先添加依赖:

		<dependency>
            <groupId>io.nettygroupId>
            <artifactId>netty-allartifactId>
            <version>4.1.21.Finalversion>
        dependency>

WebsocketServer

@Component
public class WebsocketServer {

    private final static Logger LOGGER = LoggerFactory.getLogger(WebsocketServer.class);
    //初始化主线程(boss线程)
    NioEventLoopGroup mainGroup = new NioEventLoopGroup();
    //初始化从线程池(work线程)
    NioEventLoopGroup subGroup = new NioEventLoopGroup();

    @PostConstruct
    public void start() {
        try {
            //创建服务启动器
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //指定使用主线程和从线程
            serverBootstrap.group(mainGroup, subGroup)
                    //指定使用NIO通道类型
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(9001))
                    //保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //指定通道初始化器用来加载当Channel收到事件消息后,如何进行业务处理
                    .childHandler(new WsServerInitializer());
            //绑定端口启动服务器,并等待服务启动
            ChannelFuture future = serverBootstrap.bind().sync();
            if (future.isSuccess()) {
                LOGGER.info("启动 Netty 成功");
            }
            //等待服务器关闭
            //future.channel().closeFuture().sync();

            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 销毁
     */
    @PreDestroy
    public void destroy() {
        mainGroup.shutdownGracefully().syncUninterruptibly();
        subGroup.shutdownGracefully().syncUninterruptibly();
        LOGGER.info("关闭 Netty 成功");
    }
}

WsServerInitializer(通道初始化)

/**
 * Created by haoxy on 2019/1/4.
 * E-mail:[email protected]
 * github:https://github.com/haoxiaoyong1014
 * 通道初始化器
 * 用来加载通道处理器
 */
public class WsServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        //用于支持 http协议
        //websocket 基于http协议,需要 http的编码器
        pipeline.addLast(new HttpServerCodec())
                //对大数据流的支持
                .addLast(new ChunkedWriteHandler())
                //添加对HTTP请求和响应的聚合器: 只要Netty进行编码都需要使用
                //对HttpMessage进行聚合,聚合成FullHttpRequest获取FullHttpResponse
                .addLast(new HttpObjectAggregator(1024 * 64))

                //-----------支持 webSocket----------
                //需要指定接收请求的路由,处理握手动作(close,ping pong),ping+pOng=心跳
                //必须使用 ws 后缀结尾的 url 才能访问
                .addLast(new WebSocketServerProtocolHandler("/ws"))
                //添加自定义的 handler
                .addLast(new ChatHandler());
    }
}

ChatHandler(自定义业务处理类)

/**
 * Created by haoxy on 2019/1/4.
 * E-mail:[email protected]
 * github:https://github.com/haoxiaoyong1014
 */
//TextWebSocketFrame 在 netty 中是用于webSocket专门处理文本对象,frame是消息的载体.
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {


    private final static Logger LOGGER = LoggerFactory.getLogger(ChatHandler.class);

    //用于记录和管理所有客户端的Channel

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

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd hh:MM");

    //当接收到客户端发过来的消息就会触发此回调
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        String content = textWebSocketFrame.text();
        System.out.println("接收到的数据: " + content);
        //将消息发给其他客户端排除自己

        Channel channel = channelHandlerContext.channel();
        for (Channel ch : channels) {
            //排除当前通道
            if (channel != ch) {
                ch.writeAndFlush(new TextWebSocketFrame(sdf.format(new Date()) + ":" + content));
            }
        }

        /*//将接收的消息发送所有的客户端
        for (Channel channel : channels) {
            channel.writeAndFlush(new TextWebSocketFrame(sdf.format(new Date()) + ":" + content));
        }*/
    }

    //当有新的客户端连接服务器之后,就会自动调用这个方法
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        channels.add(ctx.channel());

    }

}

chat.html(前端代码)

<body>
<input type="text" id="message">
<input type="button" value="发送消息" onclick="sendMsg()">
<br/>
接收到消息:
<p id="server_message" style="background-color: #AAAAAA">p>

<script>
   var websocket = null;
   //判断当前浏览器是否支持 webSocket
   if (window.WebSocket) {
       websocket = new WebSocket("ws://127.0.0.1:9001/ws");
       websocket.onopen = function (ev) {
           console.log("建立连接");
       }
       websocket.onclose = function (ev) {
           console.log("断开连接");
       }
       websocket.onmessage = function (ev) {
           console.log("接收到服务器的消息" + ev.data);
           var server_message = document.getElementById("server_message");
           server_message.innerHTML += ev.data + "
"
; } } else { alert("当前浏览器不支持 webSocket") } function sendMsg() { var message = document.getElementById("message"); websocket.send(message.value) }
script> body>

案例效果图:

窗口A给窗口B发送消息:

Netty结合webSocket做聊天案例

窗口B回复窗口A的消息:

Netty结合webSocket做聊天案例
案例项目地址:

netty-chat


推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • PDO MySQL
    PDOMySQL如果文章有成千上万篇,该怎样保存?数据保存有多种方式,比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择,做We ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 解决.net项目中未注册“microsoft.ACE.oledb.12.0”提供程序的方法
    在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报错“未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序”。本文提供了解决这个问题的方法,包括错误描述和代码示例。通过注册提供程序和修改连接字符串,可以成功读取excel文件信息。 ... [详细]
author-avatar
遗留下的痛cc-x_393
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有