• Netty多协议开发


    HTTP协议开发

    post与get的区别

    1)get用于信息获取,post用于更新资源。

    2)get数据放在请求行中,post数据放在请求体内。

    3)get对数据长度有限制(2083字节),post没有限制。

    4)post比get安全性高。

    Netty Http+Xml协议栈开发

    高效的XML绑定JiBx     JOPO <=> XML

    package com.netty.ch10;
    
    import java.io.IOException;
    import java.io.StringReader;
    import java.io.StringWriter;
    
    import org.jibx.runtime.BindingDirectory;
    import org.jibx.runtime.IBindingFactory;
    import org.jibx.runtime.IMarshallingContext;
    import org.jibx.runtime.IUnmarshallingContext;
    import org.jibx.runtime.JiBXException;
    
    import com.netty.ch10.httpXML.pojo.Order;
    import com.netty.ch10.httpXML.pojo.OrderFactory;
    
    /**
     * jiBx类库的使用
     * @author Administrator
     * ANT 脚本生成XML与对象的绑定关系
     *1)下载JiBx包  
     *2)JiBx包  的example下找到一个build.xml  并运行target  ==>  BindGen  生成 binding.xml 和 pojo.xsd
     *  <target name="bindgen">
      
        <echo message="Running BindGen tool"/>
        <java classpathref="classpath" fork="true" failonerror="true"
            classname="org.jibx.binding.generator.BindGen">
          <arg value="-s"/>
          <arg value="${basedir}/src/com/netty/ch10/httpXML/pojo"/>
          <arg value="com.netty.ch10.httpXML.pojo.Order"/>
        </java>
        
      </target>
      3)jibx编译命令   动态修改POJO类    ==>   bind
       <!-- bind as a separate step -->
      <target name="bind" depends="check-binding">
        
        <echo message="Running JiBX binding compiler"/>
        <taskdef name="bind" classname="org.jibx.binding.ant.CompileTask">
          <classpath>
            <fileset dir="${jibx-home}/lib" includes="*.jar"/>
          </classpath>
        </taskdef>
        <bind binding="${basedir}/binding.xml">
          <classpath refid="classpath"/>
        </bind>
        
      </target>
      4)运行测试类
     */
    public class TestOrder {
        
    
        private IBindingFactory factory = null;
        private StringWriter write = null;
        private StringReader reader = null;
        private final static String CHARSET_NAME = "UTF-8"; 
        
        /**
         * pojo to xml
         * @param order
         * @return
         * @throws JiBXException
         * @throws IOException
         */
        private String encode2Xml(Order order) throws JiBXException, IOException{
            factory = BindingDirectory.getFactory(Order.class);
            write = new StringWriter();
            IMarshallingContext mctx = factory.createMarshallingContext();
            mctx.setIndent(2);
            mctx.marshalDocument(order,CHARSET_NAME,null,write);
            String xmlStr = write.toString();
            write.close();
            System.out.println(xmlStr);
            return xmlStr;
        }
        
        private Order decode2Order(String xmlBody) throws JiBXException{
            reader = new StringReader(xmlBody);
            IUnmarshallingContext uctx = factory.createUnmarshallingContext();
            Order order = (Order)uctx.unmarshalDocument(reader);
            return order;
            
        }
        public static void main(String[] args) throws JiBXException, IOException {
            TestOrder test = new TestOrder();
            Order order = OrderFactory.create();
            String body = test.encode2Xml(order);
            Order order2 = test.decode2Order(body);
            System.out.println(order2);
        }
        
    }
    View Code

    Http请求类

    package com.netty.ch10.httpXML.request;
    
    import io.netty.handler.codec.http.FullHttpRequest;
    
    public class HttpXmlRequest {
        private FullHttpRequest request;
        private Object body;
        
        /**
         * @return the body
         */
        public Object getBody() {
            return body;
        }
    
        /**
         * @param body the body to set
         */
        public void setBody(Object body) {
            this.body = body;
        }
    
        public HttpXmlRequest(FullHttpRequest request, Object body) {
            this.request = request;
            this.body = body;
        }
    
        public final FullHttpRequest getRequest() {
            return request;
        }
    
        public final void setRequest(FullHttpRequest request) {
            this.request = request;
        }
    }
    View Code

    Http响应类

    package com.netty.ch10.httpXML.response;
    
    import io.netty.handler.codec.http.FullHttpRequest;
    import io.netty.handler.codec.http.FullHttpResponse;
    
    public class HttpXmlResponse {
    
        private FullHttpResponse httpResponse;
        private Object result;
        public HttpXmlResponse(FullHttpResponse httpResponse, Object result) {
            super();
            this.httpResponse = httpResponse;
            this.result = result;
        }
        public HttpXmlResponse() {
            super();
            // TODO Auto-generated constructor stub
        }
        /**
         * @return the httpResponse
         */
        public FullHttpResponse getHttpResponse() {
            return httpResponse;
        }
        /**
         * @param httpResponse the httpResponse to set
         */
        public void setHttpResponse(FullHttpResponse httpResponse) {
            this.httpResponse = httpResponse;
        }
        /**
         * @return the result
         */
        public Object getResult() {
            return result;
        }
        /**
         * @param result the result to set
         */
        public void setResult(Object result) {
            this.result = result;
        }
        
        
        
    }
    View Code

    Encoder编码基类

    package com.netty.ch10.httpXML.common;
    
    import java.io.StringWriter;
    import java.nio.charset.Charset;
    import java.util.List;
    
    import org.jibx.runtime.BindingDirectory;
    import org.jibx.runtime.IBindingFactory;
    import org.jibx.runtime.IMarshallingContext;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.MessageToMessageCodec;
    import io.netty.handler.codec.MessageToMessageEncoder;
    /**
     * HTTP+XML 请求消息编码基类
     * @author Administrator
     *
     * @param <T>
     */
    public abstract class AbstractHttpXmlEncoder<T> extends MessageToMessageEncoder<T>{
    
        IBindingFactory factory = null;
        StringWriter writer = null;
        final static String CHARSET_NAME ="UTF-8";
        final static Charset UTF_8 = Charset.forName(CHARSET_NAME);
        
        protected ByteBuf encode0(ChannelHandlerContext ctx,Object body) throws Exception{
            factory = BindingDirectory.getFactory(body.getClass());
            writer= new StringWriter();
            IMarshallingContext mctx = factory.createMarshallingContext();
            mctx.setIndent(2);
            mctx.marshalDocument(body,CHARSET_NAME,null,writer);
            String xmlStr = writer.toString();
            writer.close();
            writer = null;
            ByteBuf encodeBuf = Unpooled.copiedBuffer(xmlStr,UTF_8);
            return encodeBuf;
        }
    
        /* (non-Javadoc)
         * @see io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            // TODO Auto-generated method stub
            super.exceptionCaught(ctx, cause);
            if(writer != null){
                writer.close();
                writer = null;
            }
        } 
        
        
    }
    View Code

    HTTP请求编码类

    package com.netty.ch10.httpXML.request;
    
    import java.net.InetAddress;
    import java.util.List;
    
    import com.netty.ch10.httpXML.common.AbstractHttpXmlEncoder;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.DefaultFullHttpRequest;
    import io.netty.handler.codec.http.FullHttpRequest;
    import io.netty.handler.codec.http.HttpHeaderNames;
    import io.netty.handler.codec.http.HttpHeaderValues;
    import io.netty.handler.codec.http.HttpHeaders;
    //import io.netty.handler.codec.http.HttpHeaders;
    import io.netty.handler.codec.http.HttpMethod;
    import io.netty.handler.codec.http.HttpVersion;
    /**
     * HTTP+XML Http 请求消息编码类
     * @author Administrator
     *
     */
    public class HttpXmlRequestEncoder  extends AbstractHttpXmlEncoder<HttpXmlRequest>{
    
    
    
        @Override
        protected void encode(ChannelHandlerContext ctx, HttpXmlRequest msg, List<Object> out) throws Exception {
            // TODO Auto-generated method stub
            //将业务需要发送的pojo对象Order实例通过JiBx序列化成字符串,随后封装成Netty的ByteBuf。
            ByteBuf body = encode0(ctx, msg.getBody()) ;
            FullHttpRequest request = msg.getRequest();
            
            //如果业务侧没有设置消息头 则构造新的http请求头
            if(request == null){
                request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,HttpMethod.GET,"/do",body);
                //从request中获取请求头
                HttpHeaders header = request.headers();
                //设置一系列的请求头  这边以硬编码的方式  产品化可以做成xml配置文件
                header.set(HttpHeaderNames.HOST,InetAddress.getLocalHost().getHostAddress());
                header.set(HttpHeaderNames.CONNECTION,HttpHeaderValues.CLOSE);
                header.set(HttpHeaderNames.ACCEPT_ENCODING,HttpHeaderValues.GZIP.toString()
                        +","+HttpHeaderValues.DEFLATE.toString());
                header.set(HttpHeaderNames.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.7");
                header.set(HttpHeaderNames.USER_AGENT,"Netty xml Http Client side");
                header.set(HttpHeaderNames.ACCEPT,"text/html.application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
                header.set(HttpHeaderNames.CONTENT_LENGTH,Integer.toString(body.readableBytes()));            
            }
            //完成消息体xml序列化后重新构造http请求消息加入到out中,由http请求编码器继续对http请求消息进行编码
            out.add(request);
        }
    
    }
    View Code

    Http响应编码类

    package com.netty.ch10.httpXML.response;
    
    import java.util.List;
    
    import com.netty.ch10.httpXML.common.AbstractHttpXmlEncoder;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.AsciiString;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.FullHttpResponse;
    import io.netty.handler.codec.http.HttpHeaderNames;
    import io.netty.handler.codec.http.HttpHeaderUtil;
    import io.netty.handler.codec.http.HttpHeaders;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.codec.http.HttpVersion;
    /**
     * Http+xml 应答消息编码类
     * @author Administrator
     *
     */
    public class HttpXmlResponseEncoder extends AbstractHttpXmlEncoder<HttpXmlResponse> {
    
        private static final HttpVersion HTTP_1_1 = HttpVersion.HTTP_1_1;
        private static final HttpResponseStatus  OK = HttpResponseStatus.OK;
        private static final AsciiString  CONTENT_TYPE = HttpHeaderNames.CONTENT_TYPE;
        @Override
        protected void encode(ChannelHandlerContext ctx, HttpXmlResponse msg, List<Object> out) throws Exception {
            ByteBuf body = encode0(ctx,msg.getResult());
            FullHttpResponse response = msg.getHttpResponse();
            if(response == null){
                response = new DefaultFullHttpResponse(HTTP_1_1,OK,body);
            }else{
                new DefaultFullHttpResponse(msg.getHttpResponse().protocolVersion(),
                        msg.getHttpResponse().status(), body);
            }
            response.headers().set(CONTENT_TYPE, "text/xml");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,Integer.toString(body.readableBytes()));
            //将编码后的response对象添加到编码结果链表中,由后续的Http编码类进行二次编码
            out.add(response);
        }
    
        
    
    }
    View Code

    Decoder编码基类

    package com.netty.ch10.httpXML.common;
    
    import java.io.StringReader;
    import java.net.SocketAddress;
    import java.nio.charset.Charset;
    import java.util.List;
    
    import org.jibx.runtime.BindingDirectory;
    import org.jibx.runtime.IBindingFactory;
    import org.jibx.runtime.IMarshallingContext;
    import org.jibx.runtime.IUnmarshallingContext;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelPromise;
    import io.netty.handler.codec.MessageToMessageDecoder;
    
    public abstract class AbstractHttpXmlDecoder<T> extends MessageToMessageDecoder<T> {
    
        private IBindingFactory factory;
        private StringReader reader;
        private Class<?> clazz;
        private boolean isPrint;
        private final static String CHARSET_NAME = "UTF-8";
        private final static Charset UTF_8 = Charset.forName(CHARSET_NAME);
        
        
        protected AbstractHttpXmlDecoder(Class<?> clazz){
            this(clazz, false);
        }
        protected AbstractHttpXmlDecoder(Class<?> clazz,boolean isPrint){
            this.clazz = clazz;
            this.isPrint = isPrint;
        }
        protected Object decode0(ChannelHandlerContext arg0,ByteBuf body) throws Exception{
            factory = BindingDirectory.getFactory(clazz);
            String context = body.toString(UTF_8);
            if(isPrint)
                System.out.println("The body is"+context);
            reader =new StringReader(context);
             IUnmarshallingContext uctx = factory.createUnmarshallingContext();
            Object result = uctx.unmarshalDocument(reader);
            reader.close();
            reader = null;
            return result;
        }
        
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            // TODO Auto-generated method stub
            super.write(ctx, msg, promise);
            if(reader != null){
                reader.close();
                reader = null;
            }
        }
        
    
        
    }
    View Code

    Http请求解码类

    package com.netty.ch10.httpXML.request;
    
    import java.util.List;
    
    import com.netty.ch10.httpXML.common.AbstractHttpXmlDecoder;
    
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.FullHttpRequest;
    import io.netty.handler.codec.http.FullHttpResponse;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.codec.http.HttpVersion;
    import io.netty.util.CharsetUtil;
    /**
     *     HTTP+XML Http请求消息解码类
     * @author Administrator
     *
     */
    public class HttpXmlRequestDecoder extends AbstractHttpXmlDecoder<FullHttpRequest> {
    
        public  HttpXmlRequestDecoder(Class<?> clazz) {
            this( clazz,false);
        }
        public  HttpXmlRequestDecoder(Class<?> clazz,boolean isPrint) {
            super(clazz,isPrint);
        }
        /* (non-Javadoc)
         * @see com.netty.ch10.httpXML.response.AbstractHttpXmlDecoder#decode(io.netty.channel.ChannelHandlerContext, java.lang.Object, java.util.List)
         */
        @Override
        protected void decode(ChannelHandlerContext arg0, FullHttpRequest arg1, List<Object> arg2) throws Exception {
            if(!arg1.decoderResult().isSuccess()){
                sendError(arg0, HttpResponseStatus.BAD_REQUEST);
                return;
            }
            //通过FullHttpRequest  和经过decode0将byteBuf反序列化的order对象  构造HttpXmlRequest
            HttpXmlRequest request = new HttpXmlRequest(arg1, decode0(arg0,arg1.content()));
            arg2.add(request);
        }
        
        private static void sendError(ChannelHandlerContext ctx,HttpResponseStatus status){
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,status
                    ,Unpooled.copiedBuffer("Failure:"+status.toString()+"
    ", CharsetUtil.UTF_8));
            response.headers().set("contextType","text/plain;charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
        
    }
    View Code

    Http响应解码类

    package com.netty.ch10.httpXML.response;
    
    import java.util.List;
    
    import com.netty.ch10.httpXML.common.AbstractHttpXmlDecoder;
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    
    public class HttpXmlResponseDecoder extends AbstractHttpXmlDecoder<DefaultFullHttpResponse> {
    
        public HttpXmlResponseDecoder(Class<?> clazz) {
            this(clazz,false);
            // TODO Auto-generated constructor stub
        }
    
        public HttpXmlResponseDecoder(Class<?> clazz,boolean isPrintLog) {
            super(clazz,isPrintLog);
            // TODO Auto-generated constructor stub
        }
        @Override
        protected void decode(ChannelHandlerContext ctx, DefaultFullHttpResponse msg, List<Object> out)
                throws Exception {
            //将反序列化的POJO对象构造成HttpXmlResponse  并添加到解码结果列表中
            HttpXmlResponse resHttpXmlResponse =
                    new HttpXmlResponse(msg,decode0(ctx,msg.content()));
            out.add(resHttpXmlResponse);
            
        }
    
    }
    View Code

    客户端及客户端请求handler

    package com.netty.ch10.httpXML;
    
    import java.net.InetSocketAddress;
    
    import com.netty.ch10.httpXML.pojo.Order;
    import com.netty.ch10.httpXML.request.HttpXmlRequestEncoder;
    import com.netty.ch10.httpXML.response.HttpXmlResponseDecoder;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpRequestDecoder;
    import io.netty.handler.codec.http.HttpRequestEncoder;
    
    /**
     * Http+xml客户端
     * @author Administrator
     *1)发起Http连接请求
     *2)构造订购请求消息,将其编码成xml,通过http协议发给服务端
     *3)构造http服务端的应答消息,将xml应答消息反序列化为订购消息POJO对象
     *4)关闭HTTP连接
     *
     *Handler的执行顺序  :https://my.oschina.net/jamaly/blog/272385
     *https://my.oschina.net/freegarden/blog/300348
     *
     *  执行顺序是:Encoder 先注册的后执行,与OutboundHandler一致;Decoder是先注册的先执行,与InboundHandler一致。
     *
     */
    public class HttpXmlClient {
    
        public void connect(int port) throws Exception{
            //配置客户端NIO线程组
            EventLoopGroup group = new NioEventLoopGroup();
            try{
                Bootstrap b = new Bootstrap();
                b.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        //负责将二进制码流解码成HTTP的应答消息
                        ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());
                        //负责将1个http请求消息的多个部分合并成一个完整的http消息
                        ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65535));
                        
                        
                        
                        //XML解码器
                        ch.pipeline().addLast("xml-decoder",new HttpXmlResponseDecoder(Order.class, true));
                        //【注意】:顺序,编码的时候按照从尾到头的顺序 HttpXmlClientHandler-->HttpXmlRequestEncoder-->HttpRequestEncoder
                        ch.pipeline().addLast("http-encoder",new HttpRequestEncoder());  //2)封装成完整的httprequest
                        ch.pipeline().addLast("xml-encoder",new HttpXmlRequestEncoder()); //1)pojo -->bytebuf
                        
                        //write POJO    receive POJO
                        ch.pipeline().addLast("xml-client-handler",new HttpXmlClientHandler());  
                    }
                });
                //发起异步连接请求
                ChannelFuture  f = b.connect(new InetSocketAddress(port)).sync();
                //等待客户端链路关闭
                f.channel().closeFuture().sync();
            }finally{
                group.shutdownGracefully();
            }
        }
        
        public static void main(String[] args) throws Exception{
            int port = 8080;
            if(args != null && args.length > 0){
                try {
                    port = Integer.valueOf(args[0]);
                } catch (Exception e) {
                    // TODO: handle exception
                }
            }
            new HttpXmlClient().connect(port);
        }
    }
    View Code
    package com.netty.ch10.httpXML;
    
    import com.netty.ch10.httpXML.request.HttpXmlRequest;
    import com.netty.ch10.httpXML.response.HttpXmlResponse;
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    public class HttpXmlClientHandler extends SimpleChannelInboundHandler<HttpXmlResponse> {
    
        @Override
        protected void messageReceived(ChannelHandlerContext ctx, HttpXmlResponse msg) throws Exception {
            System.out.println("the client receive response of http header is :"
                  + msg.getHttpResponse().headers().names());
            System.out.println("the client receive response of http body is :"+ msg.getResult());
            
        }
    
        /* (non-Javadoc)
         * @see io.netty.channel.ChannelHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            HttpXmlRequest request = new HttpXmlRequest(null, OrderFactory.create(124));
            ctx.writeAndFlush(request);
            
        }
    
        /* (non-Javadoc)
         * @see io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    
    }
    View Code

    服务端及服务端响应handler

    package com.netty.ch10.httpXML;
    
    import java.net.InetSocketAddress;
    
    import com.netty.ch10.httpXML.pojo.Order;
    import com.netty.ch10.httpXML.request.HttpXmlRequestDecoder;
    import com.netty.ch10.httpXML.response.HttpXmlResponseEncoder;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpRequestDecoder;
    import io.netty.handler.codec.http.HttpResponseDecoder;
    import io.netty.handler.codec.http.HttpResponseEncoder;
    
    /**
     * Http 服务端的功能如下
     * @author Administrator
     *1)接收Http客户端的连接
     *2)接收http客户端的xml请求消息,并将其解码成POJO对象
     *3)对pojo对象进行业务处理
     *4)通过http+xml的格式返回应答消息
     *5)主动关闭http连接
     */
    public class HttpXmlServer {
    
        public void run(final int port) throws Exception{
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup,workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
    
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());
                        ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));
                        ch.pipeline().addLast("xml-decoder",new HttpXmlRequestDecoder(Order.class,true));
                        
                        //下面顺序相反
                        ch.pipeline().addLast("http-encoder",new HttpResponseEncoder());
                        ch.pipeline().addLast("xml-encoder",new HttpXmlResponseEncoder());
                        
                        
                        ch.pipeline().addLast("xmlServerHandler",new HttpXmlServerHandler());
                        
                    }
                });
                ChannelFuture future = b.bind(new InetSocketAddress(port)).sync();
                System.out.println("Http 订购服务器启动,网址是:"+"http://localhost:"+port);
                
                // Wait until the connection is closed.
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                // TODO: handle exception
            }
            
        }
        
        public static void main(String[] args) throws Exception {
            int port = 8080;
            if(args.length>0){
                try{
                    port = Integer.parseInt(args[0]);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
            new HttpXmlServer().run(port);
        }
    }
    View Code
    package com.netty.ch10.httpXML;
    
    import java.util.ArrayList;
    import java.util.List;
    import com.netty.ch10.httpXML.pojo.Address;
    import com.netty.ch10.httpXML.pojo.Order;
    import com.netty.ch10.httpXML.request.HttpXmlRequest;
    import com.netty.ch10.httpXML.response.HttpXmlResponse;
    
    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.FullHttpResponse;
    import io.netty.handler.codec.http.HttpHeaderNames;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.codec.http.HttpVersion;
    import io.netty.util.CharsetUtil;
    import io.netty.util.concurrent.Future;
    import io.netty.util.concurrent.GenericFutureListener;
    
    public class HttpXmlServerHandler extends SimpleChannelInboundHandler<HttpXmlRequest> {
    
        @Override
        public void messageReceived(final ChannelHandlerContext ctx, HttpXmlRequest xmlRequest) throws Exception {
            FullHttpRequest request = xmlRequest.getRequest();
            Order order = (Order)xmlRequest.getBody();
            System.out.println("Http server receive request :"+ order);
            dobusiness(order);
            ChannelFuture future = ctx.writeAndFlush(new HttpXmlResponse(null,order));
            if(!ctx.channel().isActive()){
                future.addListener(new GenericFutureListener<Future<? super Void>>() {
                    
                    @Override
                    public void operationComplete(Future future) throws Exception {
                        // TODO Auto-generated method stub
                        ctx.close();
                    }
                    
                });
            }
            
        }
    
        private void dobusiness(Order order) {
            order.getCustomer().setFirstName("");
            order.getCustomer().setLastName("仁杰");
            List<String> midNames = new ArrayList<String>();
            midNames.add("李元芳");
            order.getCustomer().setMiddleNames(midNames);
            Address address = order.getBillTo();
            address.setCity("洛阳");
            address.setCountry("大唐");
            address.setState("河南道");
            address.setPostCode("123456");
            order.setBillTo(address);
            // order.setShiping(address);
    
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * io.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.
         * ChannelHandlerContext, java.lang.Throwable)
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            // TODO Auto-generated method stub
            cause.printStackTrace();
            if(ctx.channel().isActive()){
                sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
            }
        }
    
        private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
                    Unpooled.copiedBuffer("失败:" + status.toString() + "
    ", CharsetUtil.UTF_8));
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    
    }
    View Code

     WebSocket协议开发

    webSocket将网络套接字引入到客户端和服务端,浏览器和服务端之间可以通过套接字建立持久连接,双方随时都可以互发数据给对方,而不是之前由客户端控制的请求-应答模式。

    http协议的弊端:

    1)同一时刻只能在一个方向上传输数据(半双工)

    2)HTTP消息冗余而繁琐

    3)针对服务器推送的黑客攻击,如长时间轮询。

    webSocket特点:

    1)单一的TCP连接,采用全双工模式通信

    2)对代理,防火墙和路由器透明

    3)无头部信息,Cookie和身份验证

    4)无安全开销

    5)通过“ping/pong”帧保持链路激活

    6)服务器可以主动传递消息给客户端,不再需要客户端轮询。

    server

    package com.netty.ch11;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpServerCodec;
    import io.netty.handler.stream.ChunkedWriteHandler;
    
    /**
     * webSocketServer 启动端
     * 
     * @author Administrator
     *
     */
    public class WebSocketServer {
    
        public void run(int port) throws Exception {
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
    
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();
                                //将请求和应答消息编码和解码为http消息
                                pipeline.addLast("http-codec", new HttpServerCodec());
                                //将Http消息的多个部分组合成一条完整的http消息
                                pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
                                //来向客户端发送html5文件   
                                ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                                ch.pipeline().addLast("handler", new WebSocketServerHandler());
                            }
                        });
    
                Channel ch = b.bind(port).sync().channel();
                System.out.println("Web socket server started at port " + port + '.');
                System.out.println("Open your browser and navigate to http://localhost:" + port + '/');
    
                ch.closeFuture().sync();
            } finally {
                System.out.println("stop server");
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            int port = 8080;
            if (args.length > 0) {
                try {
                    port = Integer.parseInt(args[0]);
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
            }
            new WebSocketServer().run(port);
        }
    }
    View Code

    websocket处理类

    package com.netty.ch11;
    
    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.FullHttpResponse;
    import io.netty.handler.codec.http.HttpHeaderNames;
    import io.netty.handler.codec.http.websocketx.*;
    import io.netty.util.CharsetUtil;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
    import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
    
    /**
     * @version 1.0
     * @date 2014年2月14日
     */
    public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
        private static final Logger logger = Logger
                .getLogger(WebSocketServerHandler.class.getName());
    
        //握手处理类
        private WebSocketServerHandshaker handshaker;
    
    
        @Override
        public void messageReceived(ChannelHandlerContext ctx, Object msg)
                throws Exception {
            // 传统的HTTP接入  
            if (msg instanceof FullHttpRequest) {
                handleHttpRequest(ctx, (FullHttpRequest) msg);
            }
            // WebSocket接入   前端请求websocket    socket.onmessage
            else if (msg instanceof WebSocketFrame) {
                handleWebSocketFrame(ctx, (WebSocketFrame) msg);
            }
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.flush();
        }
    
        private void handleHttpRequest(ChannelHandlerContext ctx,
                                       FullHttpRequest req) throws Exception {
    
            // 如果HTTP解码失败,返回HHTP异常
            if (!req.decoderResult().isSuccess()
                    || (!"websocket".equals(req.headers().get("Upgrade")))) {
                sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,
                        BAD_REQUEST));
                return;
            }
    
            // 构造握手响应返回,本机测试
            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                    "ws://localhost:8080/websocket", null, false);
            //创建握手消息处理类
            handshaker = wsFactory.newHandshaker(req);
            //无法处理的websocket
            if (handshaker == null) {
                WebSocketServerHandshakerFactory
                        .sendUnsupportedVersionResponse(ctx.channel());
            } else {
                handshaker.handshake(ctx.channel(), req);
            }
        }
    
        private void handleWebSocketFrame(ChannelHandlerContext ctx,
                                          WebSocketFrame frame) {
    
            // 判断是否是关闭链路的指令
            if (frame instanceof CloseWebSocketFrame) {
                handshaker.close(ctx.channel(),
                        (CloseWebSocketFrame) frame.retain());
                return;
            }
            // 判断是否是Ping消息
            if (frame instanceof PingWebSocketFrame) {
                ctx.channel().write(
                        new PongWebSocketFrame(frame.content().retain()));
                return;
            }
            // 本例程仅支持文本消息,不支持二进制消息
            if (!(frame instanceof TextWebSocketFrame)) {
                throw new UnsupportedOperationException(String.format(
                        "%s frame types not supported", frame.getClass().getName()));
            }
    
            // 返回应答消息
            String request = ((TextWebSocketFrame) frame).text();
            if (logger.isLoggable(Level.FINE)) {
                logger.fine(String.format("%s received %s", ctx.channel(), request));
            }
            ctx.channel().write(
                    new TextWebSocketFrame(request
                            + " , 欢迎使用Netty WebSocket服务,现在时刻:"
                            + new java.util.Date().toString()));
        }
    
        private static void sendHttpResponse(ChannelHandlerContext ctx,
                                             FullHttpRequest req, FullHttpResponse res) {
            // 返回应答给客户端
            if (res.status().code() != 200) {
                ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(),
                        CharsetUtil.UTF_8);
                res.content().writeBytes(buf);
                buf.release();
                res.headers().set(HttpHeaderNames.CONTENT_LENGTH
                        ,Integer.toString(res.content().readableBytes()));
            }
    
            // 如果是非Keep-Alive,关闭连接
            ChannelFuture f = ctx.channel().writeAndFlush(res);
            if (!ctx.channel().isActive() || res.status().code() != 200) {
                f.addListener(ChannelFutureListener.CLOSE);
            }
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    
    
    }
    View Code

    client html

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        Netty WebSocket 时间服务器
    </head>
    <br>
    <body>
    <br>
    <script type="text/javascript">
        var socket;
        if (!window.WebSocket) {
            window.WebSocket = window.MozWebSocket;
        }
        if (window.WebSocket) {
            socket = new WebSocket("ws://localhost:8080/websocket");
            socket.onmessage = function (event) {
                var ta = document.getElementById('responseText');
                ta.value = "";
                ta.value = event.data
            };
            socket.onopen = function (event) {
                var ta = document.getElementById('responseText');
                ta.value = "打开WebSocket服务正常,浏览器支持WebSocket!";
            };
            socket.onclose = function (event) {
                var ta = document.getElementById('responseText');
                ta.value = "";
                ta.value = "WebSocket 关闭!";
            };
        }
        else {
            alert("抱歉,您的浏览器不支持WebSocket协议!");
        }
    
        function send(message) {
            if (!window.WebSocket) {
                return;
            }
            if (socket.readyState == WebSocket.OPEN) {
                socket.send(message);
            }
            else {
                alert("WebSocket连接没有建立成功!");
            }
        }
    </script>
    <form onsubmit="return false;">
        <input type="text" name="message" value="Netty最佳实践"/>
        <br><br>
        <input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/>
        <hr color="blue"/>
        <h3>服务端返回的应答消息</h3>
        <textarea id="responseText" style="500px;height:300px;"></textarea>
    </form>
    </body>
    </html>
    View Code

     私有协议栈开发

     绝大多数的私有协议传输层都是基于TCP/IP,所以利用Netty的NIO TCP协议栈可以很方便的进行私有协议的定制和开发。

    Netty协议栈功能设计

    通信模型

    1)Netty协议栈client发送握手消息,携带节点IP等有效身份认证信息。

    2)Netty协议栈服务端对握手请求消息进行合法性的认证,包括节点ID有效性检验,节点重复登录校验和IP地址合法性校验,校验通过后,返回登录成功的握手应答消息。

    3)链路建立成功后,client发送业务消息。

    4)链路成功后,server发送心跳消息。

    5)链路建立成功之后,client发送心跳消息。

    6)链路建立成功后,server发送业务消息。

    7)server退出后,server关闭连接,client感知对方关闭连接后,被动关闭连接。

    注意:双方采用全双工的通信方式,并不区分server与client。双方之间采用ping-pong心跳机制。链路断开client主动关闭连接,间隔T周期发起重连,直至成功。

    消息定义:消息头,消息体。

    链路的建立:创建握手请求消息,同时server基于IP地址进行黑白名单安全认证机制,或者通过密钥+数字证书进行接入认证。

    链路的关闭:双方通过心跳和业务消息维持链路的长连接,任何一方都不需要主动关闭连接,但是以下情况需要关闭连接,

      1)对方宕机或重启。

      2)消息读写发生IO异常。

      3)心跳发生IO异常或者超市。

      4)发生编码异常等不可恢复。

    心跳机制(ping-pong):在网络空闲时采用心跳机制检验链路的互通性,一旦发现网络故障,立即关闭链路,主动连接。

      1)当网络处于空闲状态,连续T周期没有读写,则client主动发送ping心跳消息给服务端。

      2)如果在下个周期T到来时client没有收到对方发送的Pong心跳应答消息或者读取到服务端发送的其他业务消息,则心跳计数器+1.

      3)一旦收到业务消息或者Pong消息,则心跳计数器=0,N次没有收到服务端应答,则关闭链路,间隔INTERVAL时间后发起重连。

      4)server网络空闲状态时间T后,心跳失败计数器+1,N次没有接受到client请求则关闭链路等待client重连,一旦接受到ping或业务消息,计数器清零。

    注意:通过Ping-Pong双向心跳机制,保证无论哪一方出现问题,都被及时检测出来。N次检测防止对方短时间繁忙没有及时应答而造成的误判。

    重连机制: 间隔INTERVAL时间不断重连,保证服务端有足够的时间释放句柄资源,再发起重新连接,直至成功。

    重复登录保护:在缓存地址表中查看client是否已经登录,如果已经登录,则拒绝重复登录,返回错误码-1,并关闭链路。如心跳失败同时也清空该client的地址缓存表信息。

    消息缓存重发:链路中断后,待发送的消息缓存在消息队列不能丢失,等恢复链路后消息重新发送,同时设置队列上线,防止OOM,超过上限聚集添加新的消息。

    安全性:IP黑白名单,SSL/TSL会话。

    私有栈协议开发

    数据结构定义:

    package com.netty.ch12.msg;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class Header {
    
        private int crcCode = 0xabef0101;
        private int length ;//消息长度
        private long sessionID;//会话ID
        private byte type;//消息类型
        private byte priority;//消息优先级
        private Map<String,Object> attachment = new HashMap<String,Object>();//附件  同时便于扩展
        /**
         * @return the crcCode
         */
        public int getCrcCode() {
            return crcCode;
        }
        /**
         * @param crcCode the crcCode to set
         */
        public void setCrcCode(int crcCode) {
            this.crcCode = crcCode;
        }
        /**
         * @return the length
         */
        public int getLength() {
            return length;
        }
        /**
         * @param length the length to set
         */
        public void setLength(int length) {
            this.length = length;
        }
        /**
         * @return the sessionID
         */
        public long getSessionID() {
            return sessionID;
        }
        /**
         * @param sessionID the sessionID to set
         */
        public void setSessionID(long sessionID) {
            this.sessionID = sessionID;
        }
        /**
         * @return the type
         */
        public byte getType() {
            return type;
        }
        /**
         * @param type the type to set
         */
        public void setType(byte type) {
            this.type = type;
        }
        /**
         * @return the priority
         */
        public byte getPriority() {
            return priority;
        }
        /**
         * @param priority the priority to set
         */
        public void setPriority(byte priority) {
            this.priority = priority;
        }
        /**
         * @return the attachment
         */
        public Map<String, Object> getAttachment() {
            return attachment;
        }
        /**
         * @param attachment the attachment to set
         */
        public void setAttachment(Map<String, Object> attachment) {
            this.attachment = attachment;
        }
        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return "Header [crcCode=" + crcCode + ", length=" + length + ", sessionID=" + sessionID + ", type=" + type
                    + ", priority=" + priority + ", attachment=" + attachment + "]";
        }
        
        
    }
    View Code
    package com.netty.ch12.msg;
    
    /**
     * 对netty协议栈使用到的数据结构进行定义  
     * @author Administrator
     *
     */
    public final class NettyMessage {
    
        private Header header; //消息头
        private Object body;//消息体
        /**
         * @return the header
         */
        public final Header getHeader() {
            return header;
        }
        /**
         * @param header the header to set
         */
        public final void setHeader(Header header) {
            this.header = header;
        }
        /**
         * @return the body
         */
        public final Object getBody() {
            return body;
        }
        /**
         * @param body the body to set
         */
        public final  void setBody(Object body) {
            this.body = body;
        }
        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return "NettyMessage [header=" + header + ", body=" + body + "]";
        }    
        
    }
    View Code

    Netty消息编码工具类:

    package com.netty.ch12.util;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.marshalling.MarshallingDecoder;
    import io.netty.handler.codec.marshalling.UnmarshallerProvider;
    
    public class NettyMarshallingDecoder extends MarshallingDecoder{  
          
        public NettyMarshallingDecoder(UnmarshallerProvider provider) {  
            super(provider);  
        }  
      
        public NettyMarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize){  
            super(provider, maxObjectSize);  
        }  
          
        public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {  
            return super.decode(ctx, in);  
        }  
          
    }  
    View Code
    package com.netty.ch12.util;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.marshalling.MarshallerProvider;
    import io.netty.handler.codec.marshalling.MarshallingDecoder;
    import io.netty.handler.codec.marshalling.MarshallingEncoder;
    import io.netty.handler.codec.marshalling.UnmarshallerProvider;
    
    public class NettyMarshallingEncoder  extends MarshallingEncoder{  
      
        public NettyMarshallingEncoder(MarshallerProvider provider) {  
            super(provider);  
        }  
      
        public void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception{  
            super.encode(ctx, msg, out);  
        }  
    }
    View Code
    package com.netty.ch12.util;
    
    import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;  
    import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;  
    import io.netty.handler.codec.marshalling.MarshallerProvider;  
    import io.netty.handler.codec.marshalling.MarshallingDecoder;  
    import io.netty.handler.codec.marshalling.UnmarshallerProvider;  
      
    import org.jboss.marshalling.MarshallerFactory;  
    import org.jboss.marshalling.Marshalling;  
    import org.jboss.marshalling.MarshallingConfiguration;  
      
    public class MarshallingCodeCFactory {  
        public static NettyMarshallingDecoder buildMarshallingDecoder(){  
            MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");  
            MarshallingConfiguration configuration = new MarshallingConfiguration();  
            configuration.setVersion(5);  
            UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);  
            NettyMarshallingDecoder decoder = new NettyMarshallingDecoder(provider, 1024);  
            return decoder;  
        }  
          
        public static NettyMarshallingEncoder buildMarshallingEncoder(){  
            MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");  
            MarshallingConfiguration configuration = new MarshallingConfiguration();  
            configuration.setVersion(5);  
            MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);  
            NettyMarshallingEncoder encoder = new NettyMarshallingEncoder(provider);  
            return encoder;  
        }  
    }  
    View Code

    消息编解码:

    package com.netty.ch12.handler;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Map;
    
    import com.netty.ch12.msg.NettyMessage;
    import com.netty.ch12.util.MarshallingCodeCFactory;
    import com.netty.ch12.util.NettyMarshallingEncoder;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.MessageToMessageEncoder;
    /**
     * nettty 消息编码类
     * @author Administrator
     *
     */
    public class NettyMessageEncoder extends MessageToMessageEncoder<NettyMessage> {
    
          private NettyMarshallingEncoder marshallingEncoder;  
          
            public NettyMessageEncoder(){  
                marshallingEncoder = MarshallingCodeCFactory.buildMarshallingEncoder();  
            }  
        @Override
        protected void encode(ChannelHandlerContext ctx, NettyMessage msg, List<Object> out) throws Exception {
            if(msg == null || msg.getHeader() == null) 
                throw new Exception("The encode message is null");
            ByteBuf sendBuf = Unpooled.buffer();
            sendBuf.writeInt(msg.getHeader().getCrcCode());
            sendBuf.writeInt(msg.getHeader().getLength());
            sendBuf.writeLong(msg.getHeader().getSessionID());
            sendBuf.writeByte(msg.getHeader().getType());
            sendBuf.writeByte(msg.getHeader().getPriority());
            sendBuf.writeInt(msg.getHeader().getAttachment().size());
            
            String key = null;
            byte[]  keyArray = null;
            Object value = null;
            for(Map.Entry<String, Object> param : msg.getHeader().getAttachment().entrySet()){
                key = param.getKey();
                keyArray = key.getBytes("utf-8");
                sendBuf.writeInt(keyArray.length);
                sendBuf.writeBytes(keyArray);
                value = param.getValue();
                marshallingEncoder.encode(ctx,value, sendBuf);
            }
            
            key = null;
            keyArray = null;
            value = null;
            if(msg.getBody() != null){
                marshallingEncoder.encode(ctx,msg.getBody(), sendBuf);
            }//else
                //sendBuf.writeInt(0);
             // 在第4个字节处写入Buffer的长度  
            sendBuf.setInt(4, sendBuf.readableBytes());
            // 把Message添加到List传递到下一个Handler   
            out.add(sendBuf);  
        }
    
    }
    View Code
    package com.netty.ch12.handler;
    
    import io.netty.buffer.ByteBuf;  
    import io.netty.channel.ChannelHandlerContext;  
    import io.netty.handler.codec.LengthFieldBasedFrameDecoder;  
      
    import java.util.HashMap;  
    import java.util.Map;
    
    import com.netty.ch12.msg.Header;
    import com.netty.ch12.msg.NettyMessage;
    import com.netty.ch12.util.MarshallingCodeCFactory;
    import com.netty.ch12.util.NettyMarshallingDecoder;  
      /**
       * netty 消息解码类
       * @author Administrator
       *
       */
    public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder{  
      
        private NettyMarshallingDecoder marshallingDecoder;  
          
        public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset,  
                int lengthFieldLength) {  
            super(maxFrameLength, lengthFieldOffset, lengthFieldLength);  
            marshallingDecoder = MarshallingCodeCFactory.buildMarshallingDecoder();  
        }  
          
        public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset,  
                int lengthFieldLength,int lengthAdjustment, int initialBytesToStrip) {  
            super(maxFrameLength, lengthFieldOffset, lengthFieldLength,lengthAdjustment, initialBytesToStrip);  
            marshallingDecoder = MarshallingCodeCFactory.buildMarshallingDecoder();  
        }  
        public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception{  
            ByteBuf frame = (ByteBuf)super.decode(ctx, in); 
            //为空说明是半包消息,直接返回由i/o线程读取后续的码流
            if(frame == null){  
                return null;  
            }  
              
            NettyMessage message = new NettyMessage();  
            Header header = new Header();  
            header.setCrcCode(frame.readInt());  
            header.setLength(frame.readInt());  
            header.setSessionID(frame.readLong());  
            header.setType(frame.readByte());  
            header.setPriority(frame.readByte());  
              
            int size = frame.readInt();  
            if(size > 0){  
                Map<String, Object> attach = new HashMap<String, Object>(size);  
                int keySize = 0;  
                byte[] keyArray = null;  
                String key = null;  
                for(int i=0; i<size; i++){  
                    keySize = frame.readInt();  
                    keyArray = new byte[keySize];  
                    in.readBytes(keyArray);  
                    key = new String(keyArray, "UTF-8");  
                    attach.put(key, marshallingDecoder.decode(ctx, frame));  
                }  
                key = null;  
                keyArray = null;  
                header.setAttachment(attach);  
            }   
            if(frame.readableBytes() > 0){  
                message.setBody(marshallingDecoder.decode(ctx, frame));  
            }  
            message.setHeader(header);  
            return message;  
        }  
    } 
    View Code

    common类

    package com.netty.ch12.comon;
    
    public enum MessageType {
    
        LOGIN_REQ{
            public byte  value(){
                return (byte)1;
            }
        },LOGIN_RESP{
            public byte  value(){
                return (byte)2;
            }
        },HEARTBEAT_REQ{
    
            @Override
            public byte value() {
                // TODO Auto-generated method stub
                return (byte)3;
            }
            
        },HEARTBEAT_RESP{
    
            @Override
            public byte value() {
                // TODO Auto-generated method stub
                return (byte)4;
            }
            
        };
       public abstract byte value();
    }
    View Code
    package com.netty.ch12.comon;
    
    public class NettyConstant {
        public static final String LOCALIP = "127.0.0.1";
        public static final String REMOTEIP = "127.0.0.1";
        public static final int LOCAL_PORT = 12088;
        public static final int PORT = 8080;
    
    }
    View Code

    握手和安全认证:

    握手的发起是在客户端和服务端TCP链路建立成功通道激活时,握手消息的接入和安全认证在服务端处理。

    package com.netty.ch12.handler;
    
    import com.netty.ch12.comon.MessageType;
    import com.netty.ch12.msg.Header;
    import com.netty.ch12.msg.NettyMessage;
    
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    
    public class LoginAuthReqHandler extends ChannelHandlerAdapter {
    
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.writeAndFlush(buildLoginReq());
        }
    
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            NettyMessage message = (NettyMessage) msg;
            if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.value()) {
                System.out.println("Received from server response");
                byte loginResult = (byte) message.getBody();
                if (loginResult != (byte) 0) {
                    // 握手失败 关闭连接  
                    ctx.close();
                } else {
                    System.out.println("Login is ok :" + message);
                    ctx.fireChannelRead(msg);
                }
            } else {
                ctx.fireChannelRead(msg);
            }
    
        }
    
        private NettyMessage buildLoginReq() {
            NettyMessage message = new NettyMessage();
            Header header = new Header();
            header.setType(MessageType.LOGIN_REQ.value());
            message.setHeader(header);
            message.setBody("It is request");
            return message;
        }
    
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.flush();
        }
    
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            ctx.close();
        }
    }
    View Code
    package com.netty.ch12.handler;
    
    import java.net.InetSocketAddress;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import com.netty.ch12.comon.MessageType;
    import com.netty.ch12.msg.Header;
    import com.netty.ch12.msg.NettyMessage;
    
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    
    public class LoginAuthRespHandler extends ChannelHandlerAdapter { 
     //地址缓存
      private Map<String,Boolean> nodeCheck = new ConcurrentHashMap<String,Boolean>();
      private String[] whiteList = {"127.0.0.1","192.168.1.104"};
        public void channelRead(ChannelHandlerContext ctx, Object msg)  
                throws Exception {  
            NettyMessage message = (NettyMessage)msg; 
            //如果是握手请求消息则处理   其他消息透传
            if(message.getHeader() != null 
                    && message.getHeader().getType() == MessageType.LOGIN_REQ.value()){  
                String nodeIndex = ctx.channel().remoteAddress().toString();
                NettyMessage loginResp = null;
                //重复登录 拒绝
                if(nodeCheck.containsKey(nodeIndex)){
                    loginResp = buildLoginResponse((byte)-1);
                }else{
                    InetSocketAddress address  = (InetSocketAddress) ctx.channel().remoteAddress();
                    String ip = address.getAddress().getHostAddress();
                    System.out.println("获取client连接:"+ip);
                    boolean isOk = false;
                    for(String WIP : whiteList){
                        if(WIP.equals(ip)){
                            isOk = true;
                            break;
                        }
                    }
                    loginResp = isOk ? buildLoginResponse((byte)0) : buildLoginResponse((byte)-1);
                    if(isOk){
                        nodeCheck.put(nodeIndex, true);
                    }
                    System.out.println("The login response is :" 
                                   + loginResp + "body [" + loginResp.getBody() + "]");
                    ctx.writeAndFlush(loginResp);
                }
     
            }else{
                ctx.fireChannelRead(msg);
            } 
           
        }  
      
        private NettyMessage buildLoginResponse(byte result) {  
            NettyMessage message = new NettyMessage();  
            Header header = new Header();  
            header.setType(MessageType.LOGIN_RESP.value());  
            message.setHeader(header);  
            message.setBody(result);  
            return message;  
        }  
          
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
            ctx.flush();  
        }  
      
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            nodeCheck.remove(ctx.channel().remoteAddress().toString());//删除缓存
            ctx.close(); 
            ctx.fireExceptionCaught(cause);
        }  
    }  
    View Code

    心跳检测机制

    package com.netty.ch12.handler;
    
    import java.util.concurrent.TimeUnit;
    
    import com.netty.ch12.comon.MessageType;
    import com.netty.ch12.msg.Header;
    import com.netty.ch12.msg.NettyMessage;
    
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.concurrent.ScheduledFuture;
    
    public class HeartBeatReqHandler extends ChannelHandlerAdapter{
    
        private volatile ScheduledFuture<?> heartBeat;
    
        /* (non-Javadoc)
         * @see io.netty.channel.ChannelHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // TODO Auto-generated method stub
            NettyMessage message = (NettyMessage) msg;
            //握手成功(如果握手不成功,则已经关闭连接,不会执行至此)  主动发送心跳消息
            if(message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.value()){
                heartBeat = ctx.executor().scheduleAtFixedRate(new HeartBeatTask(ctx), 0, 5000, TimeUnit.MILLISECONDS);
            }else if(message.getHeader().getType() == MessageType.HEARTBEAT_RESP.value()){
                System.out.println("Client receive server heart beat message : -- >" + message);
            }else{
                ctx.fireChannelRead(msg);
            }
            
        }
        
        //心跳探测
        private class HeartBeatTask implements Runnable{
    
            private final ChannelHandlerContext ctx;
            
            public HeartBeatTask(final ChannelHandlerContext ctx){
                this.ctx = ctx;
            }
            @Override
            public void run() {
                NettyMessage heatBeat = buildHeatBeat();
                System.out.println("client send heart beat message to server : --> " + heartBeat);
                ctx.writeAndFlush(heatBeat);
            }
            
            private NettyMessage buildHeatBeat(){
                NettyMessage message = new NettyMessage();
                Header header = new Header();
                header.setType(MessageType.HEARTBEAT_REQ.value());
                message.setHeader(header);
                return message;
            }
        }
    
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            // TODO Auto-generated method stub
            if(heartBeat != null){
                heartBeat.cancel(true);
                heartBeat = null;
            }
            ctx.fireExceptionCaught(cause);
        }
    
        
    }
    View Code
    package com.netty.ch12.handler;
    
    import com.netty.ch12.comon.MessageType;
    import com.netty.ch12.msg.Header;
    import com.netty.ch12.msg.NettyMessage;
    
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    
    public class HeartBeatRespHandler extends ChannelHandlerAdapter{
    
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
           NettyMessage message = (NettyMessage) msg;
           //返回心跳应答消息
           if(message.getHeader() != null 
                   && message.getHeader().getType() == MessageType.HEARTBEAT_REQ.value()){
               System.out.println("Receive client heart beat message : ---> " + message);
               NettyMessage heartBeat = buildHeatBeat();
               System.out.println("Send heart beat response message to client : --->" +heartBeat);
               ctx.writeAndFlush(heartBeat);
           }else{
               ctx.fireChannelRead(message);
           }
        }
    
        private NettyMessage buildHeatBeat(){
            NettyMessage message = new NettyMessage();
            Header header = new Header();
            header.setType(MessageType.HEARTBEAT_RESP.value());
            message.setHeader(header);
            return message;
        }
    }
    View Code

    客户端

    package com.netty.ch12;
    
    import java.net.InetSocketAddress;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    import com.netty.ch12.comon.NettyConstant;
    import com.netty.ch12.handler.HeartBeatReqHandler;
    import com.netty.ch12.handler.LoginAuthReqHandler;
    import com.netty.ch12.handler.NettyMessageDecoder;
    import com.netty.ch12.handler.NettyMessageEncoder;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.timeout.ReadTimeoutHandler;
    
    /**
     * 客户端主要用于初始化系统资源,根据配置信息发起连接
     * @author Administrator
     *
     */
    public class NettyClient {
    
        private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        EventLoopGroup group =  new NioEventLoopGroup();
        
        public void connect(int port ,String host) throws Exception{
            try{
                Bootstrap b = new Bootstrap();
                b.group(group).channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
    
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new NettyMessageDecoder(1024 * 1024, 4, 4, -8, 0));  
                        ch.pipeline().addLast("MessageEncoder",new NettyMessageEncoder());
                        //心跳超时的实现非常简单,利用ReadTimeoutHandler机制,当一定周期内(50s)没有读取到对方任何消息,需要主动关闭连接。
                        //如果是client,发起重新连接,如果是server,释放资源,清除客户端登录缓存信息,等待服务端重连。
                        ch.pipeline().addLast("readTimeoutHandler",new ReadTimeoutHandler(50));
                        ch.pipeline().addLast("LoginAuthHandler",new LoginAuthReqHandler());
                        ch.pipeline().addLast("HeartBeatHandler",new HeartBeatReqHandler());
                    }
                });
                //发起异步连接操作
                ChannelFuture future = b.connect(new InetSocketAddress(host,port)
                        ,new InetSocketAddress(NettyConstant.LOCALIP
                                ,NettyConstant.LOCAL_PORT));
                System.out.println("client to server "+host+":"+port);
                future.channel().closeFuture().sync();
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                //所有资源释放完成之后,清空资源,再次发起重新连接
                executor.execute(new Runnable(){
    
                    @Override
                    public void run() {
                        
                        try {
                            //等待5s
                            TimeUnit.SECONDS.sleep(5);
                            //发起重新连接
                            connect(NettyConstant.PORT, NettyConstant.REMOTEIP);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        
                    }
                    
                });
            }
        
        }
        
        public static void main(String[] args) throws Exception{
            new NettyClient().connect(NettyConstant.PORT, NettyConstant.REMOTEIP);
        }
    }
    View Code

    服务端

    package com.netty.ch12;
    
    import com.netty.ch12.comon.NettyConstant;
    import com.netty.ch12.handler.HeartBeatRespHandler;
    import com.netty.ch12.handler.LoginAuthReqHandler;
    import com.netty.ch12.handler.LoginAuthRespHandler;
    import com.netty.ch12.handler.NettyMessageDecoder;
    import com.netty.ch12.handler.NettyMessageEncoder;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.logging.LogLevel;
    import io.netty.handler.logging.LoggingHandler;
    import io.netty.handler.timeout.ReadTimeoutHandler;
    
    /**
     * 服务端   : 主要的工作就是握手的接入认证   不关心断连重连的问题
     * @author Administrator
     * netstat -ano|findstr "8080"
     */
    public class NettyServer {
    
        public void bind() throws Exception{
            //配置服务端的NIO线程组
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workerGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 100)
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(new ChannelInitializer<SocketChannel>() {
    
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new NettyMessageDecoder(1024 * 1024, 4, 4, -8, 0));
                    ch.pipeline().addLast(new NettyMessageEncoder());
                    //当一定周期内没有读取对方任何消息 则主动关闭链路
                    ch.pipeline().addLast("readTimeoutHandler",new ReadTimeoutHandler(50));
                    ch.pipeline().addLast(new LoginAuthRespHandler());
                    ch.pipeline().addLast("HeartBeatHandler",new HeartBeatRespHandler());
                }
            });
            
            ChannelFuture f = b.bind(NettyConstant.REMOTEIP,NettyConstant.PORT).sync();
            System.out.println("Netty server start ok : " 
                             +(NettyConstant.REMOTEIP + ":" + NettyConstant.PORT));
            f.channel().closeFuture().sync();
        }
        
        public static void main(String[] args) throws Exception {
            new NettyServer().bind();
        }
        
    }
    View Code
  • 相关阅读:
    Balder 3D开发系列之创建基本动画
    Expression Blend 的点滴(1)ListBox华丽大变身
    Balder 3D开发系列之创建天空盒
    Balder 3D开发系列之创建自己的primitives
    silverlight 中的应用程序库缓存
    Balder 3D开发系列之——sprite结合Camera旋转木马特效
    VisualTreeHelper不仅仅只是用来查看可视化树结构的
    Balder 3D开发系列之与Sprite初次相遇
    Balder 3D开发系列之给自定义基本体进行贴图操作
    【图解】通过Expression Blend快速制作具有物理效果的动画游戏
  • 原文地址:https://www.cnblogs.com/gaojy/p/7082897.html
Copyright © 2020-2023  润新知