博樂建設工程信息網站南昌百度seo
一、Netty服務端開發(fā)
在開始使用 Netty 開發(fā) TimeServer 之前,先回顧一下使用 NIO 進行服務端開發(fā)的步驟。
(1)創(chuàng)建ServerSocketChannel,配置它為非阻塞模式;
(2)綁定監(jiān)聽,配置TCP 參數,例如 backlog 大小;
(3)創(chuàng)建一個獨立的I/O線程,用于輪詢多路復用器 Selector;
(4)創(chuàng)建 Selector,將之前創(chuàng)建的 ServerSocketChannel 注冊到 Selector 上,監(jiān)聽SelectionKey.ACCEPT;
(5)啟動I/0線程,在循環(huán)體中執(zhí)行 Selectorselect0)方法,輪詢就緒的 Channel;
(6)當輪詢到了處于就緒狀態(tài)的 Channel 時,需要對其進行判斷,如果是OP ACCEPT狀態(tài),說明是新的客戶端接入,則調用 ServerSocketChannel.accept()方法接受新的客戶端:(7)設置新接入的客戶端鏈路 SocketChannel 為非阻塞模式,配置其他的一些TCP 參數(8)將SocketChannel注冊到 Selector,監(jiān)聽 OP READ 操作位;
(9)如果輪詢的Channel為OP READ,則說明 SocketChannel 中有新的就緒的數據包需要讀取,則構造ByteBuffer 對象,讀取數據包;
(10)如果輪詢的Channel為OP WRITE,說明還有數據沒有發(fā)送完成,需要繼續(xù)發(fā)送。
import io.netty.bootstrap.ServerBootstrap;import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;public class nettyServer {Logger logger = LoggerFactory.getLogger(nettyServer.class);@Value("${netty.port}")int port;@PostConstructpublic void bind() {EventLoopGroup bossrGroup = new NioEventLoopGroup();//接收客戶端傳過來的請求EventLoopGroup wokerGroup = new NioEventLoopGroup();//接收到請求后將后續(xù)操作try {ServerBootstrap b = new ServerBootstrap();b.group(bossrGroup, wokerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)//.childOption(ChannelOption.SO_KEEPALIVE, true).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new serverHandlerAdapter());ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new HeartbeatHandler());ch.pipeline().addLast(new IdleStateHandler(10, 1, 1));}});ChannelFuture f = b.bind(port).sync();} catch (Exception e) {}}
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;public class serverHandlerAdapter extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {super.channelActive(ctx);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {super.channelRead(ctx, msg);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {super.channelReadComplete(ctx);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);}
二、Netty客戶端開發(fā)
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class nettyClient {clientHandlerAdapter clientHandlerAdapter = new clientHandlerAdapter();public void conect(String ip, int port) {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).remoteAddress("", port).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(clientHandlerAdapter);}});b.bind(ip, port).sync();} catch (Exception e) {}}public boolean sendFile() {return clientHandlerAdapter.sendFile();}}
我們從 connect 方法講起,在第 13 行首先創(chuàng)建客戶端處理 I/0 讀寫的 NioEventLoopGroup 線程組,然后繼續(xù)創(chuàng)建客戶端輔助啟動類 Bootstrap,隨后需要對其進行配置。與服務端不同的是,它的 Channel 需要設置為 NioSocketChannel,然后為其添加 handler,此處為了簡單直接創(chuàng)建匿名內部類,實現 initChannel 方法,其作用是當創(chuàng)建 NioSocketChannel成功之后,在初始化它的時候將它的 ChannelHandler 設置到 ChannelPipeline 中,用于處理網絡I/O事件。
客戶端啟動輔助類設置完成之后,調用 connect 方法發(fā)起異步連接,然后調用同步方法等待連接成功。
最后,當客戶端連接關閉之后,客戶端主函數退出,在退出之前,釋放 NIO 線程組的資源。
下面我們繼續(xù)看下TimeClientHandler 的代碼如何實現