netty源码阅读之客户端新连接之创建NioSocketChannel

超、凢脫俗 2022-05-17 12:19 270阅读 0赞

创建NioSocketChannel其实和创建服务端的NioServerSocketChannel类似,从上一篇文章的new NioSocketChannel(this, ch)这里进入,主要做了两件事:

1、调用父类构造函数AbstractNioByteChannel(p,ch,op_read)

1)设置configureBlocking(false),并且把传进来的OP_READ事件保存起来(保存了还没有注册)

2)create id,unsafe,pipeline

2、新建new NioSocketChannelConfig()

主要是调用了这个函数setTcpNoDelay(true)

源码如下:

  1. public NioSocketChannel(Channel parent, SocketChannel socket) {
  2. super(parent, socket);
  3. config = new NioSocketChannelConfig(this, socket.socket());
  4. }

是不是和创建服务端NioServerSocketChannel很像呢?

在这里,和服务端不同的是,客户端是直接调用new产生NioSocketChannel,而服务端是通过反射的方式(为什么会是这样设计呢?new出来的对象我们无法访问其中的私有属性,但是通过反射出来的对象我们可以通过setAccessible()方法来访问其中的私有属性,前面我们要优化keySet,所以需要用到反射获取一些field?参考)。

一、调用父类构造函数AbstractNioByteChannel(p,ch,op_read)

先看调用父类的构造方法:

  1. protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
  2. super(parent, ch, SelectionKey.OP_READ);
  3. }

这里注意了,直接产生了一个OP_READ事件进去。因为这个是客户端的,所以客户端只用读事件,如果是服务端,那就是OP_ACCEPT事件。然后继续进入:

  1. protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
  2. super(parent);
  3. this.ch = ch;
  4. this.readInterestOp = readInterestOp;
  5. try {
  6. ch.configureBlocking(false);
  7. } catch (IOException e) {
  8. try {
  9. ch.close();
  10. } catch (IOException e2) {
  11. if (logger.isWarnEnabled()) {
  12. logger.warn(
  13. "Failed to close a partially initialized socket.", e2);
  14. }
  15. }
  16. throw new ChannelException("Failed to enter non-blocking mode.", e);
  17. }
  18. }

this.ch = ch(这个ch就是之前我们javaChannel.accept()得到的channel);把我们之前创建的jdk的socketChannel绑定到我们的这个服务端SocketChannel这里,并且把刚刚创建的readInterestOps(对读事件感兴趣)事件也传入了进来。

下面有一个ch.configureBlocking(false)就是设置非阻塞模式。

我们还要继续从super(parent);进入:

  1. protected AbstractChannel(Channel parent) {
  2. this.parent = parent;
  3. id = newId();
  4. unsafe = newUnsafe();
  5. pipeline = newChannelPipeline();
  6. }

如果是客户端的NioSocketChannel,会有一个parent,就是我们服务端NioServerSocketChannel

id不用说了,这个unsafe就是NioSocketChannel用来负责处理底层该数据的读写。NioServerSocketChannel的实现是NioMessageUnsafe,而NioSocketChannel的实现是NioByteUnsafe,因为服务端处理的事件是连接时间,而客户端处理的是读事件,所以有所不同。

pipeline就是处理逻辑链,后面的文章会继续深入。

二、新建new NioSocketChannelConfig()

这个congfigure就是设置tcp的一些连接参数。

从这个地方config = new NioSocketChannelConfig(this, socket.socket());进入:

  1. public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
  2. super(channel);
  3. if (javaSocket == null) {
  4. throw new NullPointerException("javaSocket");
  5. }
  6. this.javaSocket = javaSocket;
  7. // Enable TCP_NODELAY by default if possible.
  8. if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
  9. try {
  10. setTcpNoDelay(true);
  11. } catch (Exception e) {
  12. // Ignore.
  13. }
  14. }
  15. }

找到setTcpNoDelay这个方法进去

  1. @Override
  2. public SocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
  3. try {
  4. javaSocket.setTcpNoDelay(tcpNoDelay);
  5. } catch (SocketException e) {
  6. throw new ChannelException(e);
  7. }
  8. return this;
  9. }

这里就是调用jdk底层不使用Nagle算法。关于这个算法:为了提高吞吐量,把小数据包集合成大数据包一起发过去。

但是这样会提高延时,netty默认设置了禁用此算法,可以降低延时。

看这个方法PlatformDependent.canEnableTcpNoDelayByDefault()的实现:

  1. public static boolean canEnableTcpNoDelayByDefault() {
  2. return CAN_ENABLE_TCP_NODELAY_BY_DEFAULT;
  3. }
  4. private static final boolean CAN_ENABLE_TCP_NODELAY_BY_DEFAULT = !isAndroid();

也就是,当不是安卓的时候,就设置优化。另外一个细节就是,netty居然也支持安卓。

关于Nagle算法

发表评论

表情:
评论列表 (有 0 条评论,270人围观)

还没有评论,来说两句吧...

相关阅读