对《Netty权威指南》中的Http+xml协议栈开发的个人再整理

作为一个Netty的菜鸟,最近在学习李林峰先生的《Netty权威指南》,学习了Http+xml协议栈的开发,获益匪浅。本文代码大部分来自李林峰先生的《Netty权威指南》,有些代码经过读者修改。此处仅作为学习交流使用,切勿用作商业用途。文章如有纰漏,欢迎指正交流


开发场景:

客户端发送一个Order对象请求给服务器,服务器接收到之后返回给客户端一个Order应答对象。javabean在的数据以xm'l承载。

各个javabean如下:

package com.phei.netty.protocol.http.xml.pojo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

@XStreamAlias("order")
public class Order {
	@XStreamAsAttribute
	private long orderNumber;

	private Customer customer;

	private Address billTo;

	private Shipping shipping;

	private Address shipTo;
	@XStreamAsAttribute
	private Float total;

	public long getOrderNumber() {
		return orderNumber;
	}

	public void setOrderNumber(long orderId) {
		this.orderNumber = orderId;
	}

	public Customer getCustomer() {
		return customer;
	}

	public void setCustomer(Customer customer) {
		this.customer = customer;
	}

	public Address getBillTo() {
		return billTo;
	}

	public void setBillTo(Address billTo) {
		this.billTo = billTo;
	}

	public Shipping getShipping() {
		return shipping;
	}

	public void setShipping(Shipping shipping) {
		this.shipping = shipping;
	}

	public Address getShipTo() {
		return shipTo;
	}

	public void setShipTo(Address shipTo) {
		this.shipTo = shipTo;
	}

	public Float getTotal() {
		return total;
	}

	public void setTotal(Float total) {
		this.total = total;
	}

	@Override
	public String toString() {
		return "Order [orderNumber=" + orderNumber + ", customer=" + customer + ", billTo=" + billTo + ", shipping="
				+ shipping.toString() + ", shipTo=" + shipTo + ", total=" + total + "]";
	}

}

package com.phei.netty.protocol.http.xml.pojo;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("billTo")
public class Address {
	private String street1;

	private String street2;

	private String city;

	private String state;

	private String postCode;

	private String country;

	public String getStreet1() {
		return street1;
	}

	public void setStreet1(String street1) {
		this.street1 = street1;
	}

	public String getStreet2() {
		return street2;
	}

	public void setStreet2(String street2) {
		this.street2 = street2;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getState() {
		return state;
	}

	public void setState(String state) {
		this.state = state;
	}

	public String getPostCode() {
		return postCode;
	}

	public void setPostCode(String postCode) {
		this.postCode = postCode;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	@Override
	public String toString() {
		return "Address [street1=" + street1 + ", street2=" + street2 + ", city=" + city + ", state=" + state
				+ ", postCode=" + postCode + ", country=" + country + "]";
	}
}

package com.phei.netty.protocol.http.xml.pojo;

import java.util.List;

import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

public class Customer {
	@XStreamAsAttribute
	private long customerNumber;

	private String firstName;

	private String lastName;

	private List<String> middleNames;

	public long getCustomerNumber() {
		return customerNumber;
	}

	public void setCustomerNumber(long customerId) {
		this.customerNumber = customerId;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public List<String> getMiddleNames() {
		return middleNames;
	}

	public void setMiddleNames(List<String> middleNames) {
		this.middleNames = middleNames;
	}

	@Override
	public String toString() {
		return "Customer [customerNumber=" + customerNumber + ", firstName=" + firstName + ", lastName=" + lastName
				+ ", middleNames=" + middleNames + "]";
	}

}

package com.phei.netty.protocol.http.xml.pojo;

public enum Shipping {
	STANDARD_MAIL, PRIORITY_MAIL, INTERNATIONAL_MAIL, DOMESTIC_EXPRESS, INTERNATIONAL_EXPRESS
}


本文按照事件发生的先后顺序展示代码,

一、启动客户端:

package com.phei.netty.protocol.http.xml.client;

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.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;

import java.net.InetSocketAddress;

import com.phei.netty.protocol.http.xml.codec.HttpXmlRequestEncoder;
import com.phei.netty.protocol.http.xml.codec.HttpXmlResponseDecoder;
import com.phei.netty.protocol.http.xml.pojo.Order;

public class HttpXmlClient {

	public void connect(int port) throws Exception {
		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
						public void initChannel(SocketChannel ch) throws Exception {
							// xml解码器
							ch.pipeline().addLast("http-decoder", new HttpResponseDecoder());
							ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
							ch.pipeline().addLast("xml-decoder", new HttpXmlResponseDecoder(Order.class, true));
							ch.pipeline().addLast("http-encoder", new HttpRequestEncoder());
							// xml编码器
							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 {
			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);
	}
}

ChannelPipeLine中添加了xml解码器,xml编码器与用户自定义的HttpXmlClientHandle,HttpXmlClientHandle代码在下方


二、客户端与服务端连接成功后发送请求消息:

package com.phei.netty.protocol.http.xml.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import com.phei.netty.protocol.http.xml.codec.HttpXmlRequest;
import com.phei.netty.protocol.http.xml.codec.HttpXmlResponse;
import com.phei.netty.protocol.http.xml.pojo.OrderFactory;

public class HttpXmlClientHandle extends SimpleChannelInboundHandler<HttpXmlResponse> {

	@Override
	public void channelActive(ChannelHandlerContext ctx) {
		// 给客户端发送请求消息,HttpXmlRequest包含FullHttpRequest和Order这个了类
		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, 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());
	}
}

客户端成功联接服务器之后,激活HttpXmlClientHandle中的channelActive方法,客户端构造HttpXmlRequest对象并写入到ChannelPipeLine中,其中HttpXmlRequest包括FullHttpRequest对象与Object对象。

HttpXmlRequest的代码如下:

package com.phei.netty.protocol.http.xml.codec;

import io.netty.handler.codec.http.FullHttpRequest;

public class HttpXmlRequest {

	private FullHttpRequest request;
	private Object 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;
	}

	public final Object getBody() {
		return body;
	}

	public final void setBody(Object body) {
		this.body = body;
	}

	@Override
	public String toString() {
		return "HttpXmlRequest [request=" + request + ", body =" + body + "]";
	}
}


HttpXmlClientHandle中使用了OrderFactory来获得一个Order对象,OrderFactory的代码如下:

package com.phei.netty.protocol.http.xml.pojo;

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;
	}
}



三、消息传到HttpXmlRequestEncoder,并进行编码,代码如下:

package com.phei.netty.protocol.http.xml.codec;

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.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import java.net.InetAddress;
import java.util.List;

public class HttpXmlRequestEncoder extends AbstractHttpXmlEncoder<HttpXmlRequest> {

	@Override
	protected void encode(ChannelHandlerContext ctx, HttpXmlRequest msg, List<Object> out) throws Exception {
		// 调用父类的encode0方法将Order对象转换为xml字符串,并将其封装为ByteBuf
		ByteBuf body = encode0(ctx, msg.getBody());
		FullHttpRequest request = msg.getRequest();
		// 如request为空,则新建一个FullHttpRequest对象,并将设置消息头
		if (request == null) {
			// 在构造方法中,将body设置为请求消息体
			request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/do", body);
			HttpHeaders headers = request.headers();
			// 表示请求的服务器网址
			headers.set(HttpHeaderNames.HOST, InetAddress.getLocalHost().getHostAddress());
			// Connection表示客户端与服务连接类型;Keep-Alive表示长连接;CLOSE表示短连接
			// header中包含了值为close的connection,都表明当前正在使用的tcp链接在请求处理完毕后会被断掉。
			// 以后client再进行新的请求时就必须创建新的tcp链接了。
			headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
			// 浏览器支持的压缩编码是 gzip 和 deflate
			headers.set(HttpHeaderNames.ACCEPT_ENCODING,
					HttpHeaderValues.GZIP.toString() + ',' + HttpHeaderValues.DEFLATE.toString());
			// 浏览器支持的解码集
			headers.set(HttpHeaderNames.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
			// 浏览器支持的语言
			headers.set(HttpHeaderNames.ACCEPT_LANGUAGE, "zh");
			// 使用的用户代理是 Netty xml Http Client side
			headers.set(HttpHeaderNames.USER_AGENT, "Netty xml Http Client side");
			// 浏览器支持的 MIME类型,优先顺序为从左到右
			headers.set(HttpHeaderNames.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
		}
		// 由于此处没有使用chunk方式,所以要设置消息头中设置消息体的CONTENT_LENGTH
		request.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, body.readableBytes());
		// 将请求消息添加进out中,待后面的编码器对消息进行编码
		out.add(request);
	}

}
encode方法中接收到的的msg就是之前HttpXmlClientHandle中我们写入ChannelPipeLine中的对象。HttpXmlRequestEncoder获得的msg中的Object对象,通过父类的encode0方法将其转换为Bytebuf对象。

因为这个例子中上述request必定为null,所以会构造新的FullHttpRequest对象。在构造方法中将数据body传入FullHttpRequest对象中。最后要记得设置CONTENT_LENGTH请求头,并将request对象添加到out对象中。


HttpXmlRequestEncoder继承了AbstractHttpXmlEncoder,AbstractHttpXmlEncoder中定义了encode0方法,将Object对象转化为对应的xml字符串,然后将返回xml字符串的ByteBuf对象,此处没有使用《Netty权威指南》原书中的jibx实现JavaBean和xml的互相转换,而是使用了XStream。方法也很简单,可以参考http://blog.csdn.net/kingsonyoung/article/details/50524866

AbstractHttpXmlEncoder代码如下:

package com.phei.netty.protocol.http.xml.codec;

import java.nio.charset.Charset;

import com.phei.netty.protocol.http.xml.pojo.Address;
import com.phei.netty.protocol.http.xml.pojo.Customer;
import com.phei.netty.protocol.http.xml.pojo.Order;
import com.phei.netty.protocol.http.xml.pojo.Shipping;
import com.thoughtworks.xstream.XStream;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;

public abstract class AbstractHttpXmlEncoder<T> extends MessageToMessageEncoder<T> {
	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流
		XStream xStream = new XStream();
		xStream.setMode(XStream.NO_REFERENCES);
		// 注册使用了注解的VO
		xStream.processAnnotations(new Class[] { Order.class, Customer.class, Shipping.class, Address.class });
		String xml = xStream.toXML(body);
		ByteBuf encodeBuf = Unpooled.copiedBuffer(xml, UTF_8);
		return encodeBuf;
	}

	@Skip
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.out.println("fail to encode");
	}

}


四、在HttpXmlRequestEncoder中写入的FullHttpRequest对象经由客户端的HttpRequestEncoder编码器处理,发送至服务端。


五、服务端代码:

服务端同样有xml编码器和xml解码器,还有HttpXmlServerHandler。

package com.phei.netty.protocol.http.xml.server;

import io.netty.bootstrap.ServerBootstrap;
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.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.HttpResponseEncoder;
import java.net.InetSocketAddress;
import com.phei.netty.protocol.http.xml.codec.HttpXmlRequestDecoder;
import com.phei.netty.protocol.http.xml.codec.HttpXmlResponseEncoder;
import com.phei.netty.protocol.http.xml.pojo.Order;

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<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);
			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);
	}
}

六、服务端的HttpRequestDecoder接收到来自客户端的消息,再交由HttpObjectAggregator处理


七、第六步处理完之后获得一个FullHttpRequest对象,并传递到HttpXmlRequestDecoder解码器中

HttpXmlRequestDecoder代码如下:

package com.phei.netty.protocol.http.xml.codec;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
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;

public class HttpXmlRequestDecoder extends AbstractHttpXmlDecoder<FullHttpRequest> {

	public HttpXmlRequestDecoder(Class<?> clazz) {
		this(clazz, false);
	}

	public HttpXmlRequestDecoder(Class<?> clazz, boolean isPrint) {
		super(clazz, isPrint);
	}

	@Override
	protected void decode(ChannelHandlerContext arg0, FullHttpRequest arg1, List<Object> arg2) throws Exception {
		// 返回客户端错误信息
		if (!arg1.decoderResult().isSuccess()) {
			sendError(arg0, BAD_REQUEST);
			return;
		}
		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() + "\r\n", CharsetUtil.UTF_8));
		response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
		ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
	}
}
HttpXmlRequestDecoder继承了AbstractHttpXmlDecoder,decode方法中调用服父类的decode0方法将xml字符串转换成Object对象。然后构造了一个HttpXmlRequest对象。


AbstractHttpXmlDecoder代码如下:

package com.phei.netty.protocol.http.xml.codec;

import java.nio.charset.Charset;

import com.phei.netty.protocol.http.xml.pojo.Address;
import com.phei.netty.protocol.http.xml.pojo.Customer;
import com.phei.netty.protocol.http.xml.pojo.Order;
import com.phei.netty.protocol.http.xml.pojo.Shipping;
import com.thoughtworks.xstream.XStream;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

public abstract class AbstractHttpXmlDecoder<T> extends MessageToMessageDecoder<T> {

	private Class<?> clazz;
	// 是否输出码流的标志,默认为false
	private boolean isPrint;
	private final static String CHARSET_NAME = "UTF-8";
	private final static Charset UTF_8 = Charset.forName(CHARSET_NAME);

	// 当调用这个构造方法是,默认设置isPrint为false
	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 {
		String content = body.toString(UTF_8);
		if (isPrint)
			System.out.println("The body is : " + content);
		XStream xs = new XStream();
		xs.setMode(XStream.NO_REFERENCES);
		// 注册使用了注解的VO
		xs.processAnnotations(new Class[] { Order.class, Customer.class, Shipping.class, Address.class });
		Object result = xs.fromXML(content);
		return result;
	}

	@Skip
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
	}
}


八、服务器的HttpXmlServerHandler接收到HttpXmlRequestDecoder传来的HttpXmlRequest对象:

HttpXmlServerHandler代码如下

package com.phei.netty.protocol.http.xml.server;

import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import java.util.ArrayList;
import java.util.List;
import com.phei.netty.protocol.http.xml.codec.HttpXmlRequest;
import com.phei.netty.protocol.http.xml.codec.HttpXmlResponse;
import com.phei.netty.protocol.http.xml.pojo.Address;
import com.phei.netty.protocol.http.xml.pojo.Order;
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;

public class HttpXmlServerHandler extends SimpleChannelInboundHandler<HttpXmlRequest> {

	@Override
	public void messageReceived(final ChannelHandlerContext ctx, HttpXmlRequest xmlRequest) throws Exception {
		HttpRequest request = xmlRequest.getRequest();
		Order order = (Order) xmlRequest.getBody();
		// 输出解码获得的Order对象
		System.out.println("Http server receive request : " + order);
		dobusiness(order);
		System.out.println(order);
		ChannelFuture future = ctx.writeAndFlush(new HttpXmlResponse(null, order));
		if (request.headers().get(CONNECTION) != KEEP_ALIVE) {
			future.addListener(new GenericFutureListener<Future<? super Void>>() {
				public void operationComplete(Future future) throws Exception {
					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.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() + "\r\n", CharsetUtil.UTF_8));
		response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
		ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
	}
}

dobusiness(order)方法获得应答的javabean,然后写入到ChannelPipeLine


九、javabean传入到HttpXmlResponseEncoder中,进行应答解码

package com.phei.netty.protocol.http.xml.codec;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
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;

public class HttpXmlResponseEncoder extends AbstractHttpXmlEncoder<HttpXmlResponse> {

	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 {
			response = new DefaultFullHttpResponse(msg.getHttpResponse().protocolVersion(),
					msg.getHttpResponse().status(), body);
		}
		response.headers().set(CONTENT_TYPE, "text/xml");
		response.headers().setInt(CONTENT_LENGTH, body.readableBytes());
		out.add(response);
	}
}


十、HttpXmlResponseEncoder处理完之后获得一个FullHttpResponse对象,该对象传入到HttpXmlResponseEncoder中进行编码


十一、服务器的应答消息发至客户端的HttpResponseDecoder处理后


十二、HttpXmlResponseEncoder处理FullHttpResponse对象

package com.phei.netty.protocol.http.xml.codec;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
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;

public class HttpXmlResponseEncoder extends AbstractHttpXmlEncoder<HttpXmlResponse> {

	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 {
			response = new DefaultFullHttpResponse(msg.getHttpResponse().protocolVersion(),
					msg.getHttpResponse().status(), body);
		}
		response.headers().set(CONTENT_TYPE, "text/xml");
		response.headers().setInt(CONTENT_LENGTH, body.readableBytes());
		out.add(response);
	}
}

服务器的输出结果为:

HTTP订购服务器启动,网址是 :  http://localhost:8080
The body is : <order orderNumber="123" total="9999.999">
  <customer customerNumber="123">
    <firstName>李</firstName>
    <lastName>林峰</lastName>
  </customer>
  <billTo>
    <street1>龙眠大道</street1>
    <city>南京市</city>
    <state>江苏省</state>
    <postCode>123321</postCode>
    <country>中国</country>
  </billTo>
  <shipping>INTERNATIONAL_MAIL</shipping>
  <shipTo>
    <street1>龙眠大道</street1>
    <city>南京市</city>
    <state>江苏省</state>
    <postCode>123321</postCode>
    <country>中国</country>
  </shipTo>
</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 body is : <order orderNumber="123" total="9999.999">
  <customer customerNumber="123">
    <firstName>狄</firstName>
    <lastName>仁杰</lastName>
    <middleNames>
      <string>李元芳</string>
    </middleNames>
  </customer>
  <billTo>
    <street1>龙眠大道</street1>
    <city>洛阳</city>
    <state>河南道</state>
    <postCode>123456</postCode>
    <country>大唐</country>
  </billTo>
  <shipping>INTERNATIONAL_MAIL</shipping>
  <shipTo>
    <street1>龙眠大道</street1>
    <city>洛阳</city>
    <state>河南道</state>
    <postCode>123456</postCode>
    <country>大唐</country>
  </shipTo>
</order>
The client receive response of http header is : [content-length, content-type]
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]




评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值