<返回更多

netty实现websocket客户端与服务端消息透传

2021-05-24  今日头条  localhost
加入收藏

http协议和ws协议:最直观的特点就是访问地址的写法上略有不同,两种访问分别为http://ip:port、ws://ip:port。http协议访问采用一问一答的方式——客户端发起请求到服务端,服务端收到请求后返回对应的信息,通信结束。虽然可以利用ajax发起异步的http请求,不过都是一次访问后连接就断开(ajax轮询可以解决此问题,对服务器资源消耗较大);ws协议建立于TCP协议之上,我们观察ws的头部信息会发现一件有意思的事情

HttpObjectAggregator$AggregatedFullHttpRequest(decodeResult: success, version: HTTP/1.1, content: CompositeByteBuf(ridx: 0, widx: 0, cap: 0, components=0))
GET / HTTP/1.1
Host: localhost:8888
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Upgrade: websocket
Origin: file://
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Sec-WebSocket-Key: RLnvly29Zl3sJQOfGFjO9w==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
content-length: 0

websocket建立时是以http协议握手,当建立连接之后就不需要http之间双向通信。协议深层次的东西以我目前的能力是无法理解的,将来有机会我会再回过头来分析底层协议。那么接下来就进入我们的主题。

引入maven依赖(结合项目实际情况而定):

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>5.0.0.Alpha2</version>
</dependency>

我们的需求自己既是服务端向其他模块推送消息,又是客户端从别处获取消息后推送其他模块。那么我们要在自己项目中集成netty客户端和服务端,因为客户端无法给多个连接推送消息。这里我主要介绍
SimpleChannelInboundHandler类的三个方法:

1. channelActive

private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
         System.out.println("客户端加入连接:"+ctx.channel());
      //UserChannelUtil封装了channelGroup
        UserChannelUtil.getChannelGroup().add(ctx.channel());
    }

这个方法会在有客户端连接上来时执行,具体作用很多,通常是获取客户端的通道号加入到ChannelGroup中,例如注意这个GlobalEventExecutor.INSTANCE是单例,那么就意味着我们操作的是同一个对象,我们将连接的通道加入后将来就可以实现批量推送。

2.channelInactive

 @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //断开连接
        System.out.println("客户端断开连接:"+ctx.channel());
        UserChannelUtil.getChannelGroup().remove(ctx.channel());
    }

它表示在客户端离开时会触发该事件,在这个方法内我们可以做一些收尾工作——把通道号去除,避免了通过列表的积累造成管理混乱。

3.channelRead

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("收到客户端:"+ctx.channel()+"的消息:n"+msg.text())
    }

很显然它就是在收到消息后会执行该事件,客户端和服务端的方法都是相同的。

重点:netty主要是通过连接时产生的通道channel进行消息的推送,只要拿到这个channe就能对任意的客户端推送消息,我们分析以下场景:当客户端连接服务端时会产生一个channel,观察channelRead()方法里面一个参数是channel,另一个则是接收的消息 。现在我们的任务就是获取服务端channelRead()的channel,在客户端的channelRead()中调用服务端的channelRead()方法,再把刚刚获取的服务端channel传进去这样通道就以及打通了,我们只需要把客户端传来的消息试试传入就完成了我们的需求。

首先建立通道号保存的对象

@Data
public class ServerChannel{
  private static Channel serverChannel;
}

从服务端获取通道号

  @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      ServerChannel.setServerChannel(ctx.channel());
        System.out.println("收到客户端:"+ctx.channel()+"的消息:n"+msg.text())
    }

在客户端调用服务端的方法

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("收到服务端:"+ctx.channel()+"的消息:n"+msg.text())
      //通过反射创建服务端对象
      Class<WebSocketServerHandler> SocketHandler=WebSocketServerHandler.class;
       WebSocketServerHandler socketHandler=SocketHandler.newInstance();
						//获取服务端的通道号
        ChannelHandlerContext serverChannel=ServerCtx.getContext();
       if(serverChannel !=null){
                socketHandler.channelRead(serverCtx,msg);
            }
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>