netty源码之channel初始化

netty源码之channel初始化

入口说明

	ChannelFuture future =  server.bind(port).sync();
	AbstractBootstrap#bind(int inetPort)
	AbstractBootstrap#bind(SocketAddress localAddress)
	AbstractBootstrap#doBind(final SocketAddress localAddress)
	AbstractBootstrap#initAndRegister(){
		//本文主要分析这一行代码
		channel = channelFactory.newChannel();
	}

channel初始化

channel = channelFactory.newChannel();这行代码实际是反射调用NioServerSocketChannel的默认构造,如下

	//provider
	private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
	//无参
	public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    //有参
	public NioServerSocketChannel(ServerSocketChannel channel) {
        //只接受accept事件,设置非阻塞
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }
	//核心,jni调用scoket0
	private static ServerSocketChannel newSocket(SelectorProvider provider) {
  		//https://github.com/netty/netty/issues/2308,issue主要就是说SelectorProvider.provider()要加锁,所以缓存起来		
  		//核心就是这行代码
        return provider.openServerSocketChannel();
    }

provider.openServerSocketChannel();核心代码会执行到如下代码

ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
		//传到父类
        super(sp);
        //核心代码
        this.fd =  Net.serverSocket(true);
        //jni获取文件描述符的值
        this.fdVal = IOUtil.fdVal(fd);
        //设置状态正在使用
        this.state = ST_INUSE;
    }

	//核心代码调用这里
	static FileDescriptor serverSocket(boolean stream) {
		//socket0返回一个int类型的文件描述符,newFD把他包装成一个java文件描述符对象
		//在linux上一切皆文件,scoket也不例外
        return IOUtil.newFD(socket0(isIPv6Available(), stream, true, fastLoopback));
    }

    // Due to oddities SO_REUSEADDR on windows reuse is ignored
    //ipv6, tcp|udp, 是否复用处于四次挥手TimeWait状态的连接
    //fastLoopback是 Windows 特有的一项优化,可以在内核 IP 解析前拦截本地环回地址(127.0.0.1)的调用,
    //加快本地调用速度,这个值在 Linux 的实现中已被忽略
    private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse,
                                      boolean fastLoopback);

下面我们看看socket0的c语言实现

setsockopt

JNIEXPORT int JNICALL
//前两个参数是jni调用必须的参数,利用这两个参数可以实现c代码调用java代码,或者设置java类的某个属性
//后四个参数解释过了
Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
                            jboolean stream, jboolean reuse, jboolean ignored)
{	
	//最后返回的文件描述符
    int fd;
    //tcp还是udp
    int type = (stream ? SOCK_STREAM : SOCK_DGRAM);



    //宏AF_INET6存在的话
#ifdef AF_INET6
	//ipv6还是ipv4
    int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET;
    //否则
#else
    int domain = AF_INET;
#endif //if结束
	//指定网络层和传输层协议,第三个参数固定0,具体为什么可以执行man命令查阅linux手册
    fd = socket(domain, type, 0);
    //文件描述符不会小于0,通常0是标准输入,1是标准输出,2是标准出错
    if (fd < 0) {
        return handleSocketError(env, errno);
    }
    

//宏AF_INET6存在的话
#ifdef AF_INET6
    /* Disable IPV6_V6ONLY to ensure dual-socket support */
    if (domain == AF_INET6) {
        int arg = 0;
        //setsockopt设置属性
        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
                       sizeof(int)) < 0) {
            JNU_ThrowByNameWithLastError(env,
                                         JNU_JAVANETPKG "SocketException",
                                         "Unable to set IPV6_V6ONLY");
            close(fd);
            return -1;
        }
    }
#endif
	
	//复用TimeWait,设置SO_REUSEADDR 属性
    if (reuse) {
        int arg = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
                       sizeof(arg)) < 0) {
            JNU_ThrowByNameWithLastError(env,
                                         JNU_JAVANETPKG "SocketException",
                                         "Unable to set SO_REUSEADDR");
            close(fd);
            return -1;
        }
    }
    
    return fd;
}

如若不理解socket的话可以借鉴笔者并发编程里关于c语言和io模型的文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值