netty和webSocket入门

1.设置netty配置文件

package com.citydo.netty;

import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * 存储整个工程的全局配置
 * @author liuyazhuang
 *
 */
public class NettyConfig2 {


    /**
     * 存储每一个客户端接入进来的channel对象
     * */
    public static ChannelGroup g=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}

2.webSocket请求发送处理消息的方法

package com.citydo.netty;


import java.util.Date;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
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 io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;

public class WebSocketHandler extends SimpleChannelInboundHandler<Object>{
     private WebSocketServerHandshaker Handshaker;

     private static final String WEB_SOCKET_URL="ws//127.0.0.1:8888/websocket";


    //客户端与服务端创建连接的时候调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig2.g.add(ctx.channel());
        System.err.println("客户端与服务端连接开启");

    }

    //客户端与服务端断开调用
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
       NettyConfig2.g.remove(ctx.channel());
       System.err.println("客户端与服务端连接关闭");
    }

    //服务端口接收客户端发送过来数据结束之后调用
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    //工程出现异常的时候调用
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
       cause.printStackTrace();
       ctx.close();
    }
    //服务端处理客户端websocket 请求的核心方法
    @Override
    protected void messageReceived(ChannelHandlerContext context, Object msg) throws Exception {
        // TODO Auto-generated method stub
        //处理客户端向服务段发送http握手请求的业务
        if (msg instanceof FullHttpRequest) {
           handHttpRequest(context, (FullHttpRequest)msg);  
        }else if(msg instanceof WebSocketFrame) {
            //处理webSocket连接业务
            HandWebsocketFrame(context, (WebSocketFrame)msg);
        }

    }

    /**
     * 处理客户端与服务端之前websocket业务
     * @param ctx
     * @param frame
     */
    private void HandWebsocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame){
        if (frame instanceof CloseWebSocketFrame) {
            Handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());          
        }
        //判断是否是ping消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        //判断是否二进制消息,如果是二进制消息 抛出异常
        if (!(frame instanceof TextWebSocketFrame)) {
            System.err.println("目前不支持二进制消息");
            throw new RuntimeException("["+this.getClass().getName()+"] 不支持消息");

        }
        //返回应答消息
        //获取客户端想入服务器端发送的消息
        String request=((TextWebSocketFrame) frame).text();
        System.err.println("服务端收到客户端的消息===》"+request);
        TextWebSocketFrame tws=new TextWebSocketFrame(new Date().toString()
                                                       +ctx.channel().id()
                                                       +"=====>>>"
                                                       +request);


        //群发的功能 服务端向每一个连接上来的客户端群发消息
        NettyConfig2.g.writeAndFlush(tws);


    }

    /**
     * 处理客户端向服务发送http握手请求的业务
     * @param ctx
     * @param request
     */
    @SuppressWarnings("unused")
    private void handHttpRequest(ChannelHandlerContext ctx,FullHttpRequest request) {
        if (!request.getDecoderResult().isSuccess()||!"websocket".equals(request.headers().get("Upgrade"))) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        WebSocketServerHandshakerFactory wsFactory=new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);
        Handshaker=wsFactory.newHandshaker(request);    
        if (Handshaker==null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        }else {
            Handshaker.handshake(ctx.channel(), request);
        }

    }

    /**服务端向客户端响应消息
     * @param ctx
     * @param req
     * @param res
     */
    @SuppressWarnings("unused")
    private void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest req,
            DefaultFullHttpResponse res) {
        if (res.getStatus().code()!=200) {
            ByteBuf buf=Unpooled.copiedBuffer(res.getStatus().toString(),CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        //服务器向客户端发送数据
        ChannelFuture f=ctx.channel().writeAndFlush(res);
        if (res.getStatus().code()!=200) {
            f.addListener(ChannelFutureListener.CLOSE);

        }

    }

}

3.初始化各个组件

package com.citydo.netty;

import com.imooc.netty.MyWebSocketHandler;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * 初始化连接时候的各个组件
 * @author nick
 *
 */
public class WebSocketChannelHandler extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel e) throws Exception {
        e.pipeline().addLast("http-codec",new HttpClientCodec());
        e.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
        e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
        e.pipeline().addLast("handler",new MyWebSocketHandler());   
    }

}

4.启动Websocket的方法

package com.citydo.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class Application {
    public static void main(String[] args) {
        EventLoopGroup bossLoopGroup = new NioEventLoopGroup();
        EventLoopGroup workLoopGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossLoopGroup, workLoopGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.childHandler(new WebSocketChannelHandler());
            System.err.println("服务开启等待客户端连接.....");
            Channel channel = serverBootstrap.bind(8888).channel();
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //退出程序
            bossLoopGroup.shutdownGracefully();
            workLoopGroup.shutdownGracefully();
        }
    }

}

netty连接池:
在高并发情况下,做微服务架构,底层服务与服务之间的调用,一般是采用TCP长连接,长连接的性能当然要比短连接至少高十倍以上,但在采用长连接发送消息的时候,假如你的业务并发量比较高,一条通道处理不过来的话,可以选择连接池的方式,每次请求都可以从池里取通道,假如你使用Netty客户端来发送的话,连接池有三种方案选择:
第一种:采用Netty自带的连接池,优点是可以连接多个服务,但没有设置空闲连接数,以及空闲时间等而且绑定了netty客户端
第二种: Apache的commons-pool2这种连接池,这种连接池比较通用,不管使用Netty还是普通的socket都可以使用,同时也有空闲数设置,空闲时间设置,但要去连接多个不同的服务器的话,还需要自己扩展。
第三种:自己采用信号量和并发队列编写一个连接池,自己编写的连接池,虽然通用化没有那么完美,但可以灵活调用,加一些额外的特征。

猜你喜欢

转载自blog.csdn.net/qq_32447301/article/details/80963991