前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java nio 在windows的实现

java nio 在windows的实现

作者头像
平凡的学生族
发布2019-05-25 09:27:55
1.6K0
发布2019-05-25 09:27:55
举报
文章被收录于专栏:后端技术后端技术

0. 结论

先说结论吧。

  • jdk8和以前,java nio的windows实现,在底层是基于winsock2的select
  • 但是winsock2的select是否是基于轮询的,是不是我们常说的select/poll/epoll中的select,我无法查证,毕竟windows不是开源的。如果是轮询,那效率是相当低的。所以说windows就这点不好>_<。
  • 一次select可返回的最大数量是1024。

1. 在windows上的实现

参考java nio 在windows上的实现 很多人说是IOCP,其实是select。 首先我们一步步查看调用链: Selector.select->...->WindowsSelectorImpl.doSelect->WindowsSelectorImpl.SubSelector.poll->WindowsSelectorImpl.SubSelector.poll0 我们看下WindowsSelectorImpl.SubSelector.poll:

代码语言:javascript
复制
private int poll() throws IOException {
  return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}

然后再看最后调用的WindowsSelectorImpl.SubSelector.poll0:

代码语言:javascript
复制
private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);

该函数是一个native方法,其源码需要到jdk源码中查看。但jdk不是开源的,所以native方法实现只能在openJdk中找。

参考OpenJDK与JDK的区别分析,openJdk只是在jdk上加了少许不痛不痒的功能而已,关于WindowsSelectorImpl的实现应该是一样的。

接下来我们找到openJdk的源码,查看winsock2$poll0

2. WindowsSelectorImpl

我们找到openJdk8的源码jdk8 WindowsSelectorImpl.c.shtml

2.1 调用winsock2$select

参考:

在WindowsSelectorImpl$poll0函数中我们看到:

代码语言:javascript
复制
if ((result = select(0 , &readfds, &writefds, &exceptfds, tv))
                                                             == SOCKET_ERROR)

所以调用的是winsock2的select方法,阻塞监听触发read/write事件的fd(这里的fd就是指socket)。激活读写事件的socket会保存在readfds和writefds中。 所以:

  • jdk8和以前,java nio的windows实现,在底层是基于winsock2的select
  • 但是winsock2的select是否是基于轮询的,是不是我们常说的select/poll/epoll中的select,我无法查证,毕竟windows不是开源的。如果是轮询,那效率是相当低的。所以说windows就这点不好>_<。

2.2 设置上层调用的返回值

之后有如下代码:

代码语言:javascript
复制
resultbuf[0] = readfds.fd_count;
for (i = 0; i < (int)readfds.fd_count; i++) {
   resultbuf[i + 1] = (int)readfds.fd_array[i];
}
(*env)->SetIntArrayRegion(env, returnReadFds, 0, readfds.fd_count + 1, resultbuf);

该段代码的作用,是把激活读事件的socket拷贝至returnReadFds中,作为上层调用的返回值。 附近格式类似的代码功能也一样,都是把激活read/write事件的socket拷贝到returnReadFds/returnWriteFds中,作为上层调用的返回值。如果有异常发生,则returnExceptFds也会被正确地被赋值。

2.3 selector的最大监听数

根据上一节,我们发现在poll0调用中,会传入参数this.readFds, this.writeFds, this.exceptFds,以便poll0设置返回值:

代码语言:javascript
复制
private int poll() throws IOException {
  return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}

其中this.readFds是成员函数,我们看到定义:

代码语言:javascript
复制
 private final class SubSelector {
        private final int pollArrayIndex;
        private final int[] readFds;
        private final int[] writeFds;
        private final int[] exceptFds;

以及其构造函数:

可以发现,readFds、writeFds、exceptFds都被赋予了固定长度的数组, 而且这个变量并没有再变换过。 readFds、writeFds、exceptFds的作用是接收触发相应事件的socket。 所以我认为,在windows中,selector一次监听返回的最大激活数目不能超过1024。 不过在winsock2.h中,我看到:

代码语言:javascript
复制
typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

其中fd_set是returnReadFds、returnWriteFds的类型,也就是说fd_set是返回值参数的类型。 然后FD_SETSIZE的长度:

代码语言:javascript
复制
 #ifndef FD_SETSIZE
 #define  FD_SETSIZE      64 
 #endif  /* FD_SETSIZE */

意思非常明显,也就是说winsock默认最多处理64个sock。也可以通过#define修改FD_SETSIZE为想要的值。 我们回看jdk8的WindowsSelectorImpl.c,在开头就有一句:

代码语言:javascript
复制
#define FD_SETSIZE 1024

说明openJdk将这个限制改成了1024,与上文的数值一致。最后,我们再一次确认了这个限制是1024。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.04.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 结论
  • 1. 在windows上的实现
  • 2. WindowsSelectorImpl
    • 2.1 调用winsock2$select
      • 2.2 设置上层调用的返回值
        • 2.3 selector的最大监听数
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档