EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//ServerBootstrap,用于配置服务端,一般为ServerSocket通道
ServerBootstrap serverBoot = new ServerBootstrap();
serverBoot.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//Bootstrap,用于配置客户端,一般为Socket通道
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
/**
* A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
* NIO selector based implementation to accept new connections.
基于nio选择器的服务端socket通道的实现,用于接受新的连接
*/
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
/**
* {@link AbstractNioChannel} base class for {@link Channel}s that operate on messages.
*/
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
/**
* Abstract base class for {@link Channel} implementations which use a Selector based approach.
*/
public abstract class AbstractNioChannel extends AbstractChannel {
/**
* A skeletal {@link Channel} implementation.
*/
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable {
/**
* A TCP/IP {@link ServerChannel} which accepts incoming TCP/IP connections.
*/
public interface ServerSocketChannel extends ServerChannel {
/**
* A {@link Channel} that accepts an incoming connection attempt and creates
* its child {@link Channel}s by accepting them. {@link ServerSocketChannel} is
* a good example.
*/
public interface ServerChannel extends Channel {
/**
* {@link io.netty.channel.socket.SocketChannel} which uses NIO selector based implementation.
基于nio选择器的socket通道
*/
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
/**
* {@link AbstractNioChannel} base class for {@link Channel}s that operate on bytes.
抽象nio字节通道
*/
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
再来看另外一个分支SocketChannel:
/**
* A TCP/IP socket {@link Channel}.
*/
public interface SocketChannel extends DuplexChannel {
/**
* A duplex {@link Channel} that has two sides that can be shutdown independently.
*/
public interface DuplexChannel extends Channel {
package io.netty.channel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.AttributeMap;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
/**
* A nexus to a network socket or a component which is capable of I/O
* operations such as read, write, connect, and bind.
*
* A channel provides a user:
* [list]
* [*]the current state of the channel (e.g. is it open? is it connected?),
* [*]the {@linkplain ChannelConfig configuration parameters} of the channel (e.g. receive buffer size),
* [*]the I/O operations that the channel supports (e.g. read, write, connect, and bind), and
* the {@link ChannelPipeline} which handles all I/O events and requests
* associated with the channel.
* [/list]
*
* All I/O operations are asynchronous.
*
* All I/O operations in Netty are asynchronous. It means any I/O calls will
* return immediately with no guarantee that the requested I/O operation has
* been completed at the end of the call. Instead, you will be returned with
* a {@link ChannelFuture} instance which will notify you when the requested I/O
* operation has succeeded, failed, or canceled.
*
*
Channels are hierarchical
*
* A {@link Channel} can have a {@linkplain #parent() parent} depending on
* how it was created. For instance, a {@link SocketChannel}, that was accepted
* by {@link ServerSocketChannel}, will return the {@link ServerSocketChannel}
* as its parent on {@link #parent()}.
*
* The semantics of the hierarchical structure depends on the transport
* implementation where the {@link Channel} belongs to. For example, you could
* write a new {@link Channel} implementation that creates the sub-channels that
* share one socket connection, as [url=http://beepcore.org/]BEEP[/url] and
* [url=http://en.wikipedia.org/wiki/Secure_Shell]SSH[/url] do.
*
*
Downcast to access transport-specific operations
*
* Some transports exposes additional operations that is specific to the
* transport. Down-cast the {@link Channel} to sub-type to invoke such
* operations. For example, with the old I/O datagram transport, multicast
* join / leave operations are provided by {@link DatagramChannel}.
*
*
Release resources
*
* It is important to call {@link #close()} or {@link #close(ChannelPromise)} to release all
* resources once you are done with the {@link Channel}. This ensures all resources are
* released in a proper way, i.e. filehandles.
*/
public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable {
/**
* Returns the globally unique identifier of this {@link Channel}.
返回通道全局唯一id
*/
ChannelId id();
/**
* Return the {@link EventLoop} this {@link Channel} was registered to.
返回通道注册的事件循环
*/
EventLoop eventLoop();
/**
* Returns the parent of this channel.
* 返回所属通道
* @return the parent channel.
* {@code null} if this channel does not have a parent channel.
*/
Channel parent();
/**
* Returns the configuration of this channel.
返回通道配置
*/
ChannelConfig config();
/**
* Returns {@code true} if the {@link Channel} is open and may get active later
判断通道是否打开
*/
boolean isOpen();
/**
* Returns {@code true} if the {@link Channel} is registered with an {@link EventLoop}.
判断通道是否注册到事件循环
*/
boolean isRegistered();
/**
* Return {@code true} if the {@link Channel} is active and so connected.
判断当前同时是否激活,及连接
*/
boolean isActive();
/**
* Return the {@link ChannelMetadata} of the {@link Channel} which describe the nature of the {@link Channel}.
返回通道元数据
*/
ChannelMetadata metadata();
/**
* Returns the local address where this channel is bound to. The returned
* {@link SocketAddress} is supposed to be down-cast into more concrete
* type such as {@link InetSocketAddress} to retrieve the detailed
* information.
* 返回通道绑定的socket地址
* @return the local address of this channel.
* {@code null} if this channel is not bound.
*/
SocketAddress localAddress();
/**
* Returns the remote address where this channel is connected to. The
* returned {@link SocketAddress} is supposed to be down-cast into more
* concrete type such as {@link InetSocketAddress} to retrieve the detailed
* information.
*返回远端socket地址。如果没有连接,返回null。对于报文通道,返回null
* @return the remote address of this channel.
* {@code null} if this channel is not connected.
* If this channel is not connected but it can receive messages
* from arbitrary remote addresses (e.g. {@link DatagramChannel},
* use {@link DatagramPacket#recipient()} to determine
* the origination of the received message as this method will
* return {@code null}.
*/
SocketAddress remoteAddress();
/**
* Returns the {@link ChannelFuture} which will be notified when this
* channel is closed. This method always returns the same future instance.
返回通道任务结果,当通道关闭,将会通知通道任务结果。此方总是返回同一任务实例
*/
ChannelFuture closeFuture();
/**
* Returns {@code true} if and only if the I/O thread will perform the
* requested write operation immediately. Any write requests made when
* this method returns {@code false} are queued until the I/O thread is
* ready to process the queued write requests.
当且仅当IO线程立刻自行写请求操作。当方法返回false时,写请求被放在队列中,直到
有IO线程准备处理写请求队列
*/
boolean isWritable();
/**
* Get how many bytes can be written until {@link #isWritable()} returns {@code false}.
* This quantity will always be non-negative. If {@link #isWritable()} is {@code false} then 0.
在判断是否可写方法返回false前,估算有多少字节需要发送,反之的值,总是非负数,如果#isWritable方法,
返回false,则此方法返回0。此方法的作用就是判断在IO线程处理写请求队列时,判断有多少字节要发送。
*/
long bytesBeforeUnwritable();
/**
* Get how many bytes must be drained from underlying buffers until {@link #isWritable()} returns {@code true}.
* This quantity will always be non-negative. If {@link #isWritable()} is {@code true} then 0.
此方法与上述方法相反,用于估算在#isWritable方法返回true前,写请求队列中待发送的字节数
*/
long bytesBeforeWritable();
/**
* Returns an [i]internal-use-only[/i] object that provides unsafe operations.
返回通道内部线程线程安全操作辅助Unsafe
*/
Unsafe unsafe();
/**
* Return the assigned {@link ChannelPipeline}.
返回内部的Channel管道
*/
ChannelPipeline pipeline();
/**
* Return the assigned {@link ByteBufAllocator} which will be used to allocate {@link ByteBuf}s.
返回通道的字节buf分配器
*/
ByteBufAllocator alloc();
//下面两个方法时重写了ChannelOutboundInvoker的方法
@Override
Channel read();
@Override
Channel flush();
/**
* [i]Unsafe[/i] operations that should [i]never[/i] be called from user-code. These methods
* are only provided to implement the actual transport, and must be invoked from an I/O thread except for the
* following methods:
Unsafe不可以,用户代码直接调用,这些方法仅仅能被实际的transport实现,必须被IO线程调用,除了如下的方法
* [list]
* [*]{@link #localAddress()}
* [*]{@link #remoteAddress()}
* [*]{@link #closeForcibly()}
* [*]{@link #register(EventLoop, ChannelPromise)}
* [*]{@link #deregister(ChannelPromise)}
* [*]{@link #voidPromise()}
* [/list]
*/
interface Unsafe {
/**
* Return the assigned {@link RecvByteBufAllocator.Handle} which will be used to allocate {@link ByteBuf}'s when
* receiving data.
返回Unsafe内部的接收字节部分分配器Handle,用于分配接收数据时,存放数据的字节部分。
*/
RecvByteBufAllocator.Handle recvBufAllocHandle();
/**
* Return the {@link SocketAddress} to which is bound local or
* {@code null} if none.
返回绑定socket地址
*/
SocketAddress localAddress();
/**
* Return the {@link SocketAddress} to which is bound remote or
* {@code null} if none is bound yet.
返回远端socket地址,没有绑定,则为null
*/
SocketAddress remoteAddress();
/**
* Register the {@link Channel} of the {@link ChannelPromise} and notify
* the {@link ChannelFuture} once the registration was complete.
注册通道到事件循环,当注册完成时,通知可写任务结果ChannelPromise
*/
void register(EventLoop eventLoop, ChannelPromise promise);
/**
* Bind the {@link SocketAddress} to the {@link Channel} of the {@link ChannelPromise} and notify
* it once its done.
绑定socket地址,绑定完毕,通知可写任务结果ChannelPromise
*/
void bind(SocketAddress localAddress, ChannelPromise promise);
/**
* Connect the {@link Channel} of the given {@link ChannelFuture} with the given remote {@link SocketAddress}.
* If a specific local {@link SocketAddress} should be used it need to be given as argument. Otherwise just
* pass {@code null} to it.
*
* The {@link ChannelPromise} will get notified once the connect operation was complete.
连接远端socket地址,如果需要本地socket地址,则传入,没有则传null
*/
void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
/**
* Disconnect the {@link Channel} of the {@link ChannelFuture} and notify the {@link ChannelPromise} once the
* operation was complete.
断开通道连接,待完成,通知可写任务结果ChannelPromise
*/
void disconnect(ChannelPromise promise);
/**
* Close the {@link Channel} of the {@link ChannelPromise} and notify the {@link ChannelPromise} once the
* operation was complete.
关闭通道,待完成,通知可写任务结果ChannelPromise
*/
void close(ChannelPromise promise);
/**
* Closes the {@link Channel} immediately without firing any events. Probably only useful
* when registration attempt failed.
在不触发任务事件的情况下,关闭通道
*/
void closeForcibly();
/**
* Deregister the {@link Channel} of the {@link ChannelPromise} from {@link EventLoop} and notify the
* {@link ChannelPromise} once the operation was complete.
从事件循环,反注册通道,待完成,通知可写任务结果ChannelPromise
*/
void deregister(ChannelPromise promise);
/**
* Schedules a read operation that fills the inbound buffer of the first {@link ChannelInboundHandler} in the
* {@link ChannelPipeline}. If there's already a pending read operation, this method does nothing.
调度一个读操作,填充字节数据到管道中第一个Inbound处理器的inbound buf中。如果有一个正在读取的操作,则
此方法什么都不做
*/
void beginRead();
/**
* Schedules a write operation.
调度一下写操作
*/
void write(Object msg, ChannelPromise promise);
/**
* Flush out all write operations scheduled via {@link #write(Object, ChannelPromise)}.
通过#write,刷新所有写请求操作
*/
void flush();
/**
* Return a special ChannelPromise which can be reused and passed to the operations in {@link Unsafe}.
* It will never be notified of a success or error and so is only a placeholder for operations
* that take a {@link ChannelPromise} as argument but for which you not want to get notified.
返回一个可以重用和转递Unsafe操作的可选通道任务结果。当操作失败或成功时,不会被通知。
*/
ChannelPromise voidPromise();
/**
* Returns the {@link ChannelOutboundBuffer} of the {@link Channel} where the pending write requests are stored.
返回一个Outbound buf,用于存放写请求。
*/
ChannelOutboundBuffer outboundBuffer();
}
}
package io.netty.channel.socket;
import io.netty.channel.ServerChannel;
import java.net.InetSocketAddress;
/**
* A TCP/IP {@link ServerChannel} which accepts incoming TCP/IP connections.
*/
public interface ServerSocketChannel extends ServerChannel {
@Override
ServerSocketChannelConfig config();
@Override
InetSocketAddress localAddress();
@Override
InetSocketAddress remoteAddress();
}
package io.netty.channel;
import io.netty.channel.socket.ServerSocketChannel;
/**
* A {@link Channel} that accepts an incoming connection attempt and creates
* its child {@link Channel}s by accepting them. {@link ServerSocketChannel} is
* a good example.
*/
public interface ServerChannel extends Channel {
// This is a tag interface.
}
package io.netty.channel.socket;
import io.netty.channel.Channel;
import java.net.InetSocketAddress;
/**
* A TCP/IP socket {@link Channel}.
*/
public interface SocketChannel extends DuplexChannel {
@Override
ServerSocketChannel parent();
@Override
SocketChannelConfig config();
@Override
InetSocketAddress localAddress();
@Override
InetSocketAddress remoteAddress();
}
package io.netty.channel.socket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;
import java.net.Socket;
/**
* A duplex {@link Channel} that has two sides that can be shutdown independently.
*/
public interface DuplexChannel extends Channel {
/**
* Returns {@code true} if and only if the remote peer shut down its output so that no more
* data is received from this channel. Note that the semantic of this method is different from
* that of {@link Socket#shutdownInput()} and {@link Socket#isInputShutdown()}.
当且仅当,远端peer关闭输出流,通道不在有数据要接受时,返回true。需要注意的是,此方法不同于
Socket#shutdownInput和Socket#isInputShutdown方法。
*/
boolean isInputShutdown();
/**
* @see Socket#shutdownInput()
关闭通道socket输入流
*/
ChannelFuture shutdownInput();
/**
* Will shutdown the input and notify {@link ChannelPromise}.
*与上面不同的是,多了一个异步任务结果
* @see Socket#shutdownInput()
*/
ChannelFuture shutdownInput(ChannelPromise promise);
/**
* @see Socket#isOutputShutdown()
判断是否关闭socket输出流
*/
boolean isOutputShutdown();
/**
* @see Socket#shutdownOutput()
关闭socket输出流
*/
ChannelFuture shutdownOutput();
/**
* Will shutdown the output and notify {@link ChannelPromise}.
*与上面不同的是,多了一个异步任务结果
* @see Socket#shutdownOutput()
*/
ChannelFuture shutdownOutput(ChannelPromise promise);
/**
* Determine if both the input and output of this channel have been shutdown.
判断输出流和输入流是否都关闭
*/
boolean isShutdown();
/**
* Will shutdown the input and output sides of this channel.
* @return will be completed when both shutdown operations complete.
关闭通道的输入输出流,完成时,并通知异步任务结果ChannelFuture
*/
ChannelFuture shutdown();
/**
* Will shutdown the input and output sides of this channel.
* @param promise will be completed when both shutdown operations complete.
* @return will be completed when both shutdown operations complete.
关闭通道的输入输出流,完成时,并通知异步可写任务结果ChannelPromise
*/
ChannelFuture shutdown(ChannelPromise promise);
}
package io.netty.channel;
import java.io.Serializable;
/**
* Represents the globally unique identifier of a {@link Channel}.
*
* The identifier is generated from various sources listed in the following:
* [list]
* [*]MAC address (EUI-48 or EUI-64) or the network adapter, preferably a globally unique one,
* [*]the current process ID,
* [*]{@link System#currentTimeMillis()},
* [*]{@link System#nanoTime()},
* [*]a random 32-bit integer, and
* [*]a sequentially incremented 32-bit integer.
* [/list]
*
*
* The global uniqueness of the generated identifier mostly depends on the MAC address and the current process ID,
* which are auto-detected at the class-loading time in best-effort manner. If all attempts to acquire them fail,
* a warning message is logged, and random values will be used instead. Alternatively, you can specify them manually
* via system properties:
* [list]
*
{@code io.netty.machineId} - hexadecimal representation of 48 (or 64) bit integer,
* optionally separated by colon or hyphen.
* [*]{@code io.netty.processId} - an integer between 0 and 65535
* [/list]
*
*/
public interface ChannelId extends Serializable, Comparable {
/**
* Returns the short but globally non-unique string representation of the {@link ChannelId}.
*/
String asShortText();
/**
* Returns the long yet globally unique string representation of the {@link ChannelId}.
*/
String asLongText();
}
package io.netty.channel;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.socket.SocketChannelConfig;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.Map;
/**
* A set of configuration properties of a {@link Channel}.
*
* Please down-cast to more specific configuration type such as
* {@link SocketChannelConfig} or use {@link #setOptions(Map)} to set the
* transport-specific properties:
*
* {@link Channel} ch = ...;
* {@link SocketChannelConfig} cfg = [b]({@link SocketChannelConfig}) ch.getConfig();[/b]
* cfg.setTcpNoDelay(false);
*
*
* Option map
*
* An option map property is a dynamic write-only property which allows
* the configuration of a {@link Channel} without down-casting its associated
* {@link ChannelConfig}. To update an option map, please call {@link #setOptions(Map)}.
*
* All {@link ChannelConfig} has the following options:
*
*
*
* Name Associated setter method
*
* {@link ChannelOption#CONNECT_TIMEOUT_MILLIS} {@link #setConnectTimeoutMillis(int)}
*
* {@link ChannelOption#WRITE_SPIN_COUNT} {@link #setWriteSpinCount(int)}
*
* {@link ChannelOption#WRITE_BUFFER_WATER_MARK} {@link #setWriteBufferWaterMark(WriteBufferWaterMark)}
*
* {@link ChannelOption#ALLOCATOR} {@link #setAllocator(ByteBufAllocator)}
*
* {@link ChannelOption#AUTO_READ} {@link #setAutoRead(boolean)}
*
*
*
* More options are available in the sub-types of {@link ChannelConfig}. For
* example, you can configure the parameters which are specific to a TCP/IP
* socket as explained in {@link SocketChannelConfig}.
*/
public interface ChannelConfig {
/**
* Return all set {@link ChannelOption}'s.
*/
Map, Object> getOptions();
/**
* Sets the configuration properties from the specified {@link Map}.
*/
boolean setOptions(Map, ?> options);
/**
* Return the value of the given {@link ChannelOption}
*/
T getOption(ChannelOption option);
/**
* Sets a configuration property with the specified name and value.
* To override this method properly, you must call the super class:
*
* public boolean setOption(ChannelOption option, T value) {
* if (super.setOption(option, value)) {
* return true;
* }
*
* if (option.equals(additionalOption)) {
* ....
* return true;
* }
*
* return false;
* }
*
*
* @return {@code true} if and only if the property has been set
*/
boolean setOption(ChannelOption option, T value);
/**
* Returns the connect timeout of the channel in milliseconds. If the
* {@link Channel} does not support connect operation, this property is not
* used at all, and therefore will be ignored.
*
* @return the connect timeout in milliseconds. {@code 0} if disabled.
*/
int getConnectTimeoutMillis();
/**
* Sets the connect timeout of the channel in milliseconds. If the
* {@link Channel} does not support connect operation, this property is not
* used at all, and therefore will be ignored.
*
* @param connectTimeoutMillis the connect timeout in milliseconds.
* {@code 0} to disable.
*/
ChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis);
/**
* @deprecated Use {@link MaxMessagesRecvByteBufAllocator}
*
* Returns the maximum number of messages to read per read loop.
* a {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object) channelRead()} event.
* If this value is greater than 1, an event loop might attempt to read multiple times to procure multiple messages.
*/
@Deprecated
int getMaxMessagesPerRead();
/**
* @deprecated Use {@link MaxMessagesRecvByteBufAllocator}
*
* Sets the maximum number of messages to read per read loop.
* If this value is greater than 1, an event loop might attempt to read multiple times to procure multiple messages.
*/
@Deprecated
ChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead);
/**
* Returns the maximum loop count for a write operation until
* {@link WritableByteChannel#write(ByteBuffer)} returns a non-zero value.
* It is similar to what a spin lock is used for in concurrency programming.
* It improves memory utilization and write throughput depending on
* the platform that JVM runs on. The default value is {@code 16}.
*/
int getWriteSpinCount();
/**
* Sets the maximum loop count for a write operation until
* {@link WritableByteChannel#write(ByteBuffer)} returns a non-zero value.
* It is similar to what a spin lock is used for in concurrency programming.
* It improves memory utilization and write throughput depending on
* the platform that JVM runs on. The default value is {@code 16}.
*
* @throws IllegalArgumentException
* if the specified value is {@code 0} or less than {@code 0}
*/
ChannelConfig setWriteSpinCount(int writeSpinCount);
/**
* Returns {@link ByteBufAllocator} which is used for the channel
* to allocate buffers.
*/
ByteBufAllocator getAllocator();
/**
* Set the {@link ByteBufAllocator} which is used for the channel
* to allocate buffers.
*/
ChannelConfig setAllocator(ByteBufAllocator allocator);
/**
* Returns {@link RecvByteBufAllocator} which is used for the channel to allocate receive buffers.
*/
T getRecvByteBufAllocator();
/**
* Set the {@link RecvByteBufAllocator} which is used for the channel to allocate receive buffers.
*/
ChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator);
/**
* Returns {@code true} if and only if {@link ChannelHandlerContext#read()} will be invoked automatically so that
* a user application doesn't need to call it at all. The default value is {@code true}.
*/
boolean isAutoRead();
/**
* Sets if {@link ChannelHandlerContext#read()} will be invoked automatically so that a user application doesn't
* need to call it at all. The default value is {@code true}.
*/
ChannelConfig setAutoRead(boolean autoRead);
/**
* @deprecated Auto close will be removed in a future release.
*
* Returns {@code true} if and only if the {@link Channel} will be closed automatically and immediately on
* write failure. The default is {@code false}.
*/
@Deprecated
boolean isAutoClose();
/**
* @deprecated Auto close will be removed in a future release.
*
* Sets whether the {@link Channel} should be closed automatically and immediately on write failure.
* The default is {@code false}.
*/
@Deprecated
ChannelConfig setAutoClose(boolean autoClose);
/**
* Returns the high water mark of the write buffer. If the number of bytes
* queued in the write buffer exceeds this value, {@link Channel#isWritable()}
* will start to return {@code false}.
*/
int getWriteBufferHighWaterMark();
/**
*
* Sets the high water mark of the write buffer. If the number of bytes
* queued in the write buffer exceeds this value, {@link Channel#isWritable()}
* will start to return {@code false}.
*/
ChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark);
/**
* Returns the low water mark of the write buffer. Once the number of bytes
* queued in the write buffer exceeded the
* {@linkplain #setWriteBufferHighWaterMark(int) high water mark} and then
* dropped down below this value, {@link Channel#isWritable()} will start to return
* {@code true} again.
*/
int getWriteBufferLowWaterMark();
/**
*
* Sets the low water mark of the write buffer. Once the number of bytes
* queued in the write buffer exceeded the
* {@linkplain #setWriteBufferHighWaterMark(int) high water mark} and then
* dropped down below this value, {@link Channel#isWritable()} will start to return
* {@code true} again.
*/
ChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark);
/**
* Returns {@link MessageSizeEstimator} which is used for the channel
* to detect the size of a message.
*/
MessageSizeEstimator getMessageSizeEstimator();
/**
* Set the {@link MessageSizeEstimator} which is used for the channel
* to detect the size of a message.
*/
ChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator);
/**
* Returns the {@link WriteBufferWaterMark} which is used for setting the high and low
* water mark of the write buffer.
*/
WriteBufferWaterMark getWriteBufferWaterMark();
/**
* Set the {@link WriteBufferWaterMark} which is used for setting the high and low
* water mark of the write buffer.
*/
ChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark);
}
package io.netty.channel;
import java.net.SocketAddress;
/**
* Represents the properties of a {@link Channel} implementation.
*/
public final class ChannelMetadata {
private final boolean hasDisconnect;
private final int defaultMaxMessagesPerRead;
/**
* Create a new instance
*
* @param hasDisconnect {@code true} if and only if the channel has the {@code disconnect()} operation
* that allows a user to disconnect and then call {@link Channel#connect(SocketAddress)}
* again, such as UDP/IP.
*/
public ChannelMetadata(boolean hasDisconnect) {
this(hasDisconnect, 1);
}
/**
* Create a new instance
*
* @param hasDisconnect {@code true} if and only if the channel has the {@code disconnect()} operation
* that allows a user to disconnect and then call {@link Channel#connect(SocketAddress)}
* again, such as UDP/IP.
* @param defaultMaxMessagesPerRead If a {@link MaxMessagesRecvByteBufAllocator} is in use, then this value will be
* set for {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead()}. Must be {@code > 0}.
*/
public ChannelMetadata(boolean hasDisconnect, int defaultMaxMessagesPerRead) {
if (defaultMaxMessagesPerRead <= 0) {
throw new IllegalArgumentException("defaultMaxMessagesPerRead: " + defaultMaxMessagesPerRead +
" (expected > 0)");
}
this.hasDisconnect = hasDisconnect;
this.defaultMaxMessagesPerRead = defaultMaxMessagesPerRead;
}
/**
* Returns {@code true} if and only if the channel has the {@code disconnect()} operation
* that allows a user to disconnect and then call {@link Channel#connect(SocketAddress)} again,
* such as UDP/IP.
*/
public boolean hasDisconnect() {
return hasDisconnect;
}
/**
* If a {@link MaxMessagesRecvByteBufAllocator} is in use, then this is the default value for
* {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead()}.
*/
public int defaultMaxMessagesPerRead() {
return defaultMaxMessagesPerRead;
}
}
package io.netty.channel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.Unpooled;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.Recycler;
import io.netty.util.Recycler.Handle;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.PromiseNotificationUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
* (Transport implementors only) an internal data structure used by {@link AbstractChannel} to store its pending
* outbound write requests.
*
* All methods must be called by a transport implementation from an I/O thread, except the following ones:
* [list]
* [*]{@link #size()} and {@link #isEmpty()}
* [*]{@link #isWritable()}
* [*]{@link #getUserDefinedWritability(int)} and {@link #setUserDefinedWritability(int, boolean)}
* [/list]
*
*/
public final class ChannelOutboundBuffer {
// Assuming a 64-bit JVM:
// - 16 bytes object header
// - 8 reference fields
// - 2 long fields
// - 2 int fields
// - 1 boolean field
// - padding
static final int CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD =
SystemPropertyUtil.getInt("io.netty.transport.outboundBufferEntrySizeOverhead", 96);
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelOutboundBuffer.class);
private static final FastThreadLocal NIO_BUFFERS = new FastThreadLocal() {
@Override
protected ByteBuffer[] initialValue() throws Exception {
return new ByteBuffer[1024];
}
};
private final Channel channel;//关联通道
// Entry(flushedEntry) --> ... Entry(unflushedEntry) --> ... Entry(tailEntry)
//
// The Entry that is the first in the linked-list structure that was flushed
private Entry flushedEntry;
// The Entry which is the first unflushed in the linked-list structure
private Entry unflushedEntry;
// The Entry which represents the tail of the buffer
private Entry tailEntry;
// The number of flushed entries that are not written yet
private int flushed;
private int nioBufferCount;
private long nioBufferSize;
private boolean inFail;
private static final AtomicLongFieldUpdater TOTAL_PENDING_SIZE_UPDATER =
AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");
@SuppressWarnings("UnusedDeclaration")
private volatile long totalPendingSize;
private static final AtomicIntegerFieldUpdater UNWRITABLE_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "unwritable");
@SuppressWarnings("UnusedDeclaration")
private volatile int unwritable;
private volatile Runnable fireChannelWritabilityChangedTask;
ChannelOutboundBuffer(AbstractChannel channel) {
this.channel = channel;
}
...
}