首页 > 其他分享 >Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息

时间:2023-03-23 11:02:02浏览次数:50  
标签:Netty WebSocket netty 服务端 import 连接 channel


上面讲了使用使用Socket搭建多客户端的连接与通信。

那么如果在Netty中使用WebSocket进行长连接通信要怎么实现。

WebSocket

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

WebSocket是一种在单个TCP连接上进行全双工通信的协议。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

WebSocket 属性

以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:

 

属性

描述

Socket.readyState

只读属性 readyState 表示连接状态,可以是以下值:

  • 0 - 表示连接尚未建立。
  • 1 - 表示连接已建立,可以进行通信。
  • 2 - 表示连接正在进行关闭。
  • 3 - 表示连接已经关闭或者连接不能打开。

Socket.bufferedAmount

只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。


WebSocket 事件

以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

 

事件

事件处理程序

描述

open

Socket.onopen

连接建立时触发

message

Socket.onmessage

客户端接收服务端数据时触发

error

Socket.onerror

通信发生错误时触发

close

Socket.onclose

连接关闭时触发


WebSocket 方法

以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

 

方法

描述

Socket.send()

使用连接发送数据

Socket.close()

关闭连接

 

注:


霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

实现


在此基础上,在src下新建包com.badao.NettyWebSocket

然后新建服务端类WebSocketServer

package com.badao.NettyWebSocket;

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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class WebSocketServer {
    public static void main(String[] args) throws  Exception
    {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new WebSocketInitializer());
            //绑定端口
            ChannelFuture channelFuture = serverBootstrap.bind(70).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            //关闭事件组
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

服务端的搭建在上面已经讲解,这里又添加了Netty自带的日志处理器LoggingHandler

然后又添加了自定义的初始化器WebSocketInitializer

所以新建类WebSocketInitializer

package com.badao.NettyWebSocket;

import io.netty.channel.Channel;
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 WebSocketInitializer  extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpObjectAggregator(8192));
        pipeline.addLast(new WebSocketServerProtocolHandler("/badao"));

        pipeline.addLast(new WebSocketHandler());
    }
}

因为Netty也是基于Http的所以这里需要添加HttpServerCodec处理器等。

要想实现WebSocket功能,主要是添加了WebSocketServerProtocolHandler这个WebSocket服务端协议处理器。注意这里的参数需要添加一个WebSocket的路径,这里是/badao

最后添加自定义的处理器WebSocketHandler 用来做具体的处理

所以新建类WebSocketHandler

package com.badao.NettyWebSocket;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.time.LocalDateTime;

public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("收到消息:"+msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("WebSocket服务端在"+ LocalDateTime.now()+"发送消息(公众号:霸道的程序猿)"));
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded:"+ctx.channel().id().asLongText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved:"+ctx.channel().id().asLongText());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常发生");
        ctx.close();
    }
}

使其继承SimpleChannelInboundHandler注意此时的泛型类型为TextWebSocketFrame

然后重写channelRead0方法用来对收到数据时进行处理

@Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("收到消息:"+msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("WebSocket服务端在"+ LocalDateTime.now()+"发送消息(公众号:霸道的程序猿)"));
    }

在服务端将收到的消息进行输出并给客户端发送数据。

然后重写handlerAdded方法,在客户端与服务端建立连接时进行操作

@Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded:"+ctx.channel().id().asLongText());
    }

这里通过通道的id方法的asLongText方法获取连接的唯一标志。

然后在服务端输出。

同理重写handlerRemoved方法,在断掉连接时将连接的唯一标志进行输出。

@Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved:"+ctx.channel().id().asLongText());
    }

最后重写出现异常时的处理方法,在出现异常时将连接关闭。

@Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常发生");
        ctx.close();
    }

至此WebSocket服务端搭建完成,然后客户端通过JS就能实现。

在项目目录下src下新建webapp目录,在此目录下新建badao.html

 

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息_.net

修改html的代码为

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>公众号:霸道的程序猿</title>
</head>
<body>
<script type="text/javascript">
    var socket;
    if(window.WebSocket)
    {
        socket = new WebSocket("ws://localhost:70/badao")
        socket.onmessage=function (ev) {
            var ta = document.getElementById("responseText");
            ta.value = ta.value+"\n"+ev.data;
        }

        socket.onopen = function (ev) {
            var ta = document.getElementById("responseText");
            ta.value = "连接开启";
        }

        socket.onclose = function (ev) {
            var ta = document.getElementById("responseText");
            ta.value = ta.value+"\n连接关闭";
        }
    }
    else{
        alert("当前浏览器不支持WebSocket")
    }

    function send(message) {
        if(!window.WebSocket)
        {
            return;
        }else
        {
            if(socket.readyState = WebSocket.OPEN)
            {
                socket.send(message);
            }
            else
            {
                alert("连接尚未开启");
            }
        }
    }
</script>
<form>
    <textarea name="message" style="width: 400px;height: 200px">

    </textarea>

    <input type="button" value="发送数据" onclick="send(this.form.message.value)">

    <h3>服务端输出:</h3>

    <textarea id="responseText" style="width: 400px;height: 200px">

    </textarea>
</form>
</body>
</html>

在js中通过windows.WebSocket判断是否支持WebSocket

如果支持则

socket = new WebSocket("ws://localhost:70/badao")

建立连接,url的写法前面的ws://是固定的类似http://

后面的是跟的ip:端口号/上面配置的WebSocket路径

然后就是以这个WebSocket对象为中心进行连接和数据的显示。

下面的回调方法onopen会在建立连接成功后回调,onclose会在断掉连接后回调,

onmessage会在收到服务端发送的数据时回调并通过ev.data获取数据。

客户端向服务端发送数据时调用的是socket的send方法,通过

if(socket.readyState = WebSocket.OPEN)

判断连接已经成功建立。

实现长连接通信

运行WebSocketServer的main方法,然后在badao.html上右击选择运行

 

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息_Netty_02

建立连接成功后会在服务端输出连接的id,在客户端会显示连接开启。

此时如果刷新浏览器,服务端会输出一次断开连接和建立连接

 

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息_WebSocket_03

再将服务端停掉,客户端会输出连接关闭

 

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息_.net_04

再重新启动服务端,在上面的输入框输入内容并点击发送数据

 

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息_WebSocket_05

服务端会收到消息并输出,并向客户端发送一个消息。

继续发送也是如此

 

Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息_Netty_06


标签:Netty,WebSocket,netty,服务端,import,连接,channel
From: https://blog.51cto.com/BADAOLIUMANGQZ/6144519

相关文章

  • 微信小程序websocket的使用
    微信小程序websocket的使用微信小程序中使用websocket分为两步:一、现在微信公众平台的开发者工具中配置socket的域名   二、开始编写业务代码业务代码大致可以......
  • WebSocket通讯框架 jWebSocket
    jWebSocket提供用来创建基于HTML5的Web流通讯的应用框架。HTML5WebSockets将替换现有的XHR应用和Comet服务,通过一个灵活高速双向的TCPsocket通讯技术。jWebSo......
  • Easy WebSocket
    EasyWebSocket是一个封装了​​WebSocket​​API的JavaScript库,大大简化了WebSocket应用的开发。​​jeromeetienne​​​/​​EasyWebsocket​​......
  • Java Spring使用EventSource进行服务端推送
    Java代码:@ResponseBody@RequestMapping(value="/getDate",produces="text/event-stream;charset=UTF-8")publicvoidgetDate(HttpServletResponse......
  • WebSocket + Redis简单快速实现Web网站单设备登录功能
    大家好,我是小悟1、写在前面的话生活中,我们在使用一些APP的时候,有过一种体验,就是在A手机上登录账号,因为某些原因需要在B手机上登录,然后就会在A手机上看到类似"该账号在其他设......
  • netty网络框架一
    一、netty是什么Netty是一个高性能、异步事件驱动的网络应用程序框架,使用Java编写。它提供了一组简单但功能强大的抽象,使得开发人员可以轻松地开发基于网络协议的客户......
  • 浅谈分布式环境下WebSocket消息共享问题
    浅谈分布式环境下WebSocket消息共享问题技术分析我们在开发时会遇到需要使用即时通讯的场景,当然,实现方式很多,Socket、MQTT、Netty....等等。具体用哪种就在于业务的需求......
  • WebSocket 测试工具
    WebSocket测试工具 一、WebSocket简介WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端......
  • 服务端口号
    ●telnet 23/tcp●ssh 22/tcp ●nfs 2049/tcp●rpc 111/tcp|111/udp●krb5kdc 80/tcp|80/udp●kadmin 749/tcp|749/udp......
  • 【RPC高性能框架总结】5.高性能nio框架netty(中)
    接上一篇《​​4.高性能nio框架netty(上)​​》上一篇我们编写了使用Netty框架开发的客户端的启动类“NettyTestClient”以及业务处理类“NettyTestClientHandler”,本篇我......