對《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]





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com