zoukankan      html  css  js  c++  java
  • HTTP协议开发应用-HTTP&XML协议栈开发

    Netty HTTP+XML协议栈开发

    由于HTTP协议的通用性,很多异构系统间的通信交互采用HTTP协议,通过HTTP协议承载业务数据进行消息交互,例如非常流行的HTTP+XML或者RESTful+JSON。

    场景设计

    模拟一个简单的用户订购系统。客户端填写订单,通过HTTP客户端向服务端发送订购请求,请求消息放在HTTP消息体中,以XML承载,即采用HTTP+XML的方式进行通信。HTTP服务端接收到订购请求后,对订单请求进行修改,然后通过HTTP+XML的方式返回应答消息。双方采用HTTP1.1协议,连接类型为CLOSE方式,即双方交互完成,由HTTP服务端主动关闭链路,随后客户端也关闭链路并退出。

    订购请求消息定义如表:

    客户信息定义如表:

    地址信息定义如表:

    邮递方式定义如表:

    HTTP+XML协议栈设计

    首先对订购流程图进行分析,先看步骤1,构造订购请求消息并将其编码为HTTP+XML形式。Netty的HTTP协议栈提供了构造HTTP请求消息的相关接口,但是无法将普通的POJO对象转换为HTTP+XML的HTTP请求消息,需要自定义HTTP+XML格式的请求消息编码器。

    再看步骤2,利用Netty的HTTP协议栈,可以支持HTTP链路的建立和请求消息的发送,所以不需要额外开发,直接重用Netty的能力即可。

    步骤3,HTTP服务端需要将HTTP+XML格式的订购请求消息解码为订购请求POJO对象,同时获取HTTP请求消息头信息。利用Netty的HTTP协议栈服务端,可以完成HTTP请求消息的解码,但是,如果消息体为XML格式,Netty无法支持将其解码为POJO对象,需要在Netty协议栈的基础上扩展实现。

    步骤4,服务端对订购请求消息处理完成后,重新将其封装成XML,通过HTTP应答消息体携带给客户端,Netty的HTTP协议栈不支持直接将POJO对象的应答消息以XML方式发送,需要定制。

    步骤5,HTTP客户端需要将HTTP+XML格式的应答消息解码为订购POJO对象,同时能够获取应答消息的HTTP头信息,Netty的协议栈不支持自动的消息解码。

    通过分析,我们可以了解到哪些能力是Netty支持的,哪些需要扩展开发实现。下面给出设计思路。

    (1)需要一套通用、高性能的XML序列化框架,它能够灵活地实现POJO-XML的互相转换,最好能够通过工具自动生成绑定关系,或者通过XML的方式配置双方的映射关系;

    (2)作为通用的HTTP+XML协议栈,XML-POJO对象的映射关系应该非常灵活,支持命名空间和自定义标签;

    (3)提供HTTP+XML请求消息编码器,供HTTP客户端发送请求消息自动编码使用;

    (4)提供HTTP+XML请求消息解码器,供HTTP服务端对请求消息自动解码使用;

    (5)提供HTTP+XML响应消息编码器,供HTTP服务端发送响应消息自动编码使用;

    (6)提供HTTP+XML响应消息编码器,供HTTP客户端对应答消息进行自动解码使用;

    (7)协议栈使用者不需要关心HTTP+XML的编解码,对上层业务零侵入,业务只需要对上层的业务POJO对象进行编排。

    高效的XML绑定框架JiBx

    使用JiBX绑定XML文档与Java对象需要分两步走:第一步是绑定XML文件,也就是映射XML文件与Java对象之间的对应关系;第二步是在运行时,实现XML文件与Java实例之间的互相转换。这时,它已经与绑定文件无关了,可以说是完全脱耦了。

    在运行程序之前,需要先配置绑定文件并进行绑定,在绑定过程中它将会动态地修改程序中相应的class文件,主要是生成对应对象实例的方法和添加被绑定标记的属性JiBX_bindingList等。它使用的技术是BCEL(Byte Code Engineering Library),BCEL是Apache Software Foundation的Jakarta项目的一部分,也是目前Java classworking最广泛使用的一种框架,它可以让你深入JVM汇编语言进行类操作。在JiBX运行时,它使用了目前比较流行的一个技术XPP(Xml Pull Parsing),这也正是JiBX如此高效的原因。

    JiBx有两个比较重要的概念:Unmarshal(数据分解)和Marshal(数据编排)。从字面意思也很容易理解,Unmarshal是将XML文件转换成Java对象,而Marshal则是将Java对象编排成规范的XML文件。JiBX在Unmarshal/Marshal上如此高效,这要归功于使用了XPP技术,而不是使用基于树型(tree-based)方式,将整个文档写入内存,然后进行操作的DOM(Document Object Model),也不是使用基于事件流(event stream)的SAX(Simple API for Xml)。XPP使用的是不断增加的数据流处理方式,同时允许在解析XML文件时中断。

    POJO对象定义完成之后,通过Ant脚本来生成XML和POJO对象的绑定关系文件,同时也附加生成XML的Schema定义文件。

    JiBx的绑定和编译,通过JiBx的org.jibx.binding.generator.BindGen工具类可以将指定的POJO对象Order类生成绑定文件和Schema定义文件。

    JiBx的编译命令,它的作用是根据绑定文件和POJO对象的映射关系和规则动态修改POJO类

     代码示例:

    import lombok.Data;
    
    @Data
    public class Address {
        /**
         * First line of street information (required).
         */
        private String street1;
        /**
         * Second line of street information (optional).
         */
        private String street2;
        private String city;
        /**
         * State abbreviation (required for the U.S. and Canada, optional
         * otherwise).
         */
        private String state;
        /**
         * Postal code(required for the U.S.and Canada,optional otherwise).
         */
        private String postCode;
        /**
         * Country name (optional, U.S. assumed if not supplied).
         */
        private String country;
    }
    import lombok.Data;
    
    import java.util.List;
    
    @Data
    public class Customer {
        private long customerNumber;
        /**
         * Personal name.
         */
        private String firstName;
        /**
         * Family name.
         */
        private String lastName;
        /**
         * Middle name(s), if any.
         */
        private List middleNames;
    }
    import lombok.Data;
    
    @Data
    public class Order {
      private long orderNumber;
      private Customer customer;
      private Address billTo;
      private Shipping shipping;
      private Address shipTo;
      private Float total;
    }
    public enum Shipping {
        STANDARD_MAIL, PRIORITY_MAIL, INTERNATIONAL_MAIL, DOMESTIC_EXPRESS, INTERNATIONAL_EXPRESS
    }
    import org.jibx.runtime.*;
    
    import java.io.IOException;
    import java.io.StringReader;
    import java.io.StringWriter;
    import java.util.Arrays;
    
    public class TestOrder {
        private IBindingFactory factory = null;
        private StringWriter writer = null;
        private StringReader reader = null;
        private final static String CHARSET_NAME = "UTF-8";
    
        private String encode2Xml(Order order) throws JiBXException, IOException {
            factory = BindingDirectory.getFactory(Order.class);
            writer = new StringWriter();
            IMarshallingContext mctx = factory.createMarshallingContext();
            mctx.setIndent(2);
            mctx.marshalDocument(order, CHARSET_NAME, null, writer);
            String xmlStr = writer.toString();
            writer.close();
            System.out.println(xmlStr.toString());
            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 = new Order();
            order.setOrderNumber(123);
            Customer customer = new Customer();
            customer.setFirstName("ali");
            customer.setMiddleNames(Arrays.asList("baba"));
            customer.setLastName("taobao");
            order.setCustomer(customer);
            Address address = new Address();
            address.setCity("南京市");
            address.setCountry("中国");
            address.setPostCode("123321");
            address.setState("江苏省");
            address.setStreet1("龙眠大道");
            address.setStreet2("INTERNATIONAL_MAIL");
            order.setBillTo(address);
            order.setShipTo(address);
            order.setShipping(Shipping.INTERNATIONAL_MAIL);
            order.setTotal(33f);
            String body = test.encode2Xml(order);
            Order order2 = test.decode2Order(body);
            System.out.println(order2);
        }
    }
            <dependency>
                <groupId>org.jibx</groupId>
                <artifactId>jibx-bind</artifactId>
                <version>1.3.0</version>
            </dependency>

    基础封装类示例

    import io.netty.handler.codec.http.FullHttpRequest;
    import lombok.Data;
    
    @Data
    public class HttpXmlRequest {
        //它包含两个成员变量FullHttpRequest和编码对象Object,用于实现和协议栈之间的解耦。
        private FullHttpRequest request;
        private Object body;
    
        public HttpXmlRequest(FullHttpRequest request, Object body) {
            this.body = body;
            this.request = request;
        }
    }
    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.util.CharsetUtil;
    
    import java.util.List;
    
    import static io.netty.handler.codec.http.HttpResponseStatus.*;
    import static io.netty.handler.codec.http.HttpHeaders.Names.*;
    import static io.netty.handler.codec.http.HttpVersion.*;
    
    public class HttpXmlRequestDecoder extends AbstractHttpXmlDecoder {
    
        public HttpXmlRequestDecoder(Class clazz) {
            this(clazz, false);
        }
        //HttpXmlRequestDecoder有两个参数,分别为需要解码的对象的类型信息和是否打印HTTP消息体码流的码流开关,码流开关默认关闭。
        public HttpXmlRequestDecoder(Class clazz, boolean isPrint) {
            super(clazz, isPrint);
        }
    
        @Override
        protected void decode(ChannelHandlerContext arg0, Object o, List arg2) throws Exception {
            FullHttpRequest arg1 = (FullHttpRequest)o;
            //首先对HTTP请求消息本身的解码结果进行判断,如果已经解码失败,再对消息体进行二次解码已经没有意义。
            if (!arg1.getDecoderResult().isSuccess()) {
                //如果HTTP消息本身解码失败,则构造处理结果异常的HTTP应答消息返回给客户端。
                sendError(arg0, BAD_REQUEST);
                return;
            }
            //通过HttpXmlRequest和反序列化后的Order对象构造HttpXmlRequest实例,最后将它添加到解码结果List列表中。
            HttpXmlRequest request = new HttpXmlRequest(arg1, decode0(arg0,arg1.content()));
            arg2.add(request);
        }
    
        private static void sendError(ChannelHandlerContext ctx,
                                      HttpResponseStatus status) {
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
                    status, Unpooled.copiedBuffer("Failure: " + status.toString()
                    + "
    ", CharsetUtil.UTF_8));
            response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.*;
    
    import java.net.InetAddress;
    import java.util.List;
    
    public class HttpXmlRequestEncoder extends AbstractHttpXmlEncoder {
    
        @Override
        protected void encode(ChannelHandlerContext ctx, Object o,List out) throws Exception {
            HttpXmlRequest msg = (HttpXmlRequest)o;
            //首先调用父类的encode0,将业务需要发送的POJO对象Order实例通过JiBx序列化为XML字符串
            //随后将它封装成Netty的ByteBuf。
            ByteBuf body = encode0(ctx, msg.getBody());
            FullHttpRequest request = msg.getRequest();
            //对消息头进行判断,如果业务自定义和定制了消息头,则使用业务侧设置的HTTP消息头,
            //如果业务侧没有设置,则构造新的HTTP消息头。
            if (request == null) {
                //用来构造和设置默认的HTTP消息头,由于通常情况下HTTP通信双方更关注消息体本身,所以这里采用了硬编码的方式,
                //如果要产品化,可以做成XML配置文件,允许业务自定义配置,以提升定制的灵活性。
                request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,HttpMethod.GET, "/do", body);
                HttpHeaders headers = request.headers();
                headers.set(HttpHeaders.Names.HOST, InetAddress.getLocalHost().getHostAddress());
                headers.set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
                headers.set(HttpHeaders.Names.ACCEPT_ENCODING,
                        HttpHeaders.Values.GZIP.toString() + ','
                                + HttpHeaders.Values.DEFLATE.toString());
                headers.set(HttpHeaders.Names.ACCEPT_CHARSET,"ISO-8859-1,utf-8;q=0.7,*;q=0.7");
                headers.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "zh");
                headers.set(HttpHeaders.Names.USER_AGENT,"Netty xml Http Client side");
                headers.set(HttpHeaders.Names.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            }
            //由于请求消息消息体不为空,也没有使用Chunk方式,所以在HTTP消息头中设置消息体的长度Content-Length,
            //完成消息体的XML序列化后将重新构造的HTTP请求消息加入到out中,
            //由后续Netty的HTTP请求编码器继续对HTTP请求消息进行编码。
            HttpHeaders.setContentLength(request, body.readableBytes());
            out.add(request);
        }
    }
    import io.netty.handler.codec.http.FullHttpResponse;
    
    //它包含两个成员变量:FullHttpResponse和Object,Object就是业务需要发送的应答POJO对象。
    public class HttpXmlResponse {
        private FullHttpResponse httpResponse;
        private Object result;
        public HttpXmlResponse(FullHttpResponse httpResponse, Object result) {
            this.httpResponse = httpResponse;
            this.result = result;
        }
        public final FullHttpResponse getHttpResponse() {
            return httpResponse;
        }
        public final void setHttpResponse(FullHttpResponse httpResponse) {
            this.httpResponse = httpResponse;
        }
        public final Object getResult() {
            return result;
        }
        public final void setResult(Object result) {
            this.result = result;
        }
    }
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    
    import java.util.List;
    
    public class HttpXmlResponseDecoder extends AbstractHttpXmlDecoder {
    
        public HttpXmlResponseDecoder(Class clazz) {
            this(clazz, false);
        }
        public HttpXmlResponseDecoder(Class clazz, boolean isPrintlog) {
            super(clazz, isPrintlog);
        }
    
        @Override
        protected void decode(ChannelHandlerContext ctx,Object o, List out) throws Exception {
            DefaultFullHttpResponse msg = (DefaultFullHttpResponse)o;
            HttpXmlResponse resHttpXmlResponse = new HttpXmlResponse(msg, decode0(
                    ctx, msg.content()));
            out.add(resHttpXmlResponse);
        }
    }
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.FullHttpResponse;
    
    import java.util.List;
    
    import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
    import static io.netty.handler.codec.http.HttpHeaders.setContentLength;
    import static io.netty.handler.codec.http.HttpResponseStatus.OK;
    import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
    
    public class HttpXmlResponseEncoder extends AbstractHttpXmlEncoder {
    
        protected void encode(ChannelHandlerContext ctx, Object o, List out) throws Exception {
            HttpXmlResponse msg = (HttpXmlResponse) o;
            ByteBuf body = encode0(ctx, msg.getResult());
            FullHttpResponse response = msg.getHttpResponse();
            if (response == null) {
                response = new DefaultFullHttpResponse(HTTP_1_1, OK, body);
            } else {
                response = new DefaultFullHttpResponse(msg.getHttpResponse()
                        .getProtocolVersion(), msg.getHttpResponse().getStatus(),
                        body);
            }
            response.headers().set(CONTENT_TYPE, "text/xml");
            setContentLength(response, body.readableBytes());
            out.add(response);
        }
    }
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.MessageToMessageDecoder;
    import org.jibx.runtime.BindingDirectory;
    import org.jibx.runtime.IBindingFactory;
    import org.jibx.runtime.IUnmarshallingContext;
    
    import java.io.StringReader;
    import java.nio.charset.Charset;
    
    public abstract class AbstractHttpXmlDecoder extends MessageToMessageDecoder {
        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 {
            //从HTTP的消息体中获取请求码流,然后通过JiBx类库将XML转换成POJO对象。
            factory = BindingDirectory.getFactory(clazz);
            String content = body.toString(UTF_8);
            //根据码流开关决定是否打印消息体码流。
            //增加码流开关往往是为了方便问题定位,在实际项目中,需要打印到日志中。
            if (isPrint) {
                System.out.println("The body is : " + content);
            }
            reader = new StringReader(content);
            IUnmarshallingContext uctx = factory.createUnmarshallingContext();
            Object result = uctx.unmarshalDocument(reader);
            reader.close();
            reader = null;
            return result;
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                throws Exception {
            // 释放资源
            //如果解码发生异常,要判断StringReader是否已经关闭,
            //如果没有关闭,则关闭输入流并通知JVM对其进行垃圾回收。
            if (reader != null) {
                reader.close();
                reader = null;
            }
        }
    }
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.MessageToMessageEncoder;
    import org.jibx.runtime.BindingDirectory;
    import org.jibx.runtime.IBindingFactory;
    import org.jibx.runtime.IMarshallingContext;
    
    import java.io.StringWriter;
    import java.nio.charset.Charset;
    
    public abstract class AbstractHttpXmlEncoder extends MessageToMessageEncoder {
        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 {
            //在此将业务的Order实例序列化为XML字符串。
            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;
            //将XML字符串包装成Netty的ByteBuf并返回,实现了HTTP请求消息的XML编码。
            ByteBuf encodeBuf = Unpooled.copiedBuffer(xmlStr, UTF_8);
            return encodeBuf;
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                throws Exception {
            // 释放资源
            if (writer != null) {
                writer.close();
                writer = null;
            }
        }
    }

    服务端代码示例

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    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.HttpResponseEncoder;
    
    import java.net.InetSocketAddress;
    
    public class HttpXmlServer {
        public void run(final int port) throws Exception {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new ChannelInitializer() {
                            @Override
                            protected void initChannel(Channel 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);
                future.channel().closeFuture().sync();
            } finally {
                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 HttpXmlServer().run(port);
        }
    }
    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.FullHttpResponse;
    import io.netty.handler.codec.http.HttpRequest;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.util.CharsetUtil;
    import io.netty.util.concurrent.Future;
    import io.netty.util.concurrent.GenericFutureListener;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
    import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
    import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
    import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
    
    public class HttpXmlServerHandler extends SimpleChannelInboundHandler {
    
        @Override
        public void messageReceived(final ChannelHandlerContext ctx,Object o) throws Exception {
            HttpXmlRequest xmlRequest = (HttpXmlRequest)o;
            HttpRequest 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 (!isKeepAlive(request)) {
                future.addListener(new GenericFutureListener() {
                    public void operationComplete (Future future)throws Exception {
                        ctx.close();
                    }
                });
            }
        }
    
        private void dobusiness(Order order) {
            order.getCustomer().setFirstName("狄");
            order.getCustomer().setLastName("仁杰");
            List midNames = new ArrayList();
            midNames.add("李元芳");
            order.getCustomer().setMiddleNames(midNames);
            Address address = order.getBillTo();
            address.setCity("洛阳");
            address.setCountry("大唐");
            address.setState("河南道");
            address.setPostCode("123456");
            order.setBillTo(address);
            order.setShipTo(address);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {
            cause.printStackTrace();
            if (ctx.channel().isActive()) {
                sendError(ctx, INTERNAL_SERVER_ERROR);
            }
        }
    
        private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
                    status, Unpooled.copiedBuffer("失败: " + status.toString()
                    + "
    ", CharsetUtil.UTF_8));
            response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }

    客户端代码示例

    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpRequestEncoder;
    import io.netty.handler.codec.http.HttpResponseDecoder;
    
    import java.net.InetSocketAddress;
    
    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() {
                            @Override
                            public void initChannel(Channel ch)
                                    throws Exception {
                                ch.pipeline().addLast("http-decoder",new HttpResponseDecoder());
                                ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));
                                // XML解码器
                                ch.pipeline().addLast("xml-decoder",new HttpXmlResponseDecoder(Order.class,true));
                                ch.pipeline().addLast("http-encoder",new HttpRequestEncoder());
                                ch.pipeline().addLast("xml-encoder",new HttpXmlRequestEncoder());
                                ch.pipeline().addLast("xmlClientHandler",new HttpXmlClientHandle());
                            }
                        });
    
                // 发起异步连接操作
                ChannelFuture f = b.connect(new InetSocketAddress(port)).sync();
    
                // 等待客户端链路关闭
                f.channel().closeFuture().sync();
            } finally {
                // 优雅退出,释放NIO线程组
                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 (NumberFormatException e) {
                    // 采用默认值
                }
            }
            new HttpXmlClient().connect(port);
        }
    }
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    public class HttpXmlClientHandle extends SimpleChannelInboundHandler {
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            HttpXmlRequest request = new HttpXmlRequest(null,OrderFactory.create(123));
            ctx.writeAndFlush(request);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    
        @Override
        protected void messageReceived(ChannelHandlerContext ctx,Object o) throws Exception {
            HttpXmlResponse msg = (HttpXmlResponse)o;
            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());
        }
    }
    public class OrderFactory {
        public static Order create(long orderID) {
            Order order = new Order();
            order.setOrderNumber(orderID);
            order.setTotal(9999.999f);
            Address address = new Address();
            address.setCity("南京市");
            address.setCountry("中国");
            address.setPostCode("123321");
            address.setState("江苏省");
            address.setStreet1("龙眠大道");
            order.setBillTo(address);
            Customer customer = new Customer();
            customer.setCustomerNumber(orderID);
            customer.setFirstName("李");
            customer.setLastName("林峰");
            order.setCustomer(customer);
            order.setShipping(Shipping.INTERNATIONAL_MAIL);
            order.setShipTo(address);
            return order;
        }
    }

    测试结果:

    服务端请求消息码流输出:

    服务端解码后的业务对象输出

    Http server receive request : Order [orderNumber=123, customer=Customer [customerNumber=123, firstName=李, lastName=林峰, middleNames=null], billTo= Address [street1=龙眠大道, street2=null, city=南京市, state=江苏省, postCode= 123321, country=中国], shipping=INTERNATIONAL_MAIL, shipTo=Address [street1=龙眠大道, street2=null, city=南京市, state=江苏省, postCode=123321, country=中国], total=9999.999]

    客户端响应消息码流输出:

    客户端解码后的业务对象输出

    The client receive response of http body is : Order [orderNumber=123, customer=Customer [customerNumber=123, firstName=狄, lastName=仁杰, middleNames=[李元芳]], billTo=Address [street1=龙眠大道, street2=null, city=洛阳, state=河南道, postCode=123456, country=大唐], shipping=INTERNATIONAL_MAIL, shipTo=Address [street1=龙眠大道, street2=null, city=洛阳, state=河南道, postCode=123456, country=大唐], total=9999.999] 

  • 相关阅读:
    2020年面向对象程序设计寒假作业1_实践题
    2020年面向对象程序设计寒假作业1_问答题
    实验5:开源控制器实践——POX
    实验4:开源控制器实践——OpenDaylight
    实验3:OpenFlow协议分析实践
    实验2:Open vSwitch虚拟交换机实践
    第一次个人编程作业
    实验1:SDN拓扑实践
    第一次博客作业
    面向对象程序设计寒假作业3
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/6178619.html
Copyright © 2011-2022 走看看