我正在开发一个Netty HTTP服务器,它应该只是快速发送一个本地文件&尽可能通过HTTP实现。
现在传输提前停止,错误消息如下所示:
$ curl http://192.168.56.101:32324/dl/%2Fstorage%2Femulated%2F0%2FMovies%2Fstartrek-tlr3-german_h720p.mov > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
curl: (18) transfer closed with 90831821 bytes remaining to read
$ curl http://192.168.56.101:32324/dl/%2Fstorage%2Femulated%2F0%2FMovies%2Fstartrek-tlr3-german_h720p.mov > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
curl: (18) transfer closed with 90833209 bytes remaining to read
为此,我把Netty包括在内:
的build.gradle :
// Netty & Friends.
compile group: 'io.netty', name: 'netty-all', version: '4.1.0.Beta4'
compile group: 'tv.cntt', name: 'jauter', version: '1.7'
compile 'javax.activation:activation:1.1.1'
compile 'org.javassist:javassist:3.19.0-GA'
MediaLinkServerInitializer.java :
final class MediaLinkServerInitializer extends ChannelInitializer<SocketChannel> {
private final Handler handler;
public MediaLinkServerInitializer(Context context) {
final Router router = new Router()
.GET("/dl/:path", StaticFileHandler.class)
.HEAD("/dl/:path", StaticFileHandler.class);
handler = new Handler(router);
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(handler.name(), handler);
}
}
此项目使用netty-router扩展名将HTTP路径连接到处理程序类。
Bootstrap调用:
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new MediaLinkServerInitializer(context));
channel = b.bind(host, port).sync().channel();
log.log(Level.CONFIG, "Started instance on {0}:{1,number,#}", new Object[]{host, port});
channel.closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
log.finest("Channel closed.");
channel = null;
}
});
channel.closeFuture().sync();
StaticFileHandler.java :
public class StaticFileHandler extends SimpleChannelInboundHandler<Routed> {
@Override
public void channelRead0(final ChannelHandlerContext ctx, final Routed request) throws Exception {
File file = ... // get from URL, check etc.
RandomAccessFile raf;
try {
raf = new RandomAccessFile(file, "r");
} catch (FileNotFoundException ignore) {
sendError(ctx, NOT_FOUND);
return;
}
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
setContentLength(response, fileLength);
setContentTypeHeader(response, file);
setDateAndCacheHeaders(response, file);
setKeepAlive(response, isKeepAlive(request.request()));
// Write the initial line and the header.
ctx.write(response);
ChannelProgressiveFuture future = (ChannelProgressiveFuture) ctx.writeAndFlush(
new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
future.addListener(new ChannelProgressiveFutureListener() {
@Override
public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception {
}
@Override
public void operationComplete(ChannelProgressiveFuture future) throws Exception {
log.log(Level.FINEST, "Transfer complete: {0}", future.channel());
}
});
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
}
}
根据此presentation,只有当平台通过“发送文件”支持零拷贝传输时,才能使用FileRegion。系统调用。我非常确定我的目标平台(Android 4.4,内核3.4+)可以。
一旦我像这样使用io.netty.handler.stream.ChunkedWriteHandler
,转移就会起作用:
ChannelProgressiveFuture future = (ChannelProgressiveFuture) ctx.writeAndFlush(
new ChunkedNioFile(raf.getChannel(), 0, fileLength, 1024 * 96), ctx.newProgressivePromise());
future.addListener(new ChannelProgressiveFutureListener() {
@Override
public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception {
}
@Override
public void operationComplete(ChannelProgressiveFuture future) throws Exception {
log.log(Level.FINEST, "Transfer complete: {0}", future.channel());
}
});
那些早期失败的转移可能会出现什么问题?