Netty:Channel

Channel是客户端和服务端得以传输信息的通道,它维护这套接字或者可进行IO操作的组件。
下面我们以客户端及NioSocketChannel为例,看看他是怎么和socket发生联系的。
当客户端初始化时,首先要进行这样的操作
Bootstrap b = new Bootstrap();
......
b.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true);

通过channel方法去创建通道,流程如下:

首先执行AbstractBootstrap中的channel方法
public B channel(Class<? extends C> channelClass) {
	if (channelClass == null) {
		throw new NullPointerException("channelClass");
	}
	return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

它将Channel的具体类型传递给了ReflectiveChannelFactory类的Class<? extends T> clazz属性,ReflectiveChannelFactory类中实现了ChannelFactory接口的newChannel方法,用来创建Channel实例,后面会用到。

当客户端执行connect动作时,执行Bootstrap的connect代码
public Bootstrap remoteAddress(InetAddress inetHost, int inetPort) {
	remoteAddress = new InetSocketAddress(inetHost, inetPort);
	return this;
}

继而执行doResolveAndConnect方法

private ChannelFuture doResolveAndConnect(SocketAddress remoteAddress, final SocketAddress localAddress) {
	final ChannelFuture regFuture = initAndRegister();
	if (regFuture.cause() != null) {
		return regFuture;
	}

	final Channel channel = regFuture.channel();
	final EventLoop eventLoop = channel.eventLoop();
	......
}

这里执行了父类AbstractBootstrap中的initAndRegister方法

final ChannelFuture initAndRegister() {
	final Channel channel = channelFactory().newChannel();
	try {
		init(channel);
	} catch (Throwable t) {
		channel.unsafe().closeForcibly();
		// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
		return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
	}

	ChannelFuture regFuture = group().register(channel);
	......
}

上面的代码我看到了newChannel方法,他执行的就是ReflectiveChannelFactory的newChannel

    @Override
    public T newChannel() {
        try {
            return clazz.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }

这里创建了一个Channel的实例对象。
在initAndRegister方法中,还有一个重要的方法就是init方法,它里面创建了一个重要的元素,就是ChannelPipeline,关于ChannelPipeline,我们后续介绍。

经过上面一系列的动作,Channel就被创建好了。下面就看看他是怎样通过socket连接的。
我们在Netty:EventLoopGroup中提到,服务端和客户端通过bind和connect开启一个线程,去监听通道的数据操作。客户端通过Bootstrap类的doConnect0开始这个过程。
private static void doConnect0(
		final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelFuture regFuture,
		final ChannelPromise connectPromise) {

	// This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
	// the pipeline in its channelRegistered() implementation.
	final Channel channel = connectPromise.channel();
	channel.eventLoop().execute(new Runnable() {
		@Override
		public void run() {
			if (regFuture.isSuccess()) {
				if (localAddress == null) {
					channel.connect(remoteAddress, connectPromise);
				} else {
					channel.connect(remoteAddress, localAddress, connectPromise);
				}
				connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
			} else {
				connectPromise.setFailure(regFuture.cause());
			}
		}
	});
}

上面代码中,channel.eventLoop().execute启动线程,channel.connect开始连接。

这里的channel是NioSocketChannel,它的继承关系大致如下
NioSocketChannel extends AbstractNioByteChannel extends AbstractNioChannel extends AbstractNioChannel extends AbstractChannel extends DefaultAttributeMap

channel.connect执行AbstractNioChannel中的connect方法
	@Override
	public final void connect(
			final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
			......
			if (doConnect(remoteAddress, localAddress)) {
				fulfillConnectPromise(promise, wasActive);
			} else {
			......
		}
	}

doConnect是abstract方法,由NioSocketChannel的doConnect实现

@Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
	......
	boolean success = false;
	try {
		boolean connected = javaChannel().connect(remoteAddress);
		......
		return connected;
	} finally {
		if (!success) {
			doClose();
		}
	}
}

javaChannel在AbstractNioChannel类中,返回SelectableChannel对象,这个对象是在NioSocketChannel初始化时,通过newSocket静态方法,使用默认的SelectorProvider的openSocketChannel得到的,然后通过它的socket()方法,获取套接字进行和服务端的连接,这样服务端和客户端就通过套接字联通在一起了。

服务端分析一样的思路。

时间: 2024-11-08 22:21:42

Netty:Channel的相关文章

netty报错:io.netty.channel.ChannelPipelineException

1.九月 23, 2018 8:35:02 下午 io.netty.channel.ChannelInitializer channelRegistered警告: Failed to initialize a channel. Closing: [id: 0xa09c718b, /127.0.0.1:50509 => /127.0.0.1:9999]io.netty.channel.ChannelPipelineException: com.sxt.netty.first.Server4Hell

Netty Channel 接口名词理解

1.Channel channel 是负责数据读,写的对象,有点类似于老的io里面的stream.它和stream的区别,channel是双向的,既可以write 也可以read,而stream要分outstream和inputstream.而且在NIO中用户不应该直接从channel中读写数据,而是应该通过buffer,通过buffer再将数据读写到channel中. 一个channel 可以提供给用户下面几个信息 (1)channel的当前状态,比如open 还是closed (2)Chan

netty io.netty.channel 简介1

Interface AddressedEnvelope<M,A extends SocketAddress> 此接口将一个消息.发送地址和接收地址封装到了一起 Interface Channel 此接口表示到网络socket或者组件(component)的一个连接,其提供了IO操作的一些功能,比如read, write, connect, and bind.一个channel可以给用户提供如下功能:1.当前channel的状态(open.connected等).2.channel的配置参数(如

关于 Netty Channel 的 Autoread

Netty 4 的 Channel 多了一个 autoread 参数, 它的用处是在让 channel 在触发某些事件以后(例如 channelActive, channelReadComplete)以后还会自动调用一次 read(), 代码: DefaultChannelPipeline.java @Override public ChannelPipeline fireChannelActive() { head.fireChannelActive(); if (channel.config

netty io.netty.channel介绍2

Interface ChannelHandlerContext 上下文对象使得当前channelhandler可以与其所属的channelpipeline以及其他handler进行交互,可以通知所属channelpipeline中的下一个handler,也可动态修改其所属的channelpipeline,具体功能如下: 通知.通过调用channelhandlercontext提供的方法可以调用同一个channelpipeline中的相邻的下一个channelhandler,详情可以参照chann

netty源码解解析(4.0)-14 Channel NIO实现:读取数据

 本章分析Nio Channel的数据读取功能的实现. Channel读取数据需要Channel和ChannelHandler配合使用,netty设计数据读取功能包括三个要素:Channel, EventLoop和ChannelHandler.Channel有个read方法,这个方法不会直接读取数据,它的作用是通知持有当前channel的eventLoop可以从这个这个channel读取数据了,这个方法被调用之后eventLoop会在channel有数据可读的时候从channel读出数据然后把数

netty(五) channel

问题 channel 是如何处理发送一半中断后继续重发的 channel 具体作用是什么 概述 这一节我们将介绍 Channel 和内部接口 Unsafe .其中Unsafe 是内部接口,聚合在Channel 中协助网络读写操作相关的操作,设计初衷就是 Channel 的内部辅助类,不应该被用户使用. 继承类分析 继承关系链 : AbstractChannel -> AbstractNioChannel -> AbstractNioByteChannel -> NioSocketChan

netty 解决TCP粘包与拆包问题(二)

TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识消息的总长度 一.采用指定分割符解决粘包与拆包问题 服务端 1 package com.ming.netty.nio.stickpack; 2 3 4 5 import java.net.InetSocketAddress; 6 7 import io.netty.bootstrap.ServerB

nio原理/netty简单应用

一.非阻塞IO模式原理 与阻塞模式对应的另一种模式叫非阻塞IO模式,在整个通信过程中读和写操作不会阻塞,当前处理线程不存在阻塞情况.从A机器到B机器它的通信过程是:A机器一条线程将通道设置为写事件后往下执行,而另外一条线程遍历到此通道有字节要写并往socket写数据,B机器一条线程遍历到此通道有字节要读,交给另外一条线程对socket读数据,处理完又把通道设置为写事件,遍历线程遍历到此通道有字节要写,又往socket写数据传往A机器,不断往下循环此操作直到完成通信.这个过程每台机器都有两类主要线