/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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.apache.tomcat.util.net;
// --------------------------------------------------------- Public Methods
/** * Number of keep-alive sockets. * * @return Always returns -1.
*/ publicint getKeepAliveCount() { // For this connector, only the overall connection count is relevant return -1;
}
// ----------------------------------------------- Public Lifecycle Methods
@Override protectedvoid startAcceptorThread() { // Instead of starting a real acceptor thread, this will instead call // an asynchronous accept operation if (acceptor == null) {
acceptor = new Nio2Acceptor(this);
acceptor.setThreadName(getName() + "-Acceptor");
}
acceptor.state = AcceptorState.RUNNING;
getExecutor().execute(acceptor);
}
/** * Stop the endpoint. This will cause all processing threads to stop.
*/
@Override publicvoid stopInternal() { if (!paused) {
pause();
} if (running) {
running = false;
acceptor.stop(10); // Use the executor to avoid binding the main thread if something bad // occurs and unbind will also wait for a bit for it to complete
getExecutor().execute(() -> { // Then close all active connections if any remain try { for (SocketWrapperBase<Nio2Channel> wrapper : getConnections()) {
wrapper.close();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
allClosed = true;
}
}); if (nioChannels != null) {
Nio2Channel socket; while ((socket = nioChannels.pop()) != null) {
socket.free();
}
nioChannels = null;
} if (processorCache != null) {
processorCache.clear();
processorCache = null;
}
}
}
/** * Deallocate NIO memory pools, and close server socket.
*/
@Override publicvoid unbind() throws Exception { if (running) {
stop();
}
doCloseServerSocket();
destroySsl(); super.unbind(); // Unlike other connectors, the thread pool is tied to the server socket
shutdownExecutor(); if (getHandler() != null) {
getHandler().recycle();
}
}
@Override protectedvoid doCloseServerSocket() throws IOException { // Close server socket if (serverSock != null) {
serverSock.close();
serverSock = null;
}
}
/** * Process the specified connection. * @param socket The socket channel * @return <code>true</code> if the socket was correctly configured * and processing may continue, <code>false</code> if the socket needs to be * close immediately
*/
@Override protectedboolean setSocketOptions(AsynchronousSocketChannel socket) {
Nio2SocketWrapper socketWrapper = null; try { // Allocate channel and wrapper
Nio2Channel channel = null; if (nioChannels != null) {
channel = nioChannels.pop();
} if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer()); if (isSSLEnabled()) {
channel = new SecureNio2Channel(bufhandler, this);
} else {
channel = new Nio2Channel(bufhandler);
}
}
Nio2SocketWrapper newWrapper = new Nio2SocketWrapper(channel, this);
channel.reset(socket, newWrapper);
connections.put(socket, newWrapper);
socketWrapper = newWrapper;
// Set socket properties
socketProperties.setProperties(socket);
socketWrapper.setReadTimeout(getConnectionTimeout());
socketWrapper.setWriteTimeout(getConnectionTimeout());
socketWrapper.setKeepAliveLeft(Nio2Endpoint.this.getMaxKeepAliveRequests()); // Continue processing on the same thread as the acceptor is async return processSocket(socketWrapper, SocketEvent.OPEN_READ, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.socketOptionsError"), t); if (socketWrapper == null) {
destroySocket(socket);
}
} // Tell to close the socket if needed returnfalse;
}
public Nio2Acceptor(AbstractEndpoint<?, AsynchronousSocketChannel> endpoint) { super(endpoint);
}
@Override publicvoid run() { // The initial accept will be called in a separate utility thread if (!isPaused()) { //if we have reached max connections, wait try {
countUpOrAwaitConnection();
} catch (InterruptedException e) { // Ignore
} if (!isPaused()) { // Note: as a special behavior, the completion handler for accept is // always called in a separate thread.
serverSock.accept(null, this);
} else {
state = AcceptorState.PAUSED;
}
} else {
state = AcceptorState.PAUSED;
}
}
/** * Signals the Acceptor to stop. * * @param waitSeconds Ignored for NIO2. *
*/
@Override publicvoid stop(int waitSeconds) {
acceptor.state = AcceptorState.ENDED;
}
@Override publicvoid completed(AsynchronousSocketChannel socket, Void attachment) { // Successful accept, reset the error delay
errorDelay = 0; // Continue processing the socket on the current thread // Configure the socket if (isRunning() && !isPaused()) { if (getMaxConnections() == -1) {
serverSock.accept(null, this);
} elseif (getConnectionCount() < getMaxConnections()) { try { // This will not block
countUpOrAwaitConnection();
} catch (InterruptedException e) { // Ignore
}
serverSock.accept(null, this);
} else { // Accept again on a new thread since countUpOrAwaitConnection may block
getExecutor().execute(this);
} if (!setSocketOptions(socket)) {
closeSocket(socket);
}
} else { if (isRunning()) {
state = AcceptorState.PAUSED;
}
destroySocket(socket);
}
}
@Override publicvoid failed(Throwable t, Void attachment) { if (isRunning()) { if (!isPaused()) { if (getMaxConnections() == -1) {
serverSock.accept(null, this);
} else { // Accept again on a new thread since countUpOrAwaitConnection may block
getExecutor().execute(this);
}
} else {
state = AcceptorState.PAUSED;
} // We didn't get a socket
countDownConnection(); // Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
} else { // We didn't get a socket
countDownConnection();
}
}
public Nio2SocketWrapper(Nio2Channel channel, final Nio2Endpoint endpoint) { super(channel, endpoint);
nioChannels = endpoint.getNioChannels();
socketBufferHandler = channel.getBufHandler();
this.readCompletionHandler = new CompletionHandler<>() {
@Override publicvoid completed(Integer nBytes, ByteBuffer attachment) { if (log.isDebugEnabled()) {
log.debug("Socket: [" + Nio2SocketWrapper.this + "], Interest: [" + readInterest + "]");
} boolean notify = false; synchronized (readCompletionHandler) {
readNotify = false; if (nBytes.intValue() < 0) {
failed(new EOFException(), attachment);
} else { if (readInterest && !isInline()) {
readNotify = true;
} else { // Release here since there will be no // notify/dispatch to do the release.
readPending.release();
}
readInterest = false;
}
notify = readNotify;
} if (notify) {
getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_READ, false);
}
}
@Override publicvoid failed(Throwable exc, ByteBuffer attachment) {
IOException ioe; if (exc instanceof IOException) {
ioe = (IOException) exc;
} else {
ioe = new IOException(exc);
}
setError(ioe); if (exc instanceof AsynchronousCloseException) { // Release here since there will be no // notify/dispatch to do the release.
readPending.release(); // If already closed, don't call onError and close again
getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.STOP, false);
} elseif (!getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, true)) {
close();
}
}
};
this.writeCompletionHandler = new CompletionHandler<>() {
@Override publicvoid completed(Integer nBytes, ByteBuffer attachment) { boolean notify = false; synchronized (writeCompletionHandler) {
writeNotify = false; if (nBytes.intValue() < 0) {
failed(new EOFException(sm.getString("iob.failedwrite")), attachment);
} elseif (!nonBlockingWriteBuffer.isEmpty()) { // Continue writing data using a gathering write
ByteBuffer[] array = nonBlockingWriteBuffer.toArray(attachment);
getSocket().write(array, 0, array.length,
toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS,
array, gatheringWriteCompletionHandler);
} elseif (attachment.hasRemaining()) { // Regular write
getSocket().write(attachment, toTimeout(getWriteTimeout()),
TimeUnit.MILLISECONDS, attachment, writeCompletionHandler);
} else { // All data has been written if (writeInterest && !isInline()) {
writeNotify = true; // Set extra flag so that write nesting does not cause multiple notifications
notify = true;
} else { // Release here since there will be no // notify/dispatch to do the release.
writePending.release();
}
writeInterest = false;
}
} if (notify) { if (!endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_WRITE, true)) {
close();
}
}
}
@Override publicvoid failed(Throwable exc, ByteBuffer attachment) {
IOException ioe; if (exc instanceof IOException) {
ioe = (IOException) exc;
} else {
ioe = new IOException(exc);
}
setError(ioe);
writePending.release(); if (!endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, true)) {
close();
}
}
};
gatheringWriteCompletionHandler = new CompletionHandler<>() {
@Override publicvoid completed(Long nBytes, ByteBuffer[] attachment) { boolean notify = false; synchronized (writeCompletionHandler) {
writeNotify = false; if (nBytes.longValue() < 0) {
failed(new EOFException(sm.getString("iob.failedwrite")), attachment);
} elseif (!nonBlockingWriteBuffer.isEmpty() || buffersArrayHasRemaining(attachment, 0, attachment.length)) { // Continue writing data using a gathering write
ByteBuffer[] array = nonBlockingWriteBuffer.toArray(attachment);
getSocket().write(array, 0, array.length,
toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS,
array, gatheringWriteCompletionHandler);
} else { // All data has been written if (writeInterest && !isInline()) {
writeNotify = true; // Set extra flag so that write nesting does not cause multiple notifications
notify = true;
} else { // Release here since there will be no // notify/dispatch to do the release.
writePending.release();
}
writeInterest = false;
}
} if (notify) { if (!endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_WRITE, true)) {
close();
}
}
}
@Override publicvoid failed(Throwable exc, ByteBuffer[] attachment) {
IOException ioe; if (exc instanceof IOException) {
ioe = (IOException) exc;
} else {
ioe = new IOException(exc);
}
setError(ioe);
writePending.release(); if (!endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, true)) {
close();
}
}
};
@Override publicboolean isReadyForRead() throws IOException { synchronized (readCompletionHandler) { // A notification has been sent, it is possible to read at least once if (readNotify) { returntrue;
} // If a read is pending, reading is not possible until a notification is sent if (!readPending.tryAcquire()) {
readInterest = true; returnfalse;
} // It is possible to read directly from the buffer contents if (!socketBufferHandler.isReadBufferEmpty()) {
readPending.release(); returntrue;
} // Try to read some data boolean isReady = fillReadBuffer(false) > 0; if (!isReady) {
readInterest = true;
} return isReady;
}
}
@Override publicboolean isReadyForWrite() { synchronized (writeCompletionHandler) { // A notification has been sent, it is possible to write at least once if (writeNotify) { returntrue;
} // If a write is pending, writing is not possible until a notification is sent if (!writePending.tryAcquire()) {
writeInterest = true; returnfalse;
} // If the buffer is empty, it is possible to write to it if (socketBufferHandler.isWriteBufferEmpty() && nonBlockingWriteBuffer.isEmpty()) {
writePending.release(); returntrue;
} // Try to flush all data boolean isReady = !flushNonBlockingInternal(true); if (!isReady) {
writeInterest = true;
} return isReady;
}
}
@Override publicint read(boolean block, byte[] b, int off, int len) throws IOException {
checkError();
if (log.isDebugEnabled()) {
log.debug("Socket: [" + this + "], block: [" + block + "], length: [" + len + "]");
}
if (socketBufferHandler == null) { thrownew IOException(sm.getString("socket.closed"));
}
if (!notify) { if (block) { try {
readPending.acquire();
} catch (InterruptedException e) { thrownew IOException(e);
}
} else { if (!readPending.tryAcquire()) { if (log.isDebugEnabled()) {
log.debug("Socket: [" + this + "], Read in progress. Returning [0]");
} return 0;
}
}
}
int nRead = populateReadBuffer(b, off, len); if (nRead > 0) { // The code that was notified is now reading its data synchronized (readCompletionHandler) {
readNotify = false;
} // This may be sufficient to complete the request and we // don't want to trigger another read since if there is no // more data to read and this request takes a while to // process the read will timeout triggering an error.
readPending.release(); return nRead;
}
synchronized (readCompletionHandler) { // Fill the read buffer as best we can.
nRead = fillReadBuffer(block); // Fill as much of the remaining byte array as possible with the // data that was just read if (nRead > 0) {
socketBufferHandler.configureReadBufferForRead();
nRead = Math.min(nRead, len);
socketBufferHandler.getReadBuffer().get(b, off, nRead);
} elseif (nRead == 0 && !block) {
readInterest = true;
} if (log.isDebugEnabled()) {
log.debug("Socket: [" + this + "], Read: [" + nRead + "]");
} return nRead;
}
}
if (!notify) { if (block) { try {
readPending.acquire();
} catch (InterruptedException e) { thrownew IOException(e);
}
} else { if (!readPending.tryAcquire()) { if (log.isDebugEnabled()) {
log.debug("Socket: [" + this + "], Read in progress. Returning [0]");
} return 0;
}
}
}
int nRead = populateReadBuffer(to); if (nRead > 0) { // The code that was notified is now reading its data synchronized (readCompletionHandler) {
readNotify = false;
} // This may be sufficient to complete the request and we // don't want to trigger another read since if there is no // more data to read and this request takes a while to // process the read will timeout triggering an error.
readPending.release(); return nRead;
}
synchronized (readCompletionHandler) { // The socket read buffer capacity is socket.appReadBufSize int limit = socketBufferHandler.getReadBuffer().capacity(); if (block && to.remaining() >= limit) {
to.limit(to.position() + limit);
nRead = fillReadBuffer(block, to); if (log.isDebugEnabled()) {
log.debug("Socket: [" + this + "], Read direct from socket: [" + nRead + "]");
}
} else { // Fill the read buffer as best we can.
nRead = fillReadBuffer(block); if (log.isDebugEnabled()) {
log.debug("Socket: [" + this + "], Read into buffer: [" + nRead + "]");
} // Fill as much of the remaining byte array as possible with the // data that was just read if (nRead > 0) {
nRead = populateReadBuffer(to);
} elseif (nRead == 0 && !block) {
readInterest = true;
}
} return nRead;
}
}
@Override publicvoid run() { if (read) { long nBytes = 0; // If there is still data inside the main read buffer, it needs to be read first if (!socketBufferHandler.isReadBufferEmpty()) { synchronized (readCompletionHandler) {
socketBufferHandler.configureReadBufferForRead(); for (int i = 0; i < length && !socketBufferHandler.isReadBufferEmpty(); i++) {
nBytes += transfer(socketBufferHandler.getReadBuffer(), buffers[offset + i]);
}
} if (nBytes > 0) {
completion.completed(Long.valueOf(nBytes), this);
}
} if (nBytes == 0) {
getSocket().read(buffers, offset, length, timeout, unit, this, completion);
}
} else { // If there is still data inside the main write buffer, it needs to be written first if (!socketBufferHandler.isWriteBufferEmpty()) { synchronized (writeCompletionHandler) {
socketBufferHandler.configureWriteBufferForRead();
ByteBuffer[] array = nonBlockingWriteBuffer.toArray(socketBufferHandler.getWriteBuffer()); if (buffersArrayHasRemaining(array, 0, array.length)) {
getSocket().write(array, 0, array.length, timeout, unit,
array, new CompletionHandler<Long, ByteBuffer[]>() {
@Override publicvoid completed(Long nBytes, ByteBuffer[] buffers) { if (nBytes.longValue() < 0) {
failed(new EOFException(), null);
} elseif (buffersArrayHasRemaining(buffers, 0, buffers.length)) {
getSocket().write(buffers, 0, buffers.length, toTimeout(getWriteTimeout()),
TimeUnit.MILLISECONDS, buffers, this);
} else { // Continue until everything is written
process();
}
}
@Override publicvoid failed(Throwable exc, ByteBuffer[] buffers) {
completion.failed(exc, Nio2OperationState.this);
}
}); return;
}
}
}
getSocket().write(buffers, offset, length, timeout, unit, this, completion);
}
}
}
/* Callers of this method must: * - have acquired the readPending semaphore * - have acquired a lock on readCompletionHandler * * This method will release (or arrange for the release of) the * readPending semaphore once the read has completed.
*/ privateint fillReadBuffer(boolean block) throws IOException {
socketBufferHandler.configureReadBufferForWrite(); return fillReadBuffer(block, socketBufferHandler.getReadBuffer());
}
privateint fillReadBuffer(boolean block, ByteBuffer to) throws IOException { int nRead = 0;
Future<Integer> integer = null; if (block) { try {
integer = getSocket().read(to); long timeout = getReadTimeout(); if (timeout > 0) {
nRead = integer.get(timeout, TimeUnit.MILLISECONDS).intValue();
} else {
nRead = integer.get().intValue();
}
} catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException) e.getCause();
} else { thrownew IOException(e);
}
} catch (InterruptedException e) { thrownew IOException(e);
} catch (TimeoutException e) {
integer.cancel(true); thrownew SocketTimeoutException();
} finally { // Blocking read so need to release here since there will // not be a callback to a completion handler.
readPending.release();
}
} else {
startInline();
getSocket().read(to, toTimeout(getReadTimeout()), TimeUnit.MILLISECONDS, to,
readCompletionHandler);
endInline(); if (readPending.availablePermits() == 1) {
nRead = to.position();
}
} return nRead;
}
/** * {@inheritDoc} * <p> * Overridden for NIO2 to enable a gathering write to be used to write * all of the remaining data in a single additional write should a * non-blocking write leave data in the buffer.
*/
@Override protectedvoid writeNonBlocking(byte[] buf, int off, int len) throws IOException { // Note: Possible alternate behavior: // If there's non blocking abuse (like a test writing 1MB in a single // "non blocking" write), then block until the previous write is // done rather than continue buffering // Also allows doing autoblocking // Could be "smart" with coordination with the main CoyoteOutputStream to // indicate the end of a write // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS)) synchronized (writeCompletionHandler) {
checkError(); if (writeNotify || writePending.tryAcquire()) { // No pending completion handler, so writing to the main buffer // is possible
socketBufferHandler.configureWriteBufferForWrite(); int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer());
len = len - thisTime;
off = off + thisTime; if (len > 0) { // Remaining data must be buffered
nonBlockingWriteBuffer.add(buf, off, len);
}
flushNonBlockingInternal(true);
} else {
nonBlockingWriteBuffer.add(buf, off, len);
}
}
}
/** * {@inheritDoc} * <p> * Overridden for NIO2 to enable a gathering write to be used to write * all of the remaining data in a single additional write should a * non-blocking write leave data in the buffer.
*/
@Override protectedvoid writeNonBlocking(ByteBuffer from) throws IOException {
writeNonBlockingInternal(from);
}
/** * {@inheritDoc} * <p> * Overridden for NIO2 to enable a gathering write to be used to write * all of the remaining data in a single additional write should a * non-blocking write leave data in the buffer.
*/
@Override protectedvoid writeNonBlockingInternal(ByteBuffer from) throws IOException { // Note: Possible alternate behavior: // If there's non blocking abuse (like a test writing 1MB in a single // "non blocking" write), then block until the previous write is // done rather than continue buffering // Also allows doing autoblocking // Could be "smart" with coordination with the main CoyoteOutputStream to // indicate the end of a write // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS)) synchronized (writeCompletionHandler) {
checkError(); if (writeNotify || writePending.tryAcquire()) { // No pending completion handler, so writing to the main buffer // is possible
socketBufferHandler.configureWriteBufferForWrite();
transfer(from, socketBufferHandler.getWriteBuffer()); if (from.remaining() > 0) { // Remaining data must be buffered
nonBlockingWriteBuffer.add(from);
}
flushNonBlockingInternal(true);
} else {
nonBlockingWriteBuffer.add(from);
}
}
}
/** * @param block Ignored since this method is only called in the * blocking case
*/
@Override protectedvoid doWrite(boolean block, ByteBuffer from) throws IOException {
Future<Integer> integer = null; try { do {
integer = getSocket().write(from); long timeout = getWriteTimeout(); if (timeout > 0) { if (integer.get(timeout, TimeUnit.MILLISECONDS).intValue() < 0) { thrownew EOFException(sm.getString("iob.failedwrite"));
}
} else { if (integer.get().intValue() < 0) { thrownew EOFException(sm.getString("iob.failedwrite"));
}
}
} while (from.hasRemaining());
} catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException) e.getCause();
} else { thrownew IOException(e);
}
} catch (InterruptedException e) { thrownew IOException(e);
} catch (TimeoutException e) {
integer.cancel(true); thrownew SocketTimeoutException();
}
}
// Before doing a blocking flush, make sure that any pending non // blocking write has completed. try { if (writePending.tryAcquire(toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS)) {
writePending.release();
} else { thrownew SocketTimeoutException();
}
} catch (InterruptedException e) { // Ignore
}
@Override publicvoid registerReadInterest() { synchronized (readCompletionHandler) { // A notification is already being sent if (readNotify) { return;
} if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.debug.registerRead", this));
}
readInterest = true; if (readPending.tryAcquire()) { // No read pending, so do a read try { if (fillReadBuffer(false) > 0) { // Special case where the read completed inline, there is no notification // in that case so it has to be done here if (!getEndpoint().processSocket(this, SocketEvent.OPEN_READ, true)) {
close();
}
}
} catch (IOException e) { // Will never happen
setError(e);
}
}
}
}
@Override publicvoid registerWriteInterest() { synchronized (writeCompletionHandler) { // A notification is already being sent if (writeNotify) { return;
} if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.debug.registerWrite", this));
}
writeInterest = true; if (writePending.availablePermits() == 1) { // If no write is pending, notify that writing is possible if (!getEndpoint().processSocket(this, SocketEvent.OPEN_WRITE, true)) {
close();
}
}
}
}
@Override public SendfileDataBase createSendfileData(String filename, long pos, long length) { returnnew SendfileData(filename, pos, length);
}
@Override public SendfileState processSendfile(SendfileDataBase sendfileData) {
SendfileData data = (SendfileData) sendfileData;
setSendfileData(data); // Configure the send file data if (data.fchannel == null || !data.fchannel.isOpen()) {
java.nio.file.Path path = new File(sendfileData.fileName).toPath(); try {
data.fchannel = FileChannel.open(path, StandardOpenOption.READ).position(sendfileData.pos);
} catch (IOException e) { return SendfileState.ERROR;
}
}
getSocket().getBufHandler().configureWriteBufferForWrite();
ByteBuffer buffer = getSocket().getBufHandler().getWriteBuffer(); int nRead = -1; try {
nRead = data.fchannel.read(buffer);
} catch (IOException e1) { return SendfileState.ERROR;
}
publicstaticboolean isInline() { Boolean flag = inlineCompletion.get(); if (flag == null) { returnfalse;
} else { return flag.booleanValue();
}
}
// ---------------------------------------------- SocketProcessor Inner Class /** * This class is the equivalent of the Worker, but will simply use in an * external Executor thread pool.
*/ protectedclass SocketProcessor extends SocketProcessorBase<Nio2Channel> {
public SocketProcessor(SocketWrapperBase<Nio2Channel> socketWrapper, SocketEvent event) { super(socketWrapper, event);
}
try { if (socketWrapper.getSocket().isHandshakeComplete()) { // No TLS handshaking required. Let the handler // process this socket / event combination.
handshake = 0;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.