作为一个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 + "]";
}
}
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");
}
}
五、服务端代码:
服务端同样有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]