服务端NioServerSocketChannel
客户端NioSocketChannel
public class NioServerSocketChannel extends AbstractNioMessageServerChannel implements ServerSocketChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
private final ServerSocketChannelConfig config = new DefaultServerSocketChannelConfig(this, this.javaChannel().socket());
//静态方法调用jdk下的NIO生成ServerSocketChannel
private static java.nio.channels.ServerSocketChannel newSocket() {
try {
return java.nio.channels.ServerSocketChannel.open();
} catch (IOException var1) {
throw new ChannelException("Failed to open a server socket.", var1);
}
}
public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childGroup) {
super((Channel)null, eventLoop, childGroup, newSocket(), 16);
}
public InetSocketAddress localAddress() {
return (InetSocketAddress)super.localAddress();
}
public ChannelMetadata metadata() {
return METADATA;
}
public ServerSocketChannelConfig config() {
return this.config;
}
public boolean isActive() {
return this.javaChannel().socket().isBound();
}
public InetSocketAddress remoteAddress() {
return null;
}
protected java.nio.channels.ServerSocketChannel javaChannel() {
return (java.nio.channels.ServerSocketChannel)super.javaChannel();
}
protected SocketAddress localAddress0() {
return this.javaChannel().socket().getLocalSocketAddress();
}
protected void doBind(SocketAddress localAddress) throws Exception {
this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
}
protected void doClose() throws Exception {
this.javaChannel().close();
}
protected int doReadMessages(List<Object> buf) throws Exception {
//接收新的客户端连接
SocketChannel ch = this.javaChannel().accept();
try {
if (ch != null) {//SocketChannel不空,则利用当前的NioServerSocketChannel、EventLoop和SocketChannel创建新的NioSocketChannel,并加入List
buf.add(new NioSocketChannel(this, this.childEventLoopGroup().next(), ch));
return 1;
}
} catch (Throwable var6) {
logger.warn("Failed to create a new channel from an accepted socket.", var6);
try {
ch.close();
} catch (Throwable var5) {
logger.warn("Failed to close a socket.", var5);
}
}
return 0;
}
//与服务端接口无关,如果误调则抛出异常
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
throw new UnsupportedOperationException();
}
protected void doFinishConnect() throws Exception {
throw new UnsupportedOperationException();
}
protected SocketAddress remoteAddress0() {
return null;
}
protected void doDisconnect() throws Exception {
throw new UnsupportedOperationException();
}
protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
throw new UnsupportedOperationException();
}
}
先分析连接操作
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) {//如果本地Socket地址不为空,则调用jdk下的NIO socket.bind()方法绑定本地地址
this.javaChannel().socket().bind(localAddress);
}
boolean success = false;
boolean var5;
try {//如果绑定成功,发起TCP连接
boolean connected = this.javaChannel().connect(remoteAddress);
if (!connected) {//连接不成功
this.selectionKey().interestOps(8);//连接失败则将NioSocketChannel中的selectionKet设置为OP_CONNECT
}
//连接成功
success = true;
var5 = connected;
} finally {
if (!success) {
this.doClose();
}
}
return var5;
}
继续分析写操作
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
while(true) {
//获取待发送的ByteBuf个数,如果小于等于1,则调用父类的doWrite()方法之后退出
int msgCount = in.size();
if (msgCount <= 1) {
super.doWrite(in);
return;
}
//批量发送缓冲区的消息之前,先对一系列的局部变量进行赋值
ByteBuffer[] nioBuffers = in.nioBuffers();
if (nioBuffers == null) {
super.doWrite(in);
return;
}
int nioBufferCnt = in.nioBufferCount();//获取需要发送的ByteBuffer数组个数
long expectedWrittenBytes = in.nioBufferSize();//获取需要发送的总字节数
java.nio.channels.SocketChannel ch = this.javaChannel();//从NioSocketChannel中获取NIO的SocketChannel
long writtenBytes = 0L;
boolean done = false;//将是否发送完成标识设置为false
boolean setOpWrite = false;//将是否有写半包标识设置为false
int i;
for(i = this.config().getWriteSpinCount() - 1; i >= 0; --i) {
//三个参数分别是需要发送的ByteBuffer数组,数组的偏移量,待发送的ByteChannel个数,返回值写入是SocketChannel的个数
long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
if (localWrittenBytes == 0L) {//如果写入字节为0,说明TCP发送缓冲区已满,此时很有可能无法再写进去
//将写半包设置为true,并跳出循环,用于向多路注册复用器注册写操作,告诉多路复用器有没发完的半包消息
//需要轮询出就绪的SocketChannel继续发送
setOpWrite = true;
break;
}
//发送完成后进行两个计算:
//需要发送的字节数减去已经发送的字节数
//发送的字节数 + 已经发送的字节数
expectedWrittenBytes -= localWrittenBytes;
writtenBytes += localWrittenBytes;
//判断缓冲区中所有的消息是否已经发送完成
//如果是,则把发送标识设置为true同时跳出循环
//如果没有发送完成,则继续循环
if (expectedWrittenBytes == 0L) {
done = true;
break;
}
}
//从循环发送中退出之后,首先对发送完成标识done进行判断
//如果发送成功,则循环释放已经发送的消息
if (done) {
for(i = msgCount; i > 0; --i) {
in.remove();
}
if (!in.isEmpty()) {
continue;
}
//环形数组的发送缓冲区释放完成后,取消半包标识,告诉多路复用器已经全部发送完成
this.clearOpWrite();
} else {//缓冲区的消息没有发送完成,出现了"写半包"
for(i = msgCount; i > 0; --i) {//循环遍历发送缓冲区,对消息的发送结果进行判断
ByteBuf buf = (ByteBuf)in.current();//从ChannelOutboundBuffer弹出第一条发送的ByteBuffer,获取其索引和字节数
int readerIndex = buf.readerIndex();//可读的字节数,这是不变的
int readableBytes = buf.writerIndex() - readerIndex;//发送的字节数,这是随着发送会增加的
if ((long)readableBytes >= writtenBytes) {
//如果可读的消息大于已经发送的总字节数,说明这条消息没有被完整地发送出去,出现了"写半包"
//需要更新可读的索引为当前索引加上已经发送的总字节数
if ((long)readableBytes > writtenBytes) {
buf.readerIndex(readerIndex + (int)writtenBytes);
in.progress(writtenBytes);
} else {如果可读的消息等于已经发送的总字节数,说明最后一次发送的消息是个整包消息,没有剩余的半包消息要发送
in.progress((long)readableBytes);
in.remove();
}
break;
}
//如果可读的消息小于已经发送的总字节数,说明当前的ByteBuf已经被完全发送出去了,将已经发送的ByteBuf删除,最后,发送的字节数要减去第一条发送的字节数,得到后续消息发送的总字节数,然后继续循环判断第二条消息...
in.progress((long)readableBytes);
in.remove();
writtenBytes -= (long)readableBytes;
}
this.incompleteWrite(setOpWrite);
}
return;
}
}
Netty的ChannelPipeline和ChannelHandler机制类似于Servlet和Filter过滤器,这类拦截器实际上是职责链模式的一种变形,主要是为了方便事件的拦截和用户业务逻辑的定制。
Netty将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipeline中流动和传递。ChannelPipeline持有IO事件拦截器ChannelHandler的链表,由ChannelHandler对IO事件进行拦截和处理。
Netty中的事件分为inbound事件和outbound事件。inbound事件通常由IO线程触发,例如TCP链路建立事件、链路关闭事件、读事件、异常通知事件等;outbound事件通常是由用户主动发起的网络IO操作等。
使用ServerBootstrap或者Bootstrap启动服务端或者客户端时,Netty会为每个Channel连接创建一个独立的Pipeline
ChannelHander类似Servlet的Filter过滤器,负责对IO事件或者IO操作进行拦截和处理。
Netty提供接口ChannelHandlerAdapter基类,它的所有接口实现都是透传,如果用户ChannelHandler关系某个事件,只需要覆盖ChannelHandlerAdapter对应的方法即可。
利用NIO进行网络编程时,往往需要将读取到的字节数或者字节缓冲区解码为业务可以使用的POJO对象。Netty提供了ByteToMessageDecoder抽象工具解码类。用户的解码器继承ByteToMessageDecoder,只需要实现decode()方法,即可完成ByteBuf到POJO对象的解码。
不过ByteToMessageDecoder没有考虑TCP粘包和组包等场景,读半包需要用户自己处理,因此我们可以继承更高级的解码器进行半包处理。
MessageToMessageDecoder实际上是Nety的二次解码器,从SocketChannel读取到的TCP数据报是ByteBuffer,先将解码为Java对象,再二次解码为POJO对象,因此称之为二次解码器。
以HTTP+XML协议栈为例,第一次解码是将字节数组解码成HttpRequest对象,然后对HttpRequest消息中的消息体字符串进行二次解码,将XML格式的字符串解码为POJO对象。
由于二次解码器是将一个POJO解码为另一个POJO,一般不涉及半包处理。
MessageToByteEncoder负责将POJO对象编码成ByteBuf
将一个POJO对象编码成另一个对象