序
李林峰的《netty权威指南》,从Java的NIO开始介绍,后面介绍TCP粘包拆包。中级篇介绍编解码技术。
第10章介绍了HTTP及netty HTTP+XML的技术。
因为xml实际使用接口中很少了,所以用json格式来替换下。
pom准备:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.17.Final</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
简单的请求过程:
1、Client向Server发送http请求。
2、Server端对http请求进行解析。
3、Server端向client发送http响应。
4、Client对http响应进行解析。
服务器端
public class HttpFileServer {
private static final String DEFAULT_URL = "/";
public void run(final int port, final String url) 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));// 目的是将多个消息转换为单一的request或者response对象
ch.pipeline().addLast("http-encoder",
new HttpResponseEncoder());//响应解码器
ch.pipeline().addLast("http-chunked",
new ChunkedWriteHandler());//目的是支持异步大文件传输()
ch.pipeline().addLast("fileServerHandler",
new HttpFileServerHandler(url));// 业务逻辑
}
});
ChannelFuture future = b.bind("10.253.8.138", port).sync();
System.out.println("HTTP文件目录服务器启动,网址是 : " + "http://10.253.8.138:"
+ port + url);
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();
}
}
String url = DEFAULT_URL;
if (args.length > 1)
url = args[1];
new HttpFileServer().run(port, url);
}
}
上面基本上是书中demo,就是把xml的编解码替换下。
public class HttpFileServerHandler extends ChannelInboundHandlerAdapter {
private static final AsciiString CONTENT_TYPE = new AsciiString("Content-Type");
private static final AsciiString CONTENT_LENGTH = new AsciiString("Content-Length");
private static final AsciiString CONNECTION = new AsciiString("Connection");
private static final AsciiString KEEP_ALIVE = new AsciiString("keep-alive");
private final String url;
public HttpFileServerHandler(String url) {
this.url = url;
}
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
if (!(msg instanceof FullHttpRequest)) {
//错误处理
return;
}
FullHttpRequest request = (FullHttpRequest) msg;//客户端的请求对象
JSONObject responseJson = new JSONObject();//新建一个返回消息的Json对象
//把客户端的请求数据格式化为Json对象
JSONObject requestJson = null;
try{
requestJson = new JSONObject(parseJosnRequest(request));
}catch(Exception e)
{
ResponseJson(ctx,request,new String("error json"));
return;
}
final String uri = request.uri();
if (request.method() == HttpMethod.POST) {
if(uri.equals("/bmi"))
{
//计算体重质量指数
double height =0.01* requestJson.getDouble("height");
double weight =requestJson.getDouble("weight");
double bmi =weight/(height*height);
bmi =((int)(bmi*100))/100.0;
responseJson.put("bmi", bmi +"");
}else{
//错误处理
responseJson.put("error", "404 Not Find");
}
}else{
//错误处理
responseJson.put("error", "404 Not Find");
}
//向客户端发送结果
ResponseJson(ctx,request,responseJson.toString());
}
private void ResponseJson(ChannelHandlerContext ctx, FullHttpRequest request, String jsonStr) {
// TODO Auto-generated method stub
boolean keepAlive = HttpUtil.isKeepAlive(request);
byte[] jsonByteByte = jsonStr.getBytes();
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(jsonByteByte));
response.headers().set(CONTENT_TYPE, "text/json");
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
if (keepAlive) {
response.headers().set(CONNECTION, KEEP_ALIVE);
}
ctx.writeAndFlush(response);
}
//获取请求的内容
private String parseJosnRequest(FullHttpRequest request) {
ByteBuf jsonBuf = request.content();
String jsonStr = jsonBuf.toString(CharsetUtil.UTF_8);
return jsonStr;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
这个参考网上的例子,把书上的看文件列表,替换成计算体重的bmi方法。
我们用postman测试下。可以正常返回数据。
客户端代码
还可以用client测试代码。
public class HttpJsonClient {
public void connect(String host, 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-decoder",
new HttpResponseDecoder());
ch.pipeline().addLast("http-aggregator",
new HttpObjectAggregator(65536));
ch.pipeline().addLast("http-encoder",
new HttpRequestEncoder());
ch.pipeline().addLast(new HttpJsonClientHandle());
}
});
// 发起异步连接操作
ChannelFuture f = b.connect(new InetSocketAddress(port)).sync();
//
URI uri = new URI("/bmi");
String msg = "{\"height\":175,\"weight\":70}";
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
uri.toASCIIString(), Unpooled.wrappedBuffer(msg.getBytes("UTF-8")));
// 构建http请求
request.headers().set(HttpHeaders.Names.HOST, host);
request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, request.content().readableBytes());
// 发送http请求
f.channel().writeAndFlush(request);
// 当代客户端链路关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
/**
* @param args
* @throws Exception
*/
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 HttpJsonClient().connect("127.0.0.1",port);
}
}
对应的handle
public class HttpJsonClientHandle extends ChannelInboundHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponse)
{
HttpResponse response = (HttpResponse) msg;
System.out.println("header:" + response.headers().names().toString());
}
if(msg instanceof HttpContent)
{
HttpContent content = (HttpContent)msg;
ByteBuf buf = content.content();
System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
buf.release();
}
}
}
可以看下client的调用输出:
header:io.netty.handler.codec.HeadersUtils$CharSequenceDelegatingStringSet@b56f674
{"bmi":"22.85"}
***********************************************************************
这是学习netty的第一个程序,有些过时的方法。
书上上来没有介绍netty的模型等基础知识,更侧重介绍了netty与Java的NIO的对比。
所以即使能跑起来,还是需要去学习netty的知识。
参考:
http://blog.csdn.net/shenzhan168/article/details/53142459