開如啓動我們有Netty的服務端,用的方法對象是ServerBootstrap對象
具體的Netty的服務端的代碼如下
public class MyNettyServer {
public static void main(String[] args) {
// 因爲我們的Netty用的是主從模式的開發模式,所以要設置二個主從組給這個服務器引擎,
// 同時這個二個group可以在創建的時候可以指定這個組當中可以有多少個線程,默認是1個
NioEventLoopGroup parent = new NioEventLoopGroup();
NioEventLoopGroup chlid = new NioEventLoopGroup();
try {
// 先進行構建我們的服務器引擎ServerBootStrap,直接new也就可以了
ServerBootstrap serverBootStrap = new ServerBootstrap();
serverBootStrap.group(parent, chlid);
// 進行配置這個服務器引擎ServerBootStrap,剛創建時是不能直接啓動這個這個服務器引擎的,要配置三個基本的配置的
// 這個三個基本的配置如下:channel,handler,option
// 這個表示的是如果我們的服務器如果如果不過來這麼多的通道,最多隻能讓1024個通道時進行阻塞等待
serverBootStrap.option(ChannelOption.SO_BACKLOG, 1024);
serverBootStrap.option(ChannelOption.SO_KEEPALIVE, true);
// 給我們的引擎設置一下我們的通道
serverBootStrap.channel(NioServerSocketChannel.class);
// 最後再註冊一個handler處理器對象,進行註冊一個通道初始化器對象
serverBootStrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("進行通道的初始化操作,這個方法是在我們的通道向Selector註冊時,就會執行這個方法");
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,
Delimiters.lineDelimiter()[0]));
ch.pipeline().addLast(new StringDecoder(Charset.defaultCharset()));
//進行數據的編碼,然後發送給客戶端
ch.pipeline().addLast(new StringEncoder(Charset.defaultCharset()));
//進行具體的一個管道調用處理操作
ch.pipeline().addFirst(new MyHandlerTest());
}
});
//現在進行綁定端口,同時用的是同步的方法
ChannelFuture future=serverBootStrap.bind(8888).sync();
//進行給我們的客戶端發送響應數據
// future.channel().writeAndFlush("開明乾坤,照大地...\r\n");
ByteBuf bu=PooledByteBufAllocator.DEFAULT.buffer(1024);
bu.writeBytes("開明乾坤,照大地".getBytes(Charset.defaultCharset()));
future.channel().writeAndFlush(bu);
//進行關閉操作,這個方法是一個阻塞的方法,線程會被阻塞到這裏
future.channel().closeFuture().sync();
//進行那麼操作
// 最後進行關閉這個組
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
parent.shutdownGracefully();
chlid.shutdownGracefully();
}
}
}
具體的服務器的handler的代碼如下:
public class MyHandlerTest extends ChannelInboundHandlerAdapter {
//這個方法是在我們的channel向我們的Select註冊時調用這個方法
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
}
//這個方法是在我們的通道進行有數據發送過來時進行調用這個方法去取得通道中數據
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
System.out.println("有數據過來了,你要接收這個數據了");
//進行判斷一下我們的數據的類型
if(msg instanceof ByteBuf){
//進行具體數據處理過程如下:
ByteBuf buf=(ByteBuf) msg;
//進行數據的轉換操作,也可以Charset.defaultCharset()
System.out.println("我們的服務器接收到客戶端發過來的數據爲:"+
buf.toString(Charset.forName("UTF-8")));
}
}
}
具體的客戶端的代碼如下:
public class MyNettyClient {
public static void main(String[] args) {
//進行創建事件組對象
NioEventLoopGroup group=new NioEventLoopGroup();
try {
//創建一個客戶端啓動引擎,用的是Bootstrap
Bootstrap bootStrap=new Bootstrap();
//進行增加組對象
bootStrap.group(group);
//進行客戶端啓動引擎的基本的配置如下
bootStrap.option(ChannelOption.SO_KEEPALIVE,true)//保持存活
.option(ChannelOption.SO_RCVBUF,1024) //設置接受緩衝區的大小
.channel(NioSocketChannel.class) //設置突然我們客戶端的通道爲SocketChannel
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
//進行基本的客戶端的初始化操作
System.out.println("我們的客戶端開始工作了...");
//增加一些基本的處理器操作
//增加數據分割符,數據後面增加一個分割符,也就是
//Delimiters.lineDelimiter()[0],這個lineDelimiter返回有二個元素
//的數據,第一個表示的是\r\n,第二個是\n,這裏用的是第一個
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,
Delimiters.lineDelimiter()[0]));
//進行增加一些數據的解碼和編碼的操作,這個Charset.defaultCharset()就是UTF-8
ch.pipeline().addLast(new StringEncoder());
//這個進行接收字符串的數據的操作
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new MyReadDataHandler());
}
});
//進行啓動我們的客戶端引擎對象,使用的是同步的啓動方法
ChannelFuture future=bootStrap.connect("localhost",8888).sync();
//進行給服務器發送數據,Delimiters.lineDelimiter()這個表示數據有\r\n時就把這個數據發送給服務器
// future.channel().write("楊海龍".getBytes(Charset.forName("UTF-8")));//這種方式不可以
// future.channel().writeAndFlush(Delimiters.lineDelimiter()[0]);//不可以
future.channel().writeAndFlush("楊海龍");//注意這個不要增加回換行在數據的後面,因爲在定義編碼的時候默認增加上去了
// future.channel().writeAndFlush(Delimiters.lineDelimiter()[0]);//不可以
//第二種數據寫出的方式
ByteBuf buf=PooledByteBufAllocator.DEFAULT.buffer(1024);
//進行寫入數據
buf.writeBytes("李四".getBytes(Charset.defaultCharset()));
//最後進行寫出數據
future.channel().writeAndFlush(buf);
//進行通道等待關閉的操作
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
}
}
我們有客戶端的Handler的代碼如下:
public class MyReadDataHandler extends ChannelInboundHandlerAdapter {
//這個方法是用來註冊我們有Channnel時調用這的方法
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
super.channelRegistered(ctx);
}
//我們的服務器有發送數據過來時,就會調用這個方法進行讀取這個這個數據
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
//進行數據的讀取
System.out.println("讀取服務器發送過來的數據...");
//進行接收數據了
if(msg instanceof ByteBuf){
ByteBuf buf=(ByteBuf) msg;
System.out.println("我們的從服務器接收到的數據爲:"+buf.toString(Charset.defaultCharset()));
}
}
}
現在開始一步一步的調度我們有Netty的服務器端的代碼
// 先進行構建我們的服務器引擎ServerBootStrap,直接new也就可以了
ServerBootstrap serverBootStrap = new ServerBootstrap();
上面這個調用創建一個服務器的啓動輔助類,加載了這個ServerBootStrap當中的所有的靜態的成員的方法,其中有重要的ServerBootstrap的配置類ServerBootstrapConfig
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);//也會調用這個類的構造方法
public ServerBootstrap() { }
}
同時我們的這個ServerBootstrap繼承於AbstractBootstrap,所以也會調用我們有AbstractBootstrap的構造方法
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
volatile EventLoopGroup group;
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory;
private volatile SocketAddress localAddress;
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
private volatile ChannelHandler handler;
AbstractBootstrap() {
// Disallow extending from a different package.
}
執行到這個方法時會綁定我們有線程的模型
serverBootStrap.group(parent, chlid);
這個方法先把這個parent加入到當前類的父類AbstractBootstrap當中的group裏面,然後再把child加入到serverBootstrap當中的group當中
/**
* The {@link EventLoopGroup} which is used to handle all the events for the to-be-created
* {@link Channel}
*/
@SuppressWarnings("unchecked")
public B group(EventLoopGroup group) {
if (group == null) {
throw new NullPointerException("group");
}
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return (B) this;
}
ServerBootstrap當中的group方法
/**
* Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
* {@link EventLoopGroup}'s are used to handle all the events and IO for {@link ServerChannel} and
* {@link Channel}'s.
*/
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
然後再進行我們有TCP參數的綁定操作:
// 這個表示的是如果我們的服務器如果如果不過來這麼多的通道,最多隻能讓1024個通道時進行阻塞等待
serverBootStrap.option(ChannelOption.SO_BACKLOG, 1024);
serverBootStrap.option(ChannelOption.SO_KEEPALIVE, true);
// 給我們的引擎設置一下我們的通道
serverBootStrap.channel(NioServerSocketChannel.class);
下面的方法調用了父類AbstractBootstrap的option的方法
/**
* Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
* created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
*/
@SuppressWarnings("unchecked")
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return (B) this;
}
上面的這個options是我們父類AbstractBootstrap的靜態的linkedHashMap的結構體
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
對應的結果爲:options={SO_BACKLOG=1024, SO_KEEPALIVE=true}
接下來開始設置我們有服務器的通道的方法了
// 給我們的引擎設置一下我們的通道
serverBootStrap.channel(NioServerSocketChannel.class);
具體的調用代碼如下:
一樣還是調用我們有ServerBootstrap的channel的方法,下面這個channelFactory(通道工廠類)是專門用我們調用這個方法時傳遞進來的字節類名來進行反射創建出來具體的通道類,也就是創建出來了NioServerSocketChannel類對象
/**
* The {@link Class} which is used to create {@link Channel} instances from.
* You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
* {@link Channel} implementation has no no-args constructor.
*/
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
接下來進行具體的handler的註冊工作
// 最後再註冊一個handler處理器對象,進行註冊一個通道初始化器對象
serverBootStrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("進行通道的初始化操作,這個方法是在我們的通道向Selector註冊時,就會執行這個方法");
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,
Delimiters.lineDelimiter()[0]));
ch.pipeline().addLast(new StringDecoder(Charset.defaultCharset()));
//進行數據的編碼,然後發送給客戶端
ch.pipeline().addLast(new StringEncoder(Charset.defaultCharset()));
//進行具體的一個管道調用處理操作
ch.pipeline().addFirst(new MyHandlerTest());
}
});
* Set the {@link ChannelHandler} which is used to serve the request for the {@link Channel}'s.
*/
public ServerBootstrap childHandler(ChannelHandler childHandler) {
if (childHandler == null) {
throw new NullPointerException("childHandler");
}
this.childHandler = childHandler;
return this;
}
接下是開始綁定我們有數據的了
//現在進行綁定端口,同時用的是同步的方法
ChannelFuture future=serverBootStrap.bind(8888).sync();
還是調用了我們有AbstractBootstrap的方法
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(InetAddress inetHost, int inetPort) {
return bind(new InetSocketAddress(inetHost, inetPort));
}
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(SocketAddress localAddress) {
validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(localAddress);
}
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();//這個方法具體的去調用我們有工廠的方法,然後調用在newChannel的方法當中調用了一個空構造方法創建出來我們ServerSocketChannel的通道類
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
現在這個validate這個方法是我們ServerBootstrap類的方法的具體的實現如下
/**
* Validate all the parameters. Sub-classes may override this, but should
* call the super method in that case.
*/
@SuppressWarnings("unchecked")
public B validate() {
if (group == null) {
throw new IllegalStateException("group not set");
}
if (channelFactory == null) {
throw new IllegalStateException("channel or channelFactory not set");
}
return (B) this;
}
在我們有上面的這個DoBind方法當中調用了一個initAndRegister();
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
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 = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
// If we are here and the promise is not failed, it's one of the following cases:
// 1) If we attempted registration from the event loop, the registration has been completed at this point.
// i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
// 2) If we attempted registration from the other thread, the registration request has been successfully
// added to the event loop's task queue for later execution.
// i.e. It's safe to attempt bind() or connect() now:
// because bind() or connect() will be executed *after* the scheduled registration task is executed
// because register(), bind(), and connect() are all bound to the same thread.
return regFuture;
}
//這個方法具體的去調用我們有工廠的方法,然後調用在newChannel的方法當中調用了一個空構造方法創建出來我們ServerSocketChannel的通道類
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
調用這個初始化init的方法代碼的操作的方法如下
@Override
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();//得到我們有在服務器當中設置的可選的參數
synchronized (options) {
setChannelOptions(channel, options, logger);
}
//我們沒有設置服務器的att,所以這個可以不用看
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
//得到一個默認的DefaultChannelPipeline
ChannelPipeline p = channel.pipeline();
//下面的這些方法都是我們有基本一些設置操作
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
//下面這些操作都是我們有handler的基本的設置
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
進行具體的設置我們有服務器的可選的參數
static void setChannelOptions(
Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {//進行for取出我們有已經設置的option
for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) {
setChannelOption(channel, e.getKey(), e.getValue(), logger);
}
}
@SuppressWarnings("unchecked")
private static void setChannelOption(
Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {
try {//修改我們的配置文件,把這些數據設置進行我們有配置類當中
if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
}
} catch (Throwable t) {
logger.warn(
"Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
}
}
其他的看後續文章