使用netty 构建websocket 服务器

1.后端netty的步骤跟上一篇一样:

==========================================================================================

1.先创建第一个类 HelloServer :

package netty;

/*
          实现客户端 发送一个请求

          服务器会返回 hello netty

 */


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.nio.NioSctpServerChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ScheduledFuture;
import netty.HelloServerInitializer;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class HelloServer {

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

        // 定义一对线程组
        // 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // netty服务器的创建, ServerBootstrap 是一个启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)        // 设置主从线程组
                    .channel(NioServerSocketChannel.class) // 设置nio的双向通道
                    .childHandler(new HelloServerInitializer()); // 子处理器,用于处理workerGroup

            // 启动server,并且设置8088为启动的端口号,同时启动方式为同步
            ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();

            // 监听关闭的channel,设置位同步方式
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

==========================================================================================

2.创建第二个类 WSServerInitializer :

package 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;
import netty.CustomHandler;

public class WSServerInitializer extends ChannelInitializer<SocketChannel> {


    protected void initChannel(SocketChannel channel) throws Exception {

              ChannelPipeline pipeline =channel.pipeline();
              //websocket  基于 http 协议,所以要有 http 编解码器
              pipeline.addLast(new HttpServerCodec());
              //对写大数据流的支持
              pipeline.addLast(new ChunkedWriteHandler());
              //对httpMessage 进行聚合,聚合成 FullHttpRequest 或 FullHttpResonse
              pipeline.addLast(new HttpObjectAggregator(1024*64));

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


        // ================ 以下是自定义的 handler===============
              //自定义的 handler
              pipeline.addLast("ChatHandler", new ChatHandler());

    }
}

==========================================================================================

创建第三个类 CustomHandler :

package netty;


import com.sun.deploy.net.HttpRequest;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

/**
 *
 *    创建自定义助手类
 *
 *    SimpleChannelInboundHandler:对于请求来讲,其实相当于【入栈,入境】
 */
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {

    @Override
    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg)
            throws Exception {
        // 获取channel
        Channel channel = ctx.channel();

     //   if (msg instanceof HttpRequest) {  //不加可以用,加了,就不行
            // 显示客户端的远程地址
            System.out.println(channel.remoteAddress());

            // 定义发送的数据消息
            ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8);

            // 构建一个http response
            FullHttpResponse response =
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                            HttpResponseStatus.OK,
                            content);
            // 为响应增加数据类型和长度
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());

            // 把响应刷到客户端
            ctx.writeAndFlush(response);
        }

   // }


}

==========================================================================================

这三个类就是全部的后端。

使用 Hbuilder编写前端 websocket(只有一个页面):

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        
        <div>发送消息:</div>
        <input type="text" id="msgContent"/>
        <input type="button" value="点我发送" οnclick="CHAT.chat()"/>
        
        <div>接受消息:</div>
        <div id="receiveMsg" style="background-color: gainsboro;"></div>
        
        <script type="application/javascript">
            
            window.CHAT = {
                socket: null,
                init: function() {
                    if (window.WebSocket) {

                       //注意:一开始的ws不能变,(相当于http),后面的ws能变,和后端的 ws一致就行,

                       //   172.21.160.104 是本机电脑的ip地址 ,8086 也是后端 bind(端口号)
                        CHAT.socket = new WebSocket("ws://172.21.160.104:8086/ws");
                        CHAT.socket.onopen = function() {
                            console.log("连接建立成功...");
                        },
                        CHAT.socket.onclose = function() {
                            console.log("连接关闭...");
                        },
                        CHAT.socket.onerror = function() {
                            console.log("发生错误...");
                        },
                        CHAT.socket.onmessage = function(e) {
                            console.log("接受到消息:" + e.data);
                            var receiveMsg = document.getElementById("receiveMsg");
                            var html = receiveMsg.innerHTML;
                            receiveMsg.innerHTML = html + "<br/>" + e.data;
                        }
                    } else {
                        alert("浏览器不支持websocket协议...");
                    }
                },
                chat: function() {
                    var msg = document.getElementById("msgContent");
                    CHAT.socket.send(msg.value);
                }
            };
            
            CHAT.init();
            
        </script>
    </body>
</html>

====================================================================================

在浏览器上:

发布了55 篇原创文章 · 获赞 5 · 访问量 6048

猜你喜欢

转载自blog.csdn.net/weixin_42528855/article/details/103364691