十.Netty入门到超神系列-基于WebSocket开发聊天室

前言

在这里插入图片描述

在很多的网站中都嵌入有聊天功能,最理想的方式就是使用WebSocket来开发,屏幕面前的你如果不清楚WebSocket的作用可以自己去百度一下,Netty提供了WebSocket支持,这篇文章将使用Netty作为服务器,使用WebSocket开发一个简易的聊天室系统。

服务端

导入依赖

  <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>
    </dependencies>

Netty提供了 WebSocketServerProtocolHandler ,它能够把平台的Http协议升级为WebSocket的WS协议,服务端代码如下

public class NettySocketServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();

        try {
            bootstrap.group(bossGroup, workGroup);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel channel) throws Exception {

                    ChannelPipeline pipeline = channel.pipeline();
                    //设置http编码器
                    pipeline.addLast(new HttpServerCodec());
                    //支持以块的方式写数据,添加块处理器
                    pipeline.addLast(new ChunkedWriteHandler());
                    //http请求是分段的,通过该处理器聚合分段
                    pipeline.addLast(new HttpObjectAggregator(8192));
                    //支持websocket长连接,添加SocketServer处理器,把http升级为ws协议
                    //客户端请求方式为:ws://localhost:8000/hello
                    pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                    //处理请求,执行业务的Hnadler
                    pipeline.addLast(new MyWebSorcketServerHandler());
                }
            });

            //启动服务器
            ChannelFuture sync = bootstrap.bind(8000).sync();
            sync.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

  • 因为使用http,所以需要通过pipeline添加http编码器 HttpServerCodec
  • WebSocketServerProtocolHandler : WebSocket支持,把http升级为ws协议
  • MyWebSorcketServerHandler :该处理器是服务器处理请求的处理器

编写请求处理器

//消息进站处理 ,TextWebSocketFrame:WebSokcet消息是以“帧 Frame”的方式参数,该类是针对Text的数据
public class MyWebSorcketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    //保存所有客户端
    private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private SimpleDateFormat dateFormat = new SimpleDateFormat("mm:ss");
    //读取消息
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        String message = dateFormat.format(new Date())+":%s:"+msg.text();
        System.out.println("收到消息: "+ctx.channel().remoteAddress()+":"+message);
        String sendMsg = String.format(message, ctx.channel().remoteAddress());
        //把消息广播给所有的客户端
        channels.writeAndFlush(new TextWebSocketFrame(sendMsg));
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        String message = dateFormat.format(new Date())+":"+ctx.channel().remoteAddress()+" 加入聊天室";
        //添加客户端的集合
        channels.add(ctx.channel());

        //自动把消息广播给其客户端
        channels.writeAndFlush(new TextWebSocketFrame(message));
        System.out.println("服务器收到链接 ID="+ctx.channel().id().asLongText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        String message = dateFormat.format(new Date())+":"+ctx.channel().remoteAddress()+" 断开连接";
        channels.writeAndFlush(new TextWebSocketFrame(message));
    }

    //客户端退出
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String message = dateFormat.format(new Date())+":"+ctx.channel().remoteAddress()+" 退出聊天室";
        channels.writeAndFlush(new TextWebSocketFrame(message));
        System.out.println(message);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("发生异常 ID="+ctx.channel().id().asLongText()+";msg="+cause.getMessage());
        ctx.channel().close();
    }
}
  • DefaultChannelGroup : 该group用来存储所有的客户端的channel
  • channelRead0 : 当接收到请求拿到数据的方法,需要把数据广播给所有客户端

客户端编写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket测试</title>
</head>
<body>

    <textarea id="viewBody" rows="10" style="width: 500px"></textarea>
    <br/>
    <input id="sendInput" type="text" style="width: 450px">
    <button id="sendBtn" onclick="send()">发送</button>
</body>
<script>
    var socket;
    if(!window.WebSocket){
        alert("浏览器不支持");
    }else{
        //创建客户端
        socket = new window.WebSocket("ws://localhost:8000/hello");
        //当收到消息
        socket.onmessage = function (event) {
            var input = document.getElementById("viewBody");
            input.value = input.value +"\n"+event.data;
        }
        //打开链接
        socket.onopen = function (event) {
            var input = document.getElementById("viewBody");
            input.value = "成功加入聊天室"+"\n";
        }

        //关闭链接
        socket.onclose = function (event) {
            var input = document.getElementById("viewBody");
            input.value = input.value +"\n"+"退出聊天室";
        }

        function send(){
            if(socket && socket.readyState == WebSocket.OPEN){
                let sendInput = document.getElementById("sendInput");
                let value = sendInput.value;
                socket.send(value);
                sendInput.value = "";
            }
        }
    }
</script>
</html>

测试效果

在这里插入图片描述
文章结束,喜欢给个好评吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

墨家巨子@俏如来

你的鼓励是我最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值