* Changed the type of WriteCompletionEvent.amount from int to long
* Added back FileRegion since mapped buffer does not outperform transferTo() * Refactored SocketSendBufferPool to support FileRegion
This commit is contained in:
parent
1989bd037b
commit
aa4b8a2a6c
@ -313,6 +313,20 @@ public class Channels {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel.getPipeline().sendUpstream(
|
||||||
|
new DefaultWriteCompletionEvent(channel, (long) amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a {@code "writeComplete"} event to the first
|
||||||
|
* {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
|
||||||
|
* the specified {@link Channel}.
|
||||||
|
*/
|
||||||
|
public static void fireWriteComplete(Channel channel, long amount) {
|
||||||
|
if (amount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
channel.getPipeline().sendUpstream(
|
channel.getPipeline().sendUpstream(
|
||||||
new DefaultWriteCompletionEvent(channel, amount));
|
new DefaultWriteCompletionEvent(channel, amount));
|
||||||
}
|
}
|
||||||
@ -324,9 +338,18 @@ public class Channels {
|
|||||||
* {@link ChannelHandlerContext}.
|
* {@link ChannelHandlerContext}.
|
||||||
*/
|
*/
|
||||||
public static void fireWriteComplete(ChannelHandlerContext ctx, int amount) {
|
public static void fireWriteComplete(ChannelHandlerContext ctx, int amount) {
|
||||||
ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount));
|
ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), (long) amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a {@code "writeComplete"} event to the
|
||||||
|
* {@link ChannelUpstreamHandler} which is placed in the closest upstream
|
||||||
|
* from the handler associated with the specified
|
||||||
|
* {@link ChannelHandlerContext}.
|
||||||
|
*/
|
||||||
|
public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) {
|
||||||
|
ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sends a {@code "channelInterestChanged"} event to the first
|
* Sends a {@code "channelInterestChanged"} event to the first
|
||||||
* {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
|
* {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
|
||||||
|
43
src/main/java/org/jboss/netty/channel/DefaultFileRegion.java
Normal file
43
src/main/java/org/jboss/netty/channel/DefaultFileRegion.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package org.jboss.netty.channel;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
|
import org.jboss.netty.logging.InternalLogger;
|
||||||
|
import org.jboss.netty.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
public class DefaultFileRegion implements FileRegion {
|
||||||
|
|
||||||
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultFileRegion.class);
|
||||||
|
|
||||||
|
private final FileChannel file;
|
||||||
|
private final long position;
|
||||||
|
private final long count;
|
||||||
|
|
||||||
|
public DefaultFileRegion(FileChannel file, long position, long count) {
|
||||||
|
this.file = file;
|
||||||
|
this.position = position;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(WritableByteChannel target) throws IOException {
|
||||||
|
return file.transferTo(position, count, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releaseExternalResources() {
|
||||||
|
try {
|
||||||
|
file.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn("Failed to close a file.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,12 +28,20 @@ import static org.jboss.netty.channel.Channels.*;
|
|||||||
public class DefaultWriteCompletionEvent implements WriteCompletionEvent {
|
public class DefaultWriteCompletionEvent implements WriteCompletionEvent {
|
||||||
|
|
||||||
private final Channel channel;
|
private final Channel channel;
|
||||||
private final int writtenAmount;
|
private final long writtenAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #DefaultWriteCompletionEvent(Channel, long)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public DefaultWriteCompletionEvent(Channel channel, int writtenAmount) {
|
||||||
|
this(channel, (long) writtenAmount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*/
|
*/
|
||||||
public DefaultWriteCompletionEvent(Channel channel, int writtenAmount) {
|
public DefaultWriteCompletionEvent(Channel channel, long writtenAmount) {
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
throw new NullPointerException("channel");
|
throw new NullPointerException("channel");
|
||||||
}
|
}
|
||||||
@ -54,7 +62,7 @@ public class DefaultWriteCompletionEvent implements WriteCompletionEvent {
|
|||||||
return succeededFuture(getChannel());
|
return succeededFuture(getChannel());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getWrittenAmount() {
|
public long getWrittenAmount() {
|
||||||
return writtenAmount;
|
return writtenAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
src/main/java/org/jboss/netty/channel/FileRegion.java
Normal file
47
src/main/java/org/jboss/netty/channel/FileRegion.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Red Hat licenses this file to you under the Apache License, version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with the
|
||||||
|
* License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jboss.netty.channel;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
|
import org.jboss.netty.util.ExternalResourceReleasable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A region of a file that is sent via a {@link Channel} which supports
|
||||||
|
* zero-copy file transfer.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
|
||||||
|
* @author <a href="http://gleamynode.net/">Trustin Lee</a>
|
||||||
|
* @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $
|
||||||
|
*/
|
||||||
|
public interface FileRegion extends ExternalResourceReleasable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the offset in the file where the transfer began.
|
||||||
|
*/
|
||||||
|
long getPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes to transfer.
|
||||||
|
*/
|
||||||
|
long getCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfers the content of this file region to the specified channel.
|
||||||
|
*/
|
||||||
|
long transferTo(WritableByteChannel target) throws IOException;
|
||||||
|
}
|
@ -34,5 +34,5 @@ public interface WriteCompletionEvent extends ChannelEvent {
|
|||||||
* @return the number of written bytes or messages, depending on the
|
* @return the number of written bytes or messages, depending on the
|
||||||
* type of the transport
|
* type of the transport
|
||||||
*/
|
*/
|
||||||
int getWrittenAmount();
|
long getWrittenAmount();
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ class NioDatagramChannel extends AbstractChannel
|
|||||||
boolean success = super.offer(e);
|
boolean success = super.offer(e);
|
||||||
assert success;
|
assert success;
|
||||||
|
|
||||||
int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes();
|
int messageSize = getMessageSize(e);
|
||||||
int newWriteBufferSize = writeBufferSize.addAndGet(messageSize);
|
int newWriteBufferSize = writeBufferSize.addAndGet(messageSize);
|
||||||
int highWaterMark = getConfig().getWriteBufferHighWaterMark();
|
int highWaterMark = getConfig().getWriteBufferHighWaterMark();
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ class NioDatagramChannel extends AbstractChannel
|
|||||||
public MessageEvent poll() {
|
public MessageEvent poll() {
|
||||||
MessageEvent e = super.poll();
|
MessageEvent e = super.poll();
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes();
|
int messageSize = getMessageSize(e);
|
||||||
int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize);
|
int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize);
|
||||||
int lowWaterMark = getConfig().getWriteBufferLowWaterMark();
|
int lowWaterMark = getConfig().getWriteBufferLowWaterMark();
|
||||||
|
|
||||||
@ -303,6 +303,14 @@ class NioDatagramChannel extends AbstractChannel
|
|||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getMessageSize(MessageEvent e) {
|
||||||
|
Object m = e.getMessage();
|
||||||
|
if (m instanceof ChannelBuffer) {
|
||||||
|
return ((ChannelBuffer) m).readableBytes();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +36,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffer;
|
|
||||||
import org.jboss.netty.buffer.ChannelBufferFactory;
|
import org.jboss.netty.buffer.ChannelBufferFactory;
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
import org.jboss.netty.channel.ChannelException;
|
import org.jboss.netty.channel.ChannelException;
|
||||||
@ -500,7 +499,7 @@ class NioDatagramWorker implements Runnable {
|
|||||||
boolean addOpWrite = false;
|
boolean addOpWrite = false;
|
||||||
boolean removeOpWrite = false;
|
boolean removeOpWrite = false;
|
||||||
|
|
||||||
int writtenBytes = 0;
|
long writtenBytes = 0;
|
||||||
|
|
||||||
final SocketSendBufferPool sendBufferPool = this.sendBufferPool;
|
final SocketSendBufferPool sendBufferPool = this.sendBufferPool;
|
||||||
final DatagramChannel ch = channel.getDatagramChannel();
|
final DatagramChannel ch = channel.getDatagramChannel();
|
||||||
@ -514,7 +513,6 @@ class NioDatagramWorker implements Runnable {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
MessageEvent evt = channel.currentWriteEvent;
|
MessageEvent evt = channel.currentWriteEvent;
|
||||||
SendBuffer buf;
|
SendBuffer buf;
|
||||||
ByteBuffer bb;
|
|
||||||
if (evt == null) {
|
if (evt == null) {
|
||||||
if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) {
|
if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) {
|
||||||
removeOpWrite = true;
|
removeOpWrite = true;
|
||||||
@ -522,20 +520,17 @@ class NioDatagramWorker implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelBuffer origBuf = (ChannelBuffer) evt.getMessage();
|
channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage());
|
||||||
channel.currentWriteBuffer = buf = sendBufferPool.acquire(origBuf);
|
|
||||||
bb = buf.buffer;
|
|
||||||
} else {
|
} else {
|
||||||
buf = channel.currentWriteBuffer;
|
buf = channel.currentWriteBuffer;
|
||||||
bb = buf.buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int localWrittenBytes = 0;
|
long localWrittenBytes = 0;
|
||||||
SocketAddress raddr = evt.getRemoteAddress();
|
SocketAddress raddr = evt.getRemoteAddress();
|
||||||
if (raddr == null) {
|
if (raddr == null) {
|
||||||
for (int i = writeSpinCount; i > 0; i --) {
|
for (int i = writeSpinCount; i > 0; i --) {
|
||||||
localWrittenBytes = ch.write(bb);
|
localWrittenBytes = buf.transferTo(ch);
|
||||||
if (localWrittenBytes != 0) {
|
if (localWrittenBytes != 0) {
|
||||||
writtenBytes += localWrittenBytes;
|
writtenBytes += localWrittenBytes;
|
||||||
break;
|
break;
|
||||||
@ -543,7 +538,7 @@ class NioDatagramWorker implements Runnable {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = writeSpinCount; i > 0; i --) {
|
for (int i = writeSpinCount; i > 0; i --) {
|
||||||
localWrittenBytes = ch.send(bb, raddr);
|
localWrittenBytes = buf.transferTo(ch, raddr);
|
||||||
if (localWrittenBytes != 0) {
|
if (localWrittenBytes != 0) {
|
||||||
writtenBytes += localWrittenBytes;
|
writtenBytes += localWrittenBytes;
|
||||||
break;
|
break;
|
||||||
@ -559,7 +554,6 @@ class NioDatagramWorker implements Runnable {
|
|||||||
channel.currentWriteBuffer = null;
|
channel.currentWriteBuffer = null;
|
||||||
evt = null;
|
evt = null;
|
||||||
buf = null;
|
buf = null;
|
||||||
bb = null;
|
|
||||||
future.setSuccess();
|
future.setSuccess();
|
||||||
} else {
|
} else {
|
||||||
// Not written at all - perhaps the kernel buffer is full.
|
// Not written at all - perhaps the kernel buffer is full.
|
||||||
@ -576,7 +570,6 @@ class NioDatagramWorker implements Runnable {
|
|||||||
channel.currentWriteBuffer = null;
|
channel.currentWriteBuffer = null;
|
||||||
buf = null;
|
buf = null;
|
||||||
evt = null;
|
evt = null;
|
||||||
bb = null;
|
|
||||||
future.setFailure(t);
|
future.setFailure(t);
|
||||||
fireExceptionCaught(channel, t);
|
fireExceptionCaught(channel, t);
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ class NioSocketChannel extends AbstractChannel
|
|||||||
boolean success = super.offer(e);
|
boolean success = super.offer(e);
|
||||||
assert success;
|
assert success;
|
||||||
|
|
||||||
int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes();
|
int messageSize = getMessageSize(e);
|
||||||
int newWriteBufferSize = writeBufferSize.addAndGet(messageSize);
|
int newWriteBufferSize = writeBufferSize.addAndGet(messageSize);
|
||||||
int highWaterMark = getConfig().getWriteBufferHighWaterMark();
|
int highWaterMark = getConfig().getWriteBufferHighWaterMark();
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ class NioSocketChannel extends AbstractChannel
|
|||||||
public MessageEvent poll() {
|
public MessageEvent poll() {
|
||||||
MessageEvent e = super.poll();
|
MessageEvent e = super.poll();
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes();
|
int messageSize = getMessageSize(e);
|
||||||
int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize);
|
int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize);
|
||||||
int lowWaterMark = getConfig().getWriteBufferLowWaterMark();
|
int lowWaterMark = getConfig().getWriteBufferLowWaterMark();
|
||||||
|
|
||||||
@ -247,6 +247,14 @@ class NioSocketChannel extends AbstractChannel
|
|||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getMessageSize(MessageEvent e) {
|
||||||
|
Object m = e.getMessage();
|
||||||
|
if (m instanceof ChannelBuffer) {
|
||||||
|
return ((ChannelBuffer) m).readableBytes();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class WriteTask implements Runnable {
|
private final class WriteTask implements Runnable {
|
||||||
|
@ -439,7 +439,7 @@ class NioWorker implements Runnable {
|
|||||||
boolean addOpWrite = false;
|
boolean addOpWrite = false;
|
||||||
boolean removeOpWrite = false;
|
boolean removeOpWrite = false;
|
||||||
|
|
||||||
int writtenBytes = 0;
|
long writtenBytes = 0;
|
||||||
|
|
||||||
final SocketSendBufferPool sendBufferPool = this.sendBufferPool;
|
final SocketSendBufferPool sendBufferPool = this.sendBufferPool;
|
||||||
final SocketChannel ch = channel.socket;
|
final SocketChannel ch = channel.socket;
|
||||||
@ -450,7 +450,6 @@ class NioWorker implements Runnable {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
MessageEvent evt = channel.currentWriteEvent;
|
MessageEvent evt = channel.currentWriteEvent;
|
||||||
SendBuffer buf;
|
SendBuffer buf;
|
||||||
ByteBuffer bb;
|
|
||||||
if (evt == null) {
|
if (evt == null) {
|
||||||
if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) {
|
if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) {
|
||||||
removeOpWrite = true;
|
removeOpWrite = true;
|
||||||
@ -458,44 +457,41 @@ class NioWorker implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelBuffer origBuf = (ChannelBuffer) evt.getMessage();
|
channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage());
|
||||||
channel.currentWriteBuffer = buf = sendBufferPool.acquire(origBuf);
|
|
||||||
bb = buf.buffer;
|
|
||||||
} else {
|
} else {
|
||||||
buf = channel.currentWriteBuffer;
|
buf = channel.currentWriteBuffer;
|
||||||
bb = buf.buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelFuture future = evt.getFuture();
|
ChannelFuture future = evt.getFuture();
|
||||||
try {
|
try {
|
||||||
int oldWrittenBytes = writtenBytes;
|
long localWrittenBytes = 0;
|
||||||
for (int i = writeSpinCount; i > 0; i --) {
|
for (int i = writeSpinCount; i > 0; i --) {
|
||||||
int localWrittenBytes = ch.write(bb);
|
localWrittenBytes = buf.transferTo(ch);
|
||||||
if (localWrittenBytes != 0) {
|
if (localWrittenBytes != 0) {
|
||||||
writtenBytes += localWrittenBytes;
|
writtenBytes += localWrittenBytes;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bb.hasRemaining()) {
|
if (buf.finished()) {
|
||||||
// Successful write - proceed to the next message.
|
// Successful write - proceed to the next message.
|
||||||
buf.release();
|
buf.release();
|
||||||
channel.currentWriteEvent = null;
|
channel.currentWriteEvent = null;
|
||||||
channel.currentWriteBuffer = null;
|
channel.currentWriteBuffer = null;
|
||||||
evt = null;
|
evt = null;
|
||||||
buf = null;
|
buf = null;
|
||||||
bb = null;
|
|
||||||
future.setSuccess();
|
future.setSuccess();
|
||||||
} else {
|
} else {
|
||||||
// Not written fully - perhaps the kernel buffer is full.
|
// Not written fully - perhaps the kernel buffer is full.
|
||||||
addOpWrite = true;
|
addOpWrite = true;
|
||||||
channel.writeSuspended = true;
|
channel.writeSuspended = true;
|
||||||
|
|
||||||
// Notify progress listeners if necessary.
|
if (localWrittenBytes > 0) {
|
||||||
future.setProgress(
|
// Notify progress listeners if necessary.
|
||||||
writtenBytes - oldWrittenBytes,
|
future.setProgress(
|
||||||
bb.position() - buf.initialPos,
|
localWrittenBytes,
|
||||||
bb.limit() - buf.initialPos);
|
buf.writtenBytes(), buf.totalBytes());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (AsynchronousCloseException e) {
|
} catch (AsynchronousCloseException e) {
|
||||||
@ -506,7 +502,6 @@ class NioWorker implements Runnable {
|
|||||||
channel.currentWriteBuffer = null;
|
channel.currentWriteBuffer = null;
|
||||||
buf = null;
|
buf = null;
|
||||||
evt = null;
|
evt = null;
|
||||||
bb = null;
|
|
||||||
future.setFailure(t);
|
future.setFailure(t);
|
||||||
fireExceptionCaught(channel, t);
|
fireExceptionCaught(channel, t);
|
||||||
if (t instanceof IOException) {
|
if (t instanceof IOException) {
|
||||||
|
@ -15,10 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.jboss.netty.channel.socket.nio;
|
package org.jboss.netty.channel.socket.nio;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.DatagramChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffer;
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.channel.FileRegion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
|
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
|
||||||
@ -42,27 +47,34 @@ final class SocketSendBufferPool {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
final SendBuffer acquire(ChannelBuffer src) {
|
final SendBuffer acquire(Object message) {
|
||||||
if (src.isDirect()) {
|
if (message instanceof ChannelBuffer) {
|
||||||
return new SendBuffer(null, src.toByteBuffer());
|
return acquire((ChannelBuffer) message);
|
||||||
}
|
} else if (message instanceof FileRegion) {
|
||||||
if (src.readableBytes() > DEFAULT_PREALLOCATION_SIZE) {
|
return acquire((FileRegion) message);
|
||||||
return new SendBuffer(null, src.toByteBuffer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendBuffer dst = acquire(src.readableBytes());
|
throw new IllegalArgumentException(
|
||||||
ByteBuffer dstbuf = dst.buffer;
|
"unsupported message type: " + message.getClass());
|
||||||
dstbuf.mark();
|
|
||||||
src.getBytes(src.readerIndex(), dstbuf);
|
|
||||||
dstbuf.reset();
|
|
||||||
return dst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final SendBuffer acquire(int size) {
|
private final SendBuffer acquire(FileRegion src) {
|
||||||
assert size <= DEFAULT_PREALLOCATION_SIZE;
|
return new FileSendBuffer(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final SendBuffer acquire(ChannelBuffer src) {
|
||||||
|
if (src.isDirect()) {
|
||||||
|
return new UnpooledSendBuffer(src.toByteBuffer());
|
||||||
|
}
|
||||||
|
if (src.readableBytes() > DEFAULT_PREALLOCATION_SIZE) {
|
||||||
|
return new UnpooledSendBuffer(src.toByteBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
final int size = src.readableBytes();
|
||||||
Preallocation current = this.current;
|
Preallocation current = this.current;
|
||||||
ByteBuffer buffer = current.buffer;
|
ByteBuffer buffer = current.buffer;
|
||||||
int remaining = buffer.remaining();
|
int remaining = buffer.remaining();
|
||||||
|
PooledSendBuffer dst;
|
||||||
|
|
||||||
if (size < remaining) {
|
if (size < remaining) {
|
||||||
int nextPos = buffer.position() + size;
|
int nextPos = buffer.position() + size;
|
||||||
@ -70,7 +82,7 @@ final class SocketSendBufferPool {
|
|||||||
buffer.position(align(nextPos));
|
buffer.position(align(nextPos));
|
||||||
slice.limit(nextPos);
|
slice.limit(nextPos);
|
||||||
current.refCnt ++;
|
current.refCnt ++;
|
||||||
return new SendBuffer(current, slice);
|
dst = new PooledSendBuffer(current, slice);
|
||||||
} else if (size > remaining) {
|
} else if (size > remaining) {
|
||||||
this.current = current = getPreallocation();
|
this.current = current = getPreallocation();
|
||||||
buffer = current.buffer;
|
buffer = current.buffer;
|
||||||
@ -78,12 +90,18 @@ final class SocketSendBufferPool {
|
|||||||
buffer.position(align(size));
|
buffer.position(align(size));
|
||||||
slice.limit(size);
|
slice.limit(size);
|
||||||
current.refCnt ++;
|
current.refCnt ++;
|
||||||
return new SendBuffer(current, slice);
|
dst = new PooledSendBuffer(current, slice);
|
||||||
} else { // size == remaining
|
} else { // size == remaining
|
||||||
current.refCnt ++;
|
current.refCnt ++;
|
||||||
this.current = getPreallocation0();
|
this.current = getPreallocation0();
|
||||||
return new SendBuffer(current, current.buffer);
|
dst = new PooledSendBuffer(current, current.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ByteBuffer dstbuf = dst.buffer;
|
||||||
|
dstbuf.mark();
|
||||||
|
src.getBytes(src.readerIndex(), dstbuf);
|
||||||
|
dstbuf.reset();
|
||||||
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Preallocation getPreallocation() {
|
private final Preallocation getPreallocation() {
|
||||||
@ -142,20 +160,85 @@ final class SocketSendBufferPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SendBuffer {
|
interface SendBuffer {
|
||||||
|
boolean finished();
|
||||||
|
long writtenBytes();
|
||||||
|
long totalBytes();
|
||||||
|
|
||||||
|
long transferTo(WritableByteChannel ch) throws IOException;
|
||||||
|
long transferTo(DatagramChannel ch, SocketAddress raddr) throws IOException;
|
||||||
|
|
||||||
|
void release();
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnpooledSendBuffer implements SendBuffer {
|
||||||
|
final ByteBuffer buffer;
|
||||||
|
final int initialPos;
|
||||||
|
|
||||||
|
UnpooledSendBuffer(ByteBuffer buffer) {
|
||||||
|
this.buffer = buffer;
|
||||||
|
initialPos = buffer.position();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean finished() {
|
||||||
|
return !buffer.hasRemaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long writtenBytes() {
|
||||||
|
return buffer.position() - initialPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long totalBytes() {
|
||||||
|
return buffer.limit() - initialPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long transferTo(WritableByteChannel ch) throws IOException {
|
||||||
|
return ch.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long transferTo(DatagramChannel ch, SocketAddress raddr) throws IOException {
|
||||||
|
return ch.send(buffer, raddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() {
|
||||||
|
// Unpooled.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class PooledSendBuffer implements SendBuffer {
|
||||||
private final Preallocation parent;
|
private final Preallocation parent;
|
||||||
final ByteBuffer buffer;
|
final ByteBuffer buffer;
|
||||||
final int initialPos;
|
final int initialPos;
|
||||||
|
|
||||||
SendBuffer(Preallocation parent, ByteBuffer buffer) {
|
PooledSendBuffer(Preallocation parent, ByteBuffer buffer) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
initialPos = buffer.position();
|
initialPos = buffer.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
void release() {
|
public boolean finished() {
|
||||||
|
return !buffer.hasRemaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long writtenBytes() {
|
||||||
|
return buffer.position() - initialPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long totalBytes() {
|
||||||
|
return buffer.limit() - initialPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(WritableByteChannel ch) throws IOException {
|
||||||
|
return ch.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(DatagramChannel ch, SocketAddress raddr) throws IOException {
|
||||||
|
return ch.send(buffer, raddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() {
|
||||||
final Preallocation parent = this.parent;
|
final Preallocation parent = this.parent;
|
||||||
if (parent != null && -- parent.refCnt == 0) {
|
if (-- parent.refCnt == 0) {
|
||||||
parent.buffer.clear();
|
parent.buffer.clear();
|
||||||
if (parent != current) {
|
if (parent != current) {
|
||||||
poolHead = new PreallocationRef(parent, poolHead);
|
poolHead = new PreallocationRef(parent, poolHead);
|
||||||
@ -163,4 +246,42 @@ final class SocketSendBufferPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final class FileSendBuffer implements SendBuffer {
|
||||||
|
|
||||||
|
private final FileRegion file;
|
||||||
|
private long writtenBytes;
|
||||||
|
|
||||||
|
|
||||||
|
FileSendBuffer(FileRegion file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finished() {
|
||||||
|
return writtenBytes >= file.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long writtenBytes() {
|
||||||
|
return writtenBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long totalBytes() {
|
||||||
|
return file.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(WritableByteChannel ch) throws IOException {
|
||||||
|
long localWrittenBytes = file.transferTo(ch);
|
||||||
|
writtenBytes += localWrittenBytes;
|
||||||
|
return localWrittenBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(DatagramChannel ch, SocketAddress raddr)
|
||||||
|
throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() {
|
||||||
|
// Unpooled.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -32,6 +32,7 @@ import org.jboss.netty.channel.Channel;
|
|||||||
import org.jboss.netty.channel.ChannelFuture;
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.channel.ChannelFutureListener;
|
import org.jboss.netty.channel.ChannelFutureListener;
|
||||||
import org.jboss.netty.channel.ChannelHandlerContext;
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
|
import org.jboss.netty.channel.DefaultFileRegion;
|
||||||
import org.jboss.netty.channel.ExceptionEvent;
|
import org.jboss.netty.channel.ExceptionEvent;
|
||||||
import org.jboss.netty.channel.MessageEvent;
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
|
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
|
||||||
@ -40,7 +41,6 @@ import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
|||||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.jboss.netty.handler.stream.ChunkedFile;
|
|
||||||
import org.jboss.netty.util.CharsetUtil;
|
import org.jboss.netty.util.CharsetUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +91,8 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
ch.write(response);
|
ch.write(response);
|
||||||
|
|
||||||
// Write the content.
|
// Write the content.
|
||||||
ChannelFuture writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
|
//ChannelFuture writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
|
||||||
|
ChannelFuture writeFuture = ch.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength));
|
||||||
|
|
||||||
// Decide whether to close the connection or not.
|
// Decide whether to close the connection or not.
|
||||||
if (!isKeepAlive(request)) {
|
if (!isKeepAlive(request)) {
|
||||||
@ -105,6 +106,8 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
Channel ch = e.getChannel();
|
Channel ch = e.getChannel();
|
||||||
Throwable cause = e.getCause();
|
Throwable cause = e.getCause();
|
||||||
|
cause.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
if (cause instanceof TooLongFrameException) {
|
if (cause instanceof TooLongFrameException) {
|
||||||
sendError(ctx, BAD_REQUEST);
|
sendError(ctx, BAD_REQUEST);
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user