基于netty的实时通信

实时通信

  • Ajax 轮训   // 会死循环,如果会有一些数据更新的话
  • Long pull    // 阻塞模型,也是会循环  

        上面两种是被动响应,会比较耗资源

  • websocket  // 是一种协议http1.0不支持长连接,http1.0支持长连接。

        非常主动,不会像上面那么消耗资源,只需建立一次http请求就能长时间连接。

1、编写好Server启动类 

代码如下:

package com.imooc.netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class WSSersver {

  public static void main(String[] args) throws Exception{

    EventLoopGroup mainGroup = new NioEventLoopGroup();
    EventLoopGroup subGroup = new NioEventLoopGroup();

    try {
        ServerBootstrap server = new ServerBootstrap();
        server.group(mainGroup, subGroup)
              .channel(NioServerSocketChannel.class)
              .childHandler(null);

        ChannelFuture future = server.bind(8088).sync();

        future.channel().closeFuture().sync();
    } finally {
        mainGroup.shutdownGracefully();
        subGroup.shutdownGracefully();
    }

  }
}

2、编写初始化器

代码如下:

package com.imooc.netty.websocket;

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;

public class WSServerInitialzer extends ChannelInitializer<SocketChannel> {


  protected void initChannel(SocketChannel socketChannel) throws Exception {
    ChannelPipeline pipeline = socketChannel.pipeline();

    // websocket 基于http协议,所以要有http编解码器
    pipeline.addLast(new HttpServerCodec());

    // 对写大数据流的支持
    pipeline.addLast(new ChunkedWriteHandler());

    // 对httpMessage进行聚合,聚合成FullHttpRequest或FullHttpResponse
    // 几乎在netty中的编程,都会使用到此hanler
    pipeline.addLast(new HttpObjectAggregator(1024 * 64));

    // ===================== 以上是用于支持http协议 ======================


    /**
     * websocket 服务器处理的协议,用于指定给客户端连接访问的路由: /ws
     * 本handler会帮你处理一些繁重的复杂的事
     * 会帮你处理握手动作:handshaking(close,ping,pong) ping + pong = 心跳
     * 对呀websocket来讲,都是以frames进行传输的,不同的数据类型对应的frames也不同
     */
    pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

    // 自定义的handler
    pipeline.addLast(null);


  }

}

3、将初始化器放到childHandler

.childHandler(new WSServerInitialzer());

代码如下:

package com.imooc.netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class WSSersver {

  public static void main(String[] args) throws Exception{

    EventLoopGroup mainGroup = new NioEventLoopGroup();
    EventLoopGroup subGroup = new NioEventLoopGroup();

    try {
        ServerBootstrap server = new ServerBootstrap();
        server.group(mainGroup, subGroup)
              .channel(NioServerSocketChannel.class)
              .childHandler(new WSServerInitialzer());

        ChannelFuture future = server.bind(8088).sync();

        future.channel().closeFuture().sync();
    } finally {
        mainGroup.shutdownGracefully();
        subGroup.shutdownGracefully();
    }

  }
}

4、创建一个ChatHandler的类,用于处理消息的handler

代码如下:

package com.imooc.netty.websocket;

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;

import java.time.LocalDateTime;

/**
 * 处理消息的handler
 * TextWebSocketFrame:在netty中,是用于为websocket专门处理文本的对象,frame是消息的载体
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

  // 用于记录和管理所有客户端的channle
  private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

  @Override
  protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg)
        throws Exception {

      // 获取客户端传输过来的消息
      String content = msg.text();
      System.out.println("接受到的数据:" + content);

      for (Channel channel : clients) {
        channel.writeAndFlush(
              new TextWebSocketFrame(
                      "[服务器在:]" + LocalDateTime.now()
                      + "接受到消息, 消息为:" + content));
      }

      // 下面这个方法,和上面的for循环,一致
//      clients.writeAndFlush(
//        new TextWebSocketFrame(
//          "[服务器在:]" + LocalDateTime.now()
//            + "接受到消息, 消息为:" + content));



  }

  /**
   * 当客户端连接服务端之后(打开链接)
   * 获取客户端的channle,并且放到ChannelGroup中去进行管理
   * @param ctx
   * @throws Exception
   */
  @Override
  public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    clients.add(ctx.channel());
  }

  @Override
  public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    // 当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channel
    // clients.remove(ctx.channel());
    System.out.println("客户端断开,channle对应的长id为:"
                        + ctx.channel().id().asLongText());
    System.out.println("客户端断开,channle对应的短id为:"
                        + ctx.channel().id().asShortText());
  }
}

5、将ChatHandler加入到自定义的handler

// 自定义的handler
pipeline.addLast(new ChatHandler());

Websocket api

  • var socket = new WebSocket("ws://[ip]:[port]");
  •  生命周期:onopen() onmessage() onerror() onclose()
  •  主动方法:Socket.send()    Socket.close()

前端编写 

<template>
  <div>
    <div>发送信息:</div>
    <input type="text" id="msgContent" />
    <input type="button" value="点我发送" @click="chat()" />

    <div>接收消息:</div>
    <div id="receiveMsg" style="background-color: gainsboro">

    </div>
  </div>
</template>

<script>
export default {
  name: "List",
  data: function () {
    return {
      loginUser: {},
      socket: null,
    };
  },
  created() {
    this.initWebSocket();
  },
  destroyed() {
    this.websock.close(); //离开路由之后断开websocket连接
  },
  methods: {
    initWebSocket() {
      //初始化weosocket
      const wsuri = "ws://127.0.0.1:8088/ws";
      this.websock = new WebSocket(wsuri);
      this.websock.onmessage = this.websocketonmessage;
      this.websock.onopen = this.websocketonopen;
      this.websock.onerror = this.websocketonerror;
      this.websock.onclose = this.websocketclose;
    },
    websocketonopen() {
      //连接建立之后执行send方法发送数据
        console.log("连接成功");
    },
    websocketonerror() {
      //连接建立失败重连
      this.initWebSocket();
      console.log("连接成功");
    },
    websocketonmessage(e) {
      //数据接收
      var receiveMsg = document.getElementById("receiveMsg");
      var html = receiveMsg.innerHTML;
      receiveMsg.innerHTML ="<p>" + html + e.data + "</p>" ;
    },
    websocketsend(Data) {
      //数据发送
      this.websock.send(Data);
    },
    websocketclose(e) {
      //关闭
      console.log("断开连接", e);
    },

    chat () {
      var msg = document.getElementById("msgContent");
      this.websocketsend(msg.value);
    }
  },
};
</script>



<style>
</style>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值