1、长链接:使用长链接通道不断开的形式进行通信,也就是Server和Client的通道一直处于开启状态,如果服务器性能足够好,并且我们的客户端数据也比较少的情况下,可以使用这种。
2、一次性批量提交数据:这是采用短链接方式,也就是会把数据保存在本地临时缓冲区或者临时表里,当达到临界值时,进行一次性批量提交,又或者根据定时任务轮询提交,这种情况的弊端是做不到实时性传输,在对实时性不高的应用程序可以使用这种。
3、特殊的长链接:在指定的某一时间内,服务器与某些客户端没有任何通信,则断开连接。下次连接则客户端向服务端发送数据的时候,再次建立连接,但这种需要考虑两个因素。
一是在超时时,服务端和客户端没有任何通信后关闭通道?关闭通道后又如何再次建立连接?
二是客户端宕机时,无需考虑,下次客户端重启之后就可以与服务器建立连接,但是服务器宕机时,客户端如何与服务器进行链接?
import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration;
import io.netty.handler.codec.marshalling.DefaultMarshallerProvider; import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider; import io.netty.handler.codec.marshalling.MarshallerProvider; import io.netty.handler.codec.marshalling.MarshallingDecoder; import io.netty.handler.codec.marshalling.MarshallingEncoder;
/** * 序列化编解码器: * JBoss:Marshalling * Google:Protobuf * 基于Protobuf:Kyro * MessagePack框架 */ public class N06MarshallingDecoderEncoderUtils {
public final static String TAG = "serial";
/** * 解码器:将二进制序列化为java对象 */ public static MarshallingDecoder buildDecoder() { //通过Marshalling工具类获取到实例对象,serial标识创建的是java的序列化工厂对象 MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory(TAG);
//创建MarshallingConfiguration对象,并配置版本号 MarshallingConfiguration marshallingConfiguration = new MarshallingConfiguration(); marshallingConfiguration.setVersion(5);
//创建Provider DefaultUnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, marshallingConfiguration);
//构建Netty的解码对象,并配置provider和单个消息序列化的最大长度 return new MarshallingDecoder(provider, 1024 * 1024); }
/** * 编码器:将对象序列化为二进制数组 */ public static MarshallingEncoder buildEncoder() { //序列化java工厂类 MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory(TAG);
//创建配置类 MarshallingConfiguration marshallingConfiguration = new MarshallingConfiguration(); marshallingConfiguration.setVersion(5);
//创建供应者 MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, marshallingConfiguration);
//构建Netty的MarshallingEncoder对象 MarshallingEncoder marshallingEncoder = new MarshallingEncoder(provider); return marshallingEncoder; } } |
import java.io.Serializable;
/** * 请求数据 */ public class N06Request implements Serializable {
private static final long serialVersionUID = -7633763905125419996L;
private String data;
public String getData() { return data; }
public void setData(String data) { this.data = data; }
|
import java.io.Serializable;
/** * 响应数据 */ public class N06Response implements Serializable {
private static final long serialVersionUID = -6704864849770619898L;
private String data;
public String getData() { return data; }
public void setData(String data) { this.data = data; } }
|
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; 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.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.ReadTimeoutHandler;
/** * 案例:数据通信服务端 */ public class N06DataCommunicationServer { public static void main(String[] args) throws Exception { //连接和传输数据 EventLoopGroup connectEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup dataEventLoopGroup = new NioEventLoopGroup();
//启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(connectEventLoopGroup, dataEventLoopGroup) //绑定 .channel(NioServerSocketChannel.class) //模式 .option(ChannelOption.SO_BACKLOG, 1024) //TCP缓冲 .handler(new LoggingHandler(LogLevel.INFO)) //日志 .childHandler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { //编解码器 ch.pipeline().addLast(N06MarshallingDecoderEncoderUtils.buildDecoder()); ch.pipeline().addLast(N06MarshallingDecoderEncoderUtils.buildEncoder());
//读取超时 ch.pipeline().addLast(new ReadTimeoutHandler(5));
//处理类 ch.pipeline().addLast(new DataCommunicationServerHandler()); } });
//通道 ChannelFuture channelFuture = serverBootstrap.bind(1111).sync(); channelFuture.channel().closeFuture().sync();
//关闭 connectEventLoopGroup.shutdownGracefully(); dataEventLoopGroup.shutdownGracefully();
} } class DataCommunicationServerHandler extends ChannelHandlerAdapter {
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("Server Active"); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //已经设置了编解码器 N06Request request = (N06Request) msg; System.out.println("Server:" + request.getData());
//响应 N06Response response = new N06Response(); response.setData("Hi Client!"); ctx.writeAndFlush(response) //模拟关闭链接,客户端重新链接 .addListener(ChannelFutureListener.CLOSE); }
@Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { System.out.println("Server Read Complete"); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } } |
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; 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.NioSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.ReferenceCountUtil;
/** * 案例:数据通信客户端 */ public class N06DataCommunicationClient { public static void main(String[] args) throws InterruptedException { //创建链接 DataCommunicationClient dataCommunicationClient = new DataCommunicationClient(); ChannelFuture channelFuture = dataCommunicationClient.getChannelFuture();
//写出数据 N06Request request = new N06Request(); request.setData("Hi,Server!"); channelFuture.channel().writeAndFlush(request);
channelFuture.channel().closeFuture().sync();
//在服务端开启,写完数据就断开,模拟重连的过程 //获取连接 ChannelFuture channelFuture2 = dataCommunicationClient.getChannelFuture(); System.out.println(channelFuture2.channel().isActive()); System.out.println(channelFuture2.channel().isOpen());
//再次发送数据 request = new N06Request(); request.setData("Hi,Server2222222!"); channelFuture2.channel().writeAndFlush(request); channelFuture2.channel().closeFuture().sync();
System.out.println("结束"); } } class DataCommunicationClient {
private EventLoopGroup eventLoopGroup; private Bootstrap bootstrap; private ChannelFuture channelFuture;
/** * 创建实例时,就初始化启动类 */ public DataCommunicationClient () { //创建链接 this.eventLoopGroup = new NioEventLoopGroup();
//启动类 this.bootstrap = new Bootstrap(); this.bootstrap.group(eventLoopGroup) //绑定 .channel(NioSocketChannel.class) //模式 .handler(new LoggingHandler(LogLevel.INFO)) //日志 .handler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { //编解码器 ch.pipeline().addLast(N06MarshallingDecoderEncoderUtils.buildDecoder()); ch.pipeline().addLast(N06MarshallingDecoderEncoderUtils.buildEncoder());
//超时 ch.pipeline().addLast(new ReadTimeoutHandler(5));
//数据处理类 ch.pipeline().addLast(new DataCommunicationClientHandler()); } }); }
/** * 建立链接 */ public void connect() { try { this.channelFuture = bootstrap.connect("127.0.0.1", 1111).sync(); System.out.println("链接建立成功!"); } catch (InterruptedException e) { e.printStackTrace(); } }
/** * 获取连接 */ public ChannelFuture getChannelFuture() { //为空就继续获取连接 if(null == this.channelFuture) { this.connect(); } //继续获取连接 if (!this.channelFuture.channel().isActive()) { this.connect(); } return channelFuture; } } class DataCommunicationClientHandler extends ChannelHandlerAdapter {
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("Client Active"); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { N06Response response = (N06Response) msg; System.out.println("Client:" + response); } finally { ReferenceCountUtil.release(msg); } }
@Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { System.out.println("Client Read Complete"); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } } |
使用Socket通信一般会处理多个服务器之间的心跳检测,一般来讲去维护一个服务器集群,肯定要有一台或几台服务器的Master,然后由多态Slave,那么Master肯定时时刻刻知道自己下面的Slave服务器的各种情况,然后进行实时监控的功能,在分布式框架里叫作心跳检测或心跳监控,最佳处理方案就是使用一些通信框架去实现。
import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration;
import io.netty.handler.codec.marshalling.DefaultMarshallerProvider; import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider; import io.netty.handler.codec.marshalling.MarshallerProvider; import io.netty.handler.codec.marshalling.MarshallingDecoder; import io.netty.handler.codec.marshalling.MarshallingEncoder;
/** * 序列化编解码器: * JBoss:Marshalling * Google:Protobuf * 基于Protobuf:Kyro * MessagePack框架 */ public class N07MarshallingDecoderEncoderUtils {
public final static String TAG = "serial";
/** * 解码器:将二进制序列化为java对象 */ public static MarshallingDecoder buildDecoder() { //通过Marshalling工具类获取到实例对象,serial标识创建的是java的序列化工厂对象 MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory(TAG);
//创建MarshallingConfiguration对象,并配置版本号 MarshallingConfiguration marshallingConfiguration = new MarshallingConfiguration(); marshallingConfiguration.setVersion(5);
//创建Provider DefaultUnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, marshallingConfiguration);
//构建Netty的解码对象,并配置provider和单个消息序列化的最大长度 return new MarshallingDecoder(provider, 1024 * 1024); }
/** * 编码器:将对象序列化为二进制数组 */ public static MarshallingEncoder buildEncoder() { //序列化java工厂类 MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory(TAG);
//创建配置类 MarshallingConfiguration marshallingConfiguration = new MarshallingConfiguration(); marshallingConfiguration.setVersion(5);
//创建供应者 MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, marshallingConfiguration);
//构建Netty的MarshallingEncoder对象 MarshallingEncoder marshallingEncoder = new MarshallingEncoder(provider); return marshallingEncoder; } } |
import java.io.Serializable;
/** * 请求数据 */ public class N07Request implements Serializable {
private static final long serialVersionUID = -7633763905125419996L;
private String data;
public String getData() { return data; }
public void setData(String data) { this.data = data; } } |
import java.io.Serializable;
/** * 响应数据 */ public class N07Response implements Serializable {
private static final long serialVersionUID = -6704864849770619898L;
private String data;
public String getData() { return data; }
public void setData(String data) { this.data = data; }
@Override public String toString() { return "Response [data=" + data + "]"; } } |
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; 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.logging.LogLevel; import io.netty.handler.logging.LoggingHandler;
/** * 案例:心跳检测服务端 */ public class N07HeartbeatServer { public static void main(String[] args) throws Exception { //链接和传数据 EventLoopGroup connectEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup dataEventLoopGroup = new NioEventLoopGroup();
//启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(connectEventLoopGroup, dataEventLoopGroup) //绑定 .channel(NioServerSocketChannel.class) //模式 .option(ChannelOption.SO_BACKLOG, 1024) //TCP缓冲区 .handler(new LoggingHandler(LogLevel.INFO)) //日志 .childHandler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { //编解码器 ch.pipeline().addLast(N07MarshallingDecoderEncoderUtils.buildDecoder()); ch.pipeline().addLast(N07MarshallingDecoderEncoderUtils.buildEncoder());
//处理类 ch.pipeline().addLast(new HeartbeatServerHandler()); } });
System.out.println("服务端启动......");
//绑定端口 ChannelFuture channelFuture = serverBootstrap.bind(1111).sync(); channelFuture.channel().closeFuture().sync();
//关闭 connectEventLoopGroup.shutdownGracefully(); dataEventLoopGroup.shutdownGracefully(); } } class HeartbeatServerHandler extends ChannelHandlerAdapter {
private static final String KEY = "111111122222233333333";
/** * 认证 */ private boolean auth(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("进入数据验证......."); String data = (String)msg; if (KEY.equals(data)) { ctx.writeAndFlush("success"); return true; } ctx.writeAndFlush("failure"); // ctx.writeAndFlush("failure").addListener(ChannelFutureListener.CLOSE); return true; }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //认证 if (msg instanceof String) { boolean auth = auth(ctx, msg); System.out.println("验证结果:" + auth); //心跳数据 } else if(msg instanceof N07Request) { N07Request request = (N07Request) msg; System.out.println("Server:" + request.getData()); ctx.writeAndFlush("收到状态数据"); // ctx.writeAndFlush("收到状态数据").addListener(ChannelFutureListener.CLOSE);
}else { ctx.writeAndFlush("failure"); // ctx.writeAndFlush("failure").addListener(ChannelFutureListener.CLOSE); } } } |
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit;
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; 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.NioSocketChannel; import io.netty.util.ReferenceCountUtil;
/** * 案例:心跳检测客户端 */ public class N07HeartbeatClient { public static void main(String[] args) throws Exception { //创建链接 EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
//启动 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup) //绑定 .channel(NioSocketChannel.class) //模式 .handler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { //编解码器 ch.pipeline().addLast(N07MarshallingDecoderEncoderUtils.buildDecoder()); ch.pipeline().addLast(N07MarshallingDecoderEncoderUtils.buildEncoder());
//处理器 ch.pipeline().addLast(new HeartbeatClientHandler()); } });
//链接 ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",1111).sync(); channelFuture.channel().closeFuture().sync();
//关闭 eventLoopGroup.shutdownGracefully(); } } class HeartbeatClientHandler extends ChannelHandlerAdapter {
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
private static final String KEY = "111111122222233333333";
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("客户端启动去验证..."); //通道启用时,就去链接 ctx.writeAndFlush(KEY); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { System.out.println("客户端读取回传的数据..."); if (msg instanceof String) { //认证成功,发送心跳数据 if("success".equals(msg)) { //延迟2秒发送,测试时,要注意,Server端设置了关闭(ChannelFutureListener.CLOSE) this.scheduledExecutorService.scheduleWithFixedDelay(new HeartbeatRun(ctx), 0, 2, TimeUnit.SECONDS); } System.out.println("Client:" + msg); }else { System.out.println("Client:Other - " + msg); }
} finally { ReferenceCountUtil.release(msg); } } } class HeartbeatRun implements Runnable {
private ChannelHandlerContext ctx;
public HeartbeatRun(ChannelHandlerContext ctx) { super(); this.ctx = ctx; }
@Override public void run() { //发送机器数据 // Sigar sigar = new Sigar(); try { // CpuPerc cpuPerc = sigar.getCpuPerc(); N07Request request = new N07Request(); // request.setData("Cpu使用率:" + cpuPerc.getUser()); request.setData("Cpu使用率:88%"); ctx.writeAndFlush(request); } catch (Exception e) { e.printStackTrace(); } } } |
Http协议是超文本协议,是建立在TCP传输协议之上的应用层协议,目前主流是针对WEB开发,Http的应用非常广泛。其中Netty的Http协议也是一部非阻塞的。
简单:客户端请求服务器只需要携带指定的URL和携带必要的参数即可。
灵活:Http协议允许传输任意类型的数据对象,传输内容由Http消息头中的Content-Type加以标记。
无状态:协议对事务处理是没有记录能力的,如果后续处理需要之前的信息,必须重新获取,侧面显示出Http的轻量级、敏捷、负载轻。
组成部分:请求头、请求行、请求正文。
请求方式:Get(获取)、Post(附加新的提交数据)、Head(请求获取由请求URI标识的资源的响应消息头)、Put(新增)、Delete(删除)、Trace(测试和诊断)、Connect(保留未来)、Options(查询服务器性能)
状态码:分类为1、2、3、4、5。
/** * 案例 - Netty使用Http协议,也可基于SSL的链接访问 */ public class N08HttpRequestServer {
//SSL static SslContext sslContext = null;
public static void main(String[] args) throws Exception {
//SSL boolean ssl = null != System.getProperty("ssl");
//端口配置,SSL的端口不一样 int port = Integer.parseInt(System.getProperty("port",ssl? "8443" : "8080"));
//SSL配置 if (ssl) { SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate(); sslContext = SslContext.newServerContext(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey()); }
//配置链接和传输数据 EventLoopGroup connectEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup dataEventLoopGroup = new NioEventLoopGroup();
//启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(connectEventLoopGroup,dataEventLoopGroup) //绑定 .option(ChannelOption.SO_BACKLOG, 1024) .channel(NioServerSocketChannel.class) //模式 .handler(new LoggingHandler(LogLevel.INFO)) //日志 .childHandler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { //SSL配置 if(null != sslContext) { ch.pipeline().addLast(sslContext.newHandler(ch.alloc())); }
//编解码器 ch.pipeline().addLast(new HttpServerCodec());
//数据处理类 ch.pipeline().addLast(new HttpRequestServerHandler()); } });
//启动 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); System.out.println("浏览器访问:http://127.0.0.1:8080/"); channelFuture.channel().closeFuture().sync();
//关闭 connectEventLoopGroup.shutdownGracefully(); dataEventLoopGroup.shutdownGracefully(); } } class HttpRequestServerHandler extends ChannelHandlerAdapter {
/** * 响应内容 */ private static final byte[] CONTENT = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //请求是Http的请求 if (msg instanceof HttpRequest) { //编解码器已设置 HttpRequest httpRequest = (HttpRequest) msg;
if(HttpHeaderUtil.is100ContinueExpected(httpRequest)) { ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); }
//响应数据 DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,Unpooled.wrappedBuffer(CONTENT)); response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain"); response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH,response.content().readableBytes());
//是否活跃的响应 boolean keepAlive = HttpHeaderUtil.isKeepAlive(httpRequest); if (!keepAlive) { ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}else { response.headers().set(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE); ctx.writeAndFlush(response); } } } } |
Netty的Http协议栈从性能和可靠性都比较优异,非常适合Web容器场景下应用,相比于传统的Tomcat、Jetty等Web容器更加轻量级,灵活性和定制型也更好。
import java.io.File; import java.io.RandomAccessFile;
import javax.activation.MimetypesFileTypeMap;
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelProgressiveFuture; import io.netty.channel.ChannelProgressiveFutureListener; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; 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.DefaultHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderUtil; import io.netty.handler.codec.http.HttpHeaderValues; 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.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.stream.ChunkedFile; import io.netty.handler.stream.ChunkedWriteHandler;
/** * 案例 - 文件服务器,Http下载文件,并实时监控 */ public class N09HttpDownloadFileServer { public static void main(String[] args) throws Exception {
String url = "/sources/";
//链接和数据处理 EventLoopGroup connectEventLoopGroup = new NioEventLoopGroup(); EventLoopGroup dataEventLoopGroup = new NioEventLoopGroup();
//启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(connectEventLoopGroup, dataEventLoopGroup) //绑定 .channel(NioServerSocketChannel.class) //模式 .childHandler(new ChannelInitializer @Override protected void initChannel(SocketChannel ch) throws Exception { //编解码器,请求解码,响应编码 ch.pipeline().addLast("http-decoder", new HttpRequestDecoder()); ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
//多个消息转为单一的FullHttpRequest或FullHttpResponse ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65535));
//支持异步发送码流,用于支持大文件传输,但不占用过多的内存,防止java内存溢出 ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
//自定义文件服务器业务处理 ch.pipeline().addLast("fileServerHandler",new HttpDownloadFileServerHandler(url)); } });
//绑定端口 ChannelFuture channelFuture = serverBootstrap.bind("127.0.0.1",1111).sync(); System.out.println("访问地址:http://127.0.0.1:1111" + url); System.out.println("下载地址:http://127.0.0.1:1111" + url + "2.png"); channelFuture.channel().closeFuture().sync();
//关闭 connectEventLoopGroup.shutdownGracefully(); dataEventLoopGroup.shutdownGracefully(); } } class HttpDownloadFileServerHandler extends SimpleChannelInboundHandler
//访问地址 private String url;
public HttpDownloadFileServerHandler(String url) { this.url = url; }
@Override protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { //获取请求的uri路径 String uri = msg.uri();
//正常的话,要校验请求、校验uri、校验文件或文件夹等等
//拼接文件地址 String path = System.getProperty("user.dir") + File.separator + uri; System.out.println("请求的路径:" + path);
//随机文件读写类,只读的方式 RandomAccessFile randomAccessFile = null; try { randomAccessFile = new RandomAccessFile(new File(path), "r"); } catch (Exception e) { e.printStackTrace(); }
//响应数据 DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); //响应信息 HttpHeaderUtil.setContentLength(response, randomAccessFile.length()); //响应头 response.headers().set(HttpHeaderNames.CONTENT_TYPE,new MimetypesFileTypeMap().getContentType(path)); //是否一直保持连接 if (HttpHeaderUtil.isKeepAlive(msg)) { response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } //写出 ctx.write(response);
//构建文件发送线程,将文件写入到Chunked缓冲区 ChannelFuture channelFuture = ctx.write(new ChunkedFile(randomAccessFile,0,randomAccessFile.length(),8192),ctx.newProgressivePromise()); //监听传输文件 channelFuture.addListener(new ChannelProgressiveFutureListener() { @Override public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception { if (total < 0) { System.out.println("传输进展:" + progress); }else { System.out.println("传输进展:" + progress + " / " + total); } } @Override public void operationComplete(ChannelProgressiveFuture future) throws Exception { System.out.println("传输结束!"); } }); //如果使用Chunked编码,需要发送一个编码结束的看空消息体进行标记,表示所有消息体已经成功发送 ChannelFuture lastChannelFuture = ctx.write(LastHttpContent.EMPTY_LAST_CONTENT); //刷出缓存 ctx.flush(); if (!HttpHeaderUtil.isKeepAlive(msg)) { lastChannelFuture.addListener(ChannelFutureListener.CLOSE); }
System.out.println("处理完成"); } } |