Netty学习——通过websocket编程实现基于长连接的双攻的通信

jannal 2019-12-02

Netty学习(一)基于长连接的双攻的通信,通过websocket编程实现


 效果图,客户端和服务器端建立起长连接,客户端发送请求,服务器端响应

Netty学习——通过websocket编程实现基于长连接的双攻的通信

 但是目前缺少心跳,如果两个建立起来的连接,一个断网之后,另外一个是感知不到对方已经断掉的。以后使用心跳技术来进行连接检测

须知:

状态码101,代表 协议转换,从HTTP协议升级为WebSocket协议

HTTP协议,一般访问的时候:是 Http://localhost:8080/ws

WebSocket协议,访问的时候,需要是:ws://localhost:8080/ws

Netty学习——通过websocket编程实现基于长连接的双攻的通信


 实现代码:

服务器端:

package com.dawa.netty.fifthexample;

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;

import java.net.InetSocketAddress;

/**
 * @Title: MyServer
 * @Author: 大娃
 * @Date: 2019/11/28 14:02
 * @Description:
 */
public class MyServer {
    public static void main(String[] args)  throws Exception{
        //定义一个线程组 , 事件循环组 。 异步的NIO , 就是一个死循环。
        EventLoopGroup bossGroutp = new NioEventLoopGroup();  //不断的接受连接,但是不处理
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 完成后续处理,把结果返回给客户端
        try {

            //服务端,启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //group方法,有两个参数的,有一个参数的
            serverBootstrap.group(bossGroutp, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new WebSocketChannelInitializer()); //字处理器,自己 定义的

            ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8899)).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            //优雅关闭、
            bossGroutp.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
package com.dawa.netty.fifthexample;

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;

/**
 * @Title: WebSocketChannelInitializer
 * @Author: 大娃
 * @Date: 2019/11/28 14:06
 * @Description:
 */
public class WebSocketChannelInitializer 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)); //将分开的段,给聚合到一起,形成完整的HTTP响应。很重要的一个对象,在处理HTTP的时候
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        pipeline.addLast(new TextWebSocketFrameHandler());
    }
}
package com.dawa.netty.fifthexample;

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

import java.time.LocalDateTime;

/**
 * @Title: TextWebSocketFrameHandler
 * @Author: 大娃
 * @Date: 2019/12/2 08:33
 * @Description:
 */
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     *
     * @param ctx 上下文对象
     * @param msg //文本帧对象
     * @throws Exception 抛异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("收到消息:"+msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:"+ LocalDateTime.now() )); //不能直接传入 字符串.因为不同协议的规范不同,websocket要求要传回这种的对象
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded:" + ctx.channel().id().asLongText());//每一个channel 都有一个唯一的id值与其对应
    }

    @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 {
        cause.printStackTrace();
        ctx.channel().close();
    }
}

客户端(使用HTML,JS来代替模拟客户端)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>websocket客户端</title>
</head>
<body>
<!--

-->
<script type="text/javascript">
    //使用浏览器,进行websocket服务器端的访问
    var socket;
    if (window.WebSocket) {//判断浏览器是否支持websocket
        socket = new WebSocket("ws:localhost:8899/ws"); //建立websocket连接
        socket.onmessage = function (ev) { //如果socket收到消息,这个onmessage方法就会被回调
            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;
        }
        if (socket.readyState === WebSocket.OPEN) {
            socket.send(message);
        } else {
            alert("连接未开启");
        }
    }
</script>

<!--客户端访问后台 websocket程序-->
<form action="" onsubmit="return false;">
    <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: 300px;"></textarea>
    <input type="button" onclick="javascript:document.getElementById(‘responseText‘).value=‘‘;" value="清空内容">
</form>
</body>
</html>

相关推荐