基于netty 开发websocket服务端

基于netty 开发websocket服务端


package com.comtop.lcam.fwms.managementkey.websocket;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;

import javax.annotation.Resource;

import com.comtop.lcam.fwms.managementkey.dto.EslcsLockLogDTO;
import com.comtop.lcam.fwms.managementkey.facade.internal.EslcsLockLogFacade;
import com.comtop.sproc.core.base.util.SprocApplicationContextUtil;

/**
 * 服务端处理通道.这里只是打印一下请求的内容,并不对请求进行任何的响应 DiscardServerHandler 继承自 ChannelHandlerAdapter, 这个类实现了ChannelHandler接口, ChannelHandler提供了许多事件处理的接口方法,
 * 然后你可以覆盖这些方法。 现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。
 */
@ChannelHandler.Sharable
public class NettyHandler extends SimpleChannelInboundHandler {
    
    /** 服务 */
    @Resource
    private final EslcsLockLogFacade eslcsLockLogFacade = SprocApplicationContextUtil.getContext().getBean(EslcsLockLogFacade.class);
    
    /**
     * 这里我们覆盖了chanelRead()事件处理方法。 每当从客户端收到新的数据时, 这个方法会在收到消息时被调用, 这个例子中,收到的消息的类型是ByteBuf
     * 
     * @param ctx 通道处理的上下文信息
     * @param msg 接收的消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof FullHttpRequest) {
            // log.info("HTTP请求");
        } else if (msg instanceof WebSocketFrame) {
            WebSocketFrame wsf = (WebSocketFrame) msg;
            // 如果是PING请求就响应PONG
            if (wsf instanceof PingWebSocketFrame) {
                // log.debug("响应PONG");
                ctx.channel().write(new PongWebSocketFrame(wsf.content().retain()));
                return;
            }
            // 如果是文本消息就转成TextWebSocketFrame
            if (wsf instanceof TextWebSocketFrame) {
                TextWebSocketFrame twsf = (TextWebSocketFrame) wsf;
                // 收到的请求报文
                String requestText = twsf.text();
                System.out.println("server--->收到客户端发的消息:" + requestText);
                // 回写数据
                ChannelFuture writeAndFlush = ctx.channel().writeAndFlush(new TextWebSocketFrame("你好"));
                System.out.println("server--->服务端返回的信息:" + writeAndFlush);
                EslcsLockLogDTO eslcsLockLogDTO = new EslcsLockLogDTO();
                eslcsLockLogDTO.setOptimisticLockVersion(0);
                eslcsLockLogDTO.setUnlockingUserName("lifeng");
                eslcsLockLogFacade.create(eslcsLockLogDTO);
                // System.out.println("server--->服务端返回的信息:" + ctx.channel().write("hi"));
                // GatewayService.add("11", ctx.channel());
                // log.info("收到数据:\n{}", requestText);
                // 将请求报文转换成实体
                // RequestData requestData;
                try {
                    // requestData = Json.get().fromJson(requestText, RequestData.class);
                } catch (Exception e) {
                    // log.warn("客户端请求的数据不正确");
                    // NUtils.RT(ctx, EventConst.MSG, 300, "请求的数据格式不正确");
                    return;
                }
            } else {
                // log.info("只支持文本消息");
            }
        }
        
    }
    
    /***
     * 这个方法会在发生异常时触发
     * 
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        // 出现异常时关闭连接。
        cause.printStackTrace();
        ctx.close();
    }
    
    /**
     * 连接断开处理事件
     * 
     * @param ctx ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("=======================Netty断开连接=======================");
    }
    
}
 
  

 

package com.comtop.lcam.fwms.managementkey.websocket;



import io.netty.channel.ChannelInitializer;
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;

/**
 * FIXME 类注释信息(此标记自动生成,注释填写完成后请删除)
 * 
 * 
 * [
 * 调用关系:
 * 实现接口及父类:
 * 子类:
 * 内部类列表:
 * ]
 * 
* * @author 李锋 * @since 1.0 * @version 2018-11-20 李锋 */ public class ServerChannelInitializer extends ChannelInitializer { @Override protected void initChannel(SocketChannel e) { // WebSocket协议本身是基于http协议的,所以这边也要使用http解编码器 e.pipeline().addLast(new HttpServerCodec()); // 以块的方式来写的处理器 e.pipeline().addLast(new ChunkedWriteHandler()); // netty是基于分段请求的,HttpObjectAggregator的作用是将请求分段再聚合,参数是聚合字节的最大长度 e.pipeline().addLast(new HttpObjectAggregator(8192)); e.pipeline().addLast(new WebSocketServerProtocolHandler("/")); // 在管道中添加我们自己的接收数据实现方法 e.pipeline().addLast(new NettyHandler()); } }

package com.comtop.lcam.fwms.managementkey.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;

/**
 * FIXME 类注释信息(此标记自动生成,注释填写完成后请删除)
 * 
 * 
 * [
 * 调用关系:
 * 实现接口及父类:
 * 子类:
 * 内部类列表:
 * ]
 * 
* * @author 李锋 * @since 1.0 * @version 2018-11-20 李锋 */ public class NettyServerOperation { /** * FIXME 方法注释信息(此标记由Eclipse自动生成,请填写注释信息删除此标记) */ public void run() { // Boss线程:由这个线程池提供的线程是boss种类的,用于创建、连接、绑定socket, (有点像门卫)然后把这些socket传给worker线程池。 // 在服务器端每个监听的socket都有一个boss线程来处理。在客户端,只有一个boss线程来处理所有的socket /*** * NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器, Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。 在这个例子中我们实现了一个服务端的应用, 因此会有2个NioEventLoopGroup会被使用。 * 第一个经常被叫做‘boss’,用来接收进来的连接。 第二个经常被叫做‘worker’,用来处理已经被接收的连接, 一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。 * 如何知道多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现, 并且可以通过构造函数来配置他们的关系。 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); // Worker线程:Worker线程执行所有的异步I/O,即处理操作 /** * ServerBootstrap 是一个启动NIO服务的辅助启动类 你可以在这个服务中直接使用Channel */ EventLoopGroup workerGroup = new NioEventLoopGroup(); // ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器,并且开始监听端口的socket请求 ServerBootstrap b = new ServerBootstrap(); /** * 这一步是必须的,如果没有设置group将会报java.lang.IllegalStateException: group not set异常 */ b.group(bossGroup, workerGroup); /*** * ServerSocketChannel以NIO的selector为基础进行实现的,用来接收新的连接 这里告诉Channel如何获取新的连接. */ b.channel(NioServerSocketChannel.class); // EslcsSocketChannel 对出入的数据进行的业务操作,其继承ChannelInitializer b.childHandler(new ServerChannelInitializer()); System.out.println("=======================Netty在8523端口启动======================="); try { /*** * 绑定端口并启动去接收进来的连接 */ ChannelFuture f = b.bind(8523).sync(); /** * 这里会一直等待,直到socket被关闭 */ f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { /*** * 关闭 */ bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } System.out.println("=======================Netty在8523端口启动======================="); } /** * FIXME 方法注释信息(此标记由Eclipse自动生成,请填写注释信息删除此标记) * * @param args */ public static void main(String[] args) { new NettyServerOperation().run(); } }

 

你可能感兴趣的:(netty)