Netty HTTP协议简单实现

HTTP(超文本传输协议)协议是建立在TCP传输协议之上的应用层协议,它的发展是万维网协会和Internet工作小组IETF合作的结果。HTTP是一个属于应用层的面向对象的协议,由于其简洁、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过多年的使用和发展,得到了不断的完善和扩展。本文将介绍如何基于Netty 的HTTP协议栈进行HTTP服务端和客户端的开发。由于Netty的HTTP协议栈是基于Netty的NIO通信框架开发的,因此,Netty的HTTP协议也是异步非阻塞的。

HTTP服务端开发

HttpFileServer.java

package emulator.http;

import emulator.Constants;
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 io.netty.handler.stream.ChunkedWriteHandler;

/**
 * 模拟服务端
 * @author lh
 * @date 2015-08-11 14:33
 * @version 1.0
 * 
 */
public class HttpFileServer {

	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());
							// 聚合器,把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
							ch.pipeline().addLast("http-aggregator",
									new HttpObjectAggregator(65536));
							// 服务端,对响应编码
							ch.pipeline().addLast("http-encoder",
									new HttpResponseEncoder());
							// 块写入处理器
							ch.pipeline().addLast("http-chunked",
									new ChunkedWriteHandler());
							// 自定义服务端处理器
							ch.pipeline().addLast("fileServerHandler",
									new HttpFileServerHandler());
						}
					});
			ChannelFuture future = b.bind(Constants.DEFAULT_IP, port).sync();
			System.out.println("HTTP文件目录服务器启动,网址是 : " + "http://" + Constants.DEFAULT_IP	+ ":" + port);
			future.channel().closeFuture().sync();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception {
		new HttpFileServer().run(Constants.DEFAULT_PORT);
	}
}

HttpFileServerHandler.java

package emulator.http;

import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;

import emulator.Constants;
import emulator.util.Dom4JUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.CharsetUtil;

/**
 * 模拟服务端处理器
 * @author lh
 * @date 2015-08-11 14:33
 * @version 1.0
 * 
 */
public class HttpFileServerHandler extends
		SimpleChannelInboundHandler<FullHttpRequest> {

	@Override
	public void messageReceived(ChannelHandlerContext ctx,
			FullHttpRequest request) throws Exception {
		
		if (!request.getDecoderResult().isSuccess()) {
			sendError(ctx, BAD_REQUEST);
			return;
		}
		
		ByteBuf buf =  request.content();
		byte [] req = new byte[buf.readableBytes()];  
        buf.readBytes(req);  
        String xml = new String(req,"UTF-8");  
        resp(ctx,xml);
	}
	
	/**
	 * 
	 * @param xml
	 */
	private void resp(ChannelHandlerContext ctx, String xml){
		String transCode = Dom4JUtil.header(xml, "transcode");
		String retUrl = "D:\\workspaces\\eclipse-huifu\\emulator\\xml\\error.xml";
		String retCtt = null;
		if(equal(transCode, Constants.TC_DZZH)){//电子账户
			
			retUrl = "D:\\workspaces\\eclipse-huifu\\emulator\\xml\\account\\manage\\resp.xml";
		
		}else if(equal(transCode, Constants.TC_YHKBD)){//银行卡绑定
			
		}
		
		try {
			retCtt = FileUtils.readFileToString(new File(retUrl));
		} catch (IOException e) {
			e.printStackTrace();
		}
		FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
				HttpResponseStatus.FOUND, Unpooled.copiedBuffer(retCtt, CharsetUtil.UTF_8));
		response.headers().set(CONTENT_TYPE, "text/xml; charset=UTF-8");
		ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
		
	}
	
	
	public static boolean equal(String var, String cons){
		return isNotEmpty(var) && cons.equals(var);		
	}
	
	private static boolean isNotEmpty(String s){
		return (null != s && !"".equals(s));
	}

	/**
	 * 错误处理
	 * @param ctx
	 * @param status
	 */
	private static void sendError(ChannelHandlerContext ctx,
			HttpResponseStatus status) {
		String ret =  null;
		FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
				status, Unpooled.copiedBuffer(ret, CharsetUtil.UTF_8));
		response.headers().set(CONTENT_TYPE, "text/xml; charset=UTF-8");
		ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
	}


	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		ctx.close();
		cause.printStackTrace();
	}

}

HTTP客户端开发

HttpFileClient.java

package emulator.http;

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 io.netty.handler.stream.ChunkedWriteHandler;

import java.net.InetSocketAddress;

import emulator.Constants;

/**
 * 模拟客户端
 * 
 * @author lh
 * @date 2015-08-11 14:31
 * @version 1.0
 * 
 */
public class HttpFileClient {

	public void connect(int port) throws Exception {
		// 配置客户端NIO线程组
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();
			b.group(group).channel(NioSocketChannel.class)
					.option(ChannelOption.TCP_NODELAY, true)
					.handler(new ChannelInitializer<SocketChannel>() {
						@Override
						public void initChannel(SocketChannel ch)
								throws Exception {

							// 客户端,对请求编码
							ch.pipeline().addLast("http-encoder",
									new HttpRequestEncoder());
							// 客户端,对响应解码
							ch.pipeline().addLast("http-decoder",
									new HttpResponseDecoder());
							// 聚合器,把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
							ch.pipeline().addLast("http-aggregator",
									new HttpObjectAggregator(65536));
							// 块写入处理器
							ch.pipeline().addLast("http-chunked",
									new ChunkedWriteHandler());
							// 自定义客户端处理器
							ch.pipeline().addLast("fileClientHandler",
									new HttpFileClientHandler());

						}
					});

			// 发起异步连接操作
			ChannelFuture f = b.connect(new InetSocketAddress(port)).sync();

			// 当代客户端链路关闭
			f.channel().closeFuture().sync();

		} finally {
			// 优雅退出,释放NIO线程组
			group.shutdownGracefully();
		}
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		new HttpFileClient().connect(Constants.DEFAULT_PORT);
	}
}

HttpFileClientHandler.java

package emulator.http;

import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.commons.io.FileUtils;

import emulator.Constants;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.util.CharsetUtil;

/**
 * 模拟客户端处理器
 * @author lh
 * @date 2015-08-11 14:32
 * @version 1.0
 * 
 */
public class HttpFileClientHandler extends
		SimpleChannelInboundHandler<FullHttpResponse> {

	@Override
	public void channelActive(ChannelHandlerContext ctx) {
		String xml = null;
		try {
			xml = FileUtils.readFileToString(new File("D:\\workspaces\\eclipse-huifu\\emulator\\xml\\account\\manage\\req.xml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		URI uri = null;
		try {
			uri = new URI("http://"+Constants.DEFAULT_IP+":"+Constants.DEFAULT_PORT);
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1,
				HttpMethod.GET,uri.toASCIIString(), 
				Unpooled.copiedBuffer(xml, CharsetUtil.UTF_8));	
		
		req.headers().set(HttpHeaders.Names.HOST, Constants.DEFAULT_IP);
        req.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        req.headers().set(HttpHeaders.Names.CONTENT_LENGTH, req.content().readableBytes());
		
		ctx.writeAndFlush(req);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		ctx.close();
	}

	@Override
	protected void messageReceived(ChannelHandlerContext ctx,
			FullHttpResponse msg) throws Exception {
		
		ByteBuf buf =  msg.content();
		byte [] resp = new byte[buf.readableBytes()];  
        buf.readBytes(resp);  
        String xml = new String(resp,"UTF-8");  
		System.out.println("server cotent:\n"+xml);
	}
}

Constants.java

package emulator;

public final class Constants {
	
	/**
	 * 电子账户
	 */
	public static final String TC_DZZH = "31001";
	/**
	 * 银行卡绑定
	 */
	public static final String TC_YHKBD = "31002";
	/**
	 * 客户充值
	 */
	public static final String TC_KHCZ = "31021";
	/**
	 * 协议管理
	 */
	public static final String TC_XYGL = "31004";
	/**
	 * 项目管理
	 */
	public static final String TC_XMGL = "31005";
	
	/**
	 * 交易成功
	 */
	public static final String TC_JYCG = "0000";
	
	/**
	 * 交易失败
	 */
	public static final String TC_JYSB = "0001";
	
	/**
	 * 部分成功
	 */
	public static final String TC_BFCG = "0002";
	
	
	/**
	 * 默认IP
	 */
	public static final String DEFAULT_IP = "192.168.1.64";
	/**
	 * 默认端口
	 */
	public static final int DEFAULT_PORT = 8080;	

}

猜你喜欢

转载自hbxflihua.iteye.com/blog/2234403