前段时间,用NodeJS写了一个翻墙的,然后无聊的时候有写了一个JAVA的事件驱动的客户端,我的Github上都有下载。用的是JAVA net包,当时,为了实现事件驱动,各种线程池,各种折腾,总算实现了。
后来,看别的项目的代码,突现一个叫Netty的东西很早就有了对这些逻辑的封装,我晕菜,知识真实要随时更新啊,不进则退。最近,抽空学习了一下,甚至特意亚马逊上买了电子书看看,虽然作者连Maven 的log都写到书里骗钱了,但是还是值得一读的。抽空他的废话,整理两个例子。因为Netty如果不熟悉,一开始会比较费解,了解之后则用Java实现网路通讯的功能变的简单多了。
首先一个简单的HTTP服务器 简单2个类,一个启动及初始化,一个Handle处理业务
package com.expcorp.demo.netty.httpdemo;
import com.expcorp.demo.netty.TimeServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
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.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
public final class HttpHelloWorldServer {
static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
public static void main(String[] args) throws Exception {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpServerCodec());
ch.pipeline().addLast(new HttpHelloWorldServerHandler());
}
});
Channel ch = b.bind(PORT).sync().channel();
System.err.println("Open your web browser and navigate to http://127.0.0.1:" + PORT + '/');
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
一个Handler处理业务,简单设定个本地目录,有文件则返回文件,没文件返回NOFILE固定页面
package com.expcorp.demo.netty.httpdemo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpRequest;
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.HttpRequest;
import io.netty.handler.stream.ChunkedFile;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static io.netty.handler.codec.http.HttpHeaders.Values;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.Logger;
public class HttpHelloWorldServerHandler extends ChannelInboundHandlerAdapter {
private static final String ROOT = "C:\\Users\\Demo\\Desktop\\demo-netty\\";
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// System.out.print("[INFO] Check msg");
// if (msg instanceof DefaultHttpRequest) {
// System.out.println(" DefaultHttpRequest");
// }
if (msg instanceof HttpRequest) {
HttpRequest req = (HttpRequest) msg;
String strResultURL = req.getUri();
System.out.println("URL:" + strResultURL);
if (strResultURL.equals("/"))
strResultURL = "/index.html";
if (HttpHeaders.is100ContinueExpected(req)) {
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
}
boolean keepAlive = HttpHeaders.isKeepAlive(req);
File file = new File(ROOT + strResultURL);
if (file.exists()) {
try {
final ByteBuf tmpContent = new ChunkedFile(file).readChunk(ctx);
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK,
Unpooled.wrappedBuffer(tmpContent));
// response.headers().set(CONTENT_TYPE, "text/plain");
response.headers().set(CONTENT_TYPE, "text/html");
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
if (!keepAlive) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} else {
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
ctx.write(response);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
String data = "<!DOCTYPE html> \n" + "<html>\n" + " <body>No File\n" + " </body>\n" + "</html>";
final ByteBuf tmpContent = Unpooled.copiedBuffer(data.getBytes());
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK,
Unpooled.wrappedBuffer(tmpContent));
// response.headers().set(CONTENT_TYPE, "text/plain");
response.headers().set(CONTENT_TYPE, "text/html");
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
if (!keepAlive) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} else {
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
ctx.write(response);
}
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
整个Netty核心,我个人理解,就下面两句
ch.pipeline().addLast(new HttpServerCodec()); ch.pipeline().addLast(new HttpHelloWorldServerHandler());
1. XXXCodec主要负责解码,把端口取得的流数据可视化成你想要的,或者方便处理的类,通过这个Codec我们在Handler里就不必面对字节流,而是直接对处理好的数据类了,上文的例子,我们在Handler里面直接可以获得 HttpRequest了。 类似的Codec,Decodec Netty在github上的工程里面提供了很多范例,FTP等等,主流的运用都有。我们也可以自己实现自己想要的Codec,也就是通讯协议
2. 而Handler则是具体的业务流程了,是把数据扔掉,还是转发还是怎么要查数据库格式输出等等,都在这里面实现,你可以直接在这里对流操作,同样应该也可以封装到类,然后通过Decodec返回成流来实现(需要绑定输出outbound)。
暂时到这里,休息一下休息一下