Netty结合WebSocket的聊天室

Netty结合WebSocket的聊天室

首先是Netty的服务器端

  • 由于结合了springboot,所以添加了@Component注解,也可以直接写一个main函数启动
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.stereotype.Component;

/**
 * author:Jerry1ee
 * time:2020.2.1
 *
 */
@Component
public class WebChatServer {

    private static class SingletonWebChatServer{
        static final WebChatServer instance = new WebChatServer();
    }

    public static WebChatServer getInstance()
    {
        return new SingletonWebChatServer().instance;
    }
    private EventLoopGroup bossGroup ;
    private EventLoopGroup workGroup ;
    private ServerBootstrap server;
    private ChannelFuture channelFuture;

    public WebChatServer(){
        bossGroup = new NioEventLoopGroup();
        workGroup = new NioEventLoopGroup();
        server = new ServerBootstrap();
        server.group(bossGroup,workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new WebChatServerInitializer())
                .option(ChannelOption.SO_BACKLOG,128)
                .childOption(ChannelOption.SO_KEEPALIVE,true);
    }
    public void start()
    {
        this.channelFuture = server.bind(8088);
        System.out.println("服务器已经启动...");
    }

}

Netty的服务器初始化器

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;


/**
 * author:Jerry1ee
 * time:2020.2.1
 *
 */
public class WebChatServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {

        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new HttpObjectAggregator(64*1024));
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpRequestHandler("/ws"));
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        pipeline.addLast(new TextWebSocketFrameHandler());


    }
}

Netty的助手类

  • 助手类有自定义的,也有Netty中原本就有的

  • 助手类就是为了添加业务过滤逻辑的,可以处理过来的socket请求

  • 这边一共写了两个助手类

HttpRequestHandler助手类

  • 为了拦截http请求进而转化成为websocket请求的
  • 这一部分参考了别人的代码,不再贴出

发送文本的助手类

package com.lzy.chatmovie.netty;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * author:Jerry1ee
 * time:2020.2.1
 *
 */
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

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

    //有客户端连接时,自动调用,将链接的客户端记录下来
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel income = ctx.channel();

        //进入聊天室
        for (Channel channel : channels) {
                channel.writeAndFlush(new TextWebSocketFrame("欢迎 " + income.remoteAddress() + "进入聊天室!"));
        }
        channels.add(ctx.channel());
        System.out.println(income.remoteAddress()+"加入了!");
    }

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

        //进入聊天室
        for (Channel channel : channels) {
                channel.writeAndFlush(new TextWebSocketFrame(outcome.remoteAddress() + " 离开了!"));
        }
        channels.remove(ctx.channel());
        System.out.println(outcome.remoteAddress()+"离开了!");
    }

    //客户端发送消息时,自动执行
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        Channel income = ctx.channel();//获得发送消息的人的链接通道

        System.out.println(msg.text());
        System.out.println("此时所有的连接:");
        for (Channel channel : channels) {

            System.out.println(channel);
            if (channel != income) {
                channel.writeAndFlush(new TextWebSocketFrame("[用户]" + income.remoteAddress() + "说:" + msg.text()));
            } else {
                channel.writeAndFlush(new TextWebSocketFrame("我说:" + msg.text()));
            }
        }

    }
}

前端(采用了Vue框架)

<template>
  <div class="chat_container">
    <!--面包屑-->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>聊天室测试</el-breadcrumb-item>
    </el-breadcrumb>
    <!--卡片视图区域-->
    <el-card class="box-card">
      <!--聊天表单-->
      <el-form  ref="chatFormRef" :model="chatForm">
        <!--消息框-->
        <el-form-item prop="chatArea">
          <el-input  type="textarea" v-model="chatForm.chatArea" :rows="18"></el-input>
        </el-form-item>

        <el-row :gutter="20">
          <el-col :span="7">
            <!--输入框-->
            <el-form-item prop="message">
              <el-input v-model="chatForm.message" clearable @keyup.enter.native=send> </el-input>
            </el-form-item>

          </el-col>
          <el-col :span="4">
            <!--按钮-->
            <el-form-item class="buttons">
              <el-button type="primary" @click="send">发送消息</el-button>
            </el-form-item>
          </el-col>
        </el-row>

      </el-form>
    </el-card>

  </div>

</template>

<script>
  export default {
    data()
    {
      return{
        chatForm:{
          chatArea:'',
          message:''
        },
        path:"ws://localhost:8088/ws",
        socket:""

      }
    },
    mounted()
    {
      this.init()
    },
    methods:
      {
        init: function () {
          if(typeof(WebSocket) === "undefined"){
            this.$message.warning("您的浏览器不支持socket")
          }else{
            // 实例化socket
            this.socket = new WebSocket(this.path)
            // 监听socket连接
            this.socket.onopen = this.open
            // 监听socket消息
            this.socket.onmessage = this.onmessage
            //监听socket关闭
            this.socket.onclose = this.onclose
        }
      },
        open:function(event) {
          this.chatForm.chatArea = "连接开启!"
        },
        onmessage:function(msg)
        {
          this.chatForm.chatArea = this.chatForm.chatArea+"\n"+msg.data
        },
        onclose:function(){
          this.chatForm.chatArea = this.chatForm.chatArea+"\n连接关闭"
        },
        send:function () {
          if (!window.WebSocket) {
            return;
          }
          if(this.socket.readyState ==WebSocket.OPEN )
          {
            this.socket.send(this.chatForm.message)
            this.chatForm.message=""
          }
          else {
            alert("连接没有开启!")
          }
        }
  }
  }

</script>

<style lang="less" scoped>
  .login_container{
    background-color: #25606b;
    height: 100%;
  }

  /*.chat_form{*/
    /*position: absolute;*/
    /*bottom: 0;*/
    /*width: 100%;*/
    /*right: 0;*/
    /*padding: 0 100px;*/
    /*box-sizing: border-box;*/

  /*}*/

  /*.textarea_form{*/
    /*position: absolute;*/
    /*bottom:10px;*/
    /*width: 50%;*/
    /*right: 0;*/
    /*padding: 0 100px;*/
    /*box-sizing: border-box;*/

  /*}*/
</style>
发布了8 篇原创文章 · 获赞 1 · 访问量 173

猜你喜欢

转载自blog.csdn.net/weixin_43925277/article/details/104314749