Netty入门系列(1) --使用Netty搭建服务端和客户端
引言
前面我們介紹了網絡一些基本的概念,雖然說這些很難吧,但是至少要做到理解吧。有了之前的基礎,我們來正式揭開Netty這神秘的面紗就會簡單很多。
服務端
public class PrintServer {public void bind(int port) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(); //1EventLoopGroup workerGroup = new NioEventLoopGroup(); //2try {ServerBootstrap b = new ServerBootstrap(); //3b.group(bossGroup, workerGroup) //4 .channel(NioServerSocketChannel.class) //5.option(ChannelOption.SO_BACKLOG, 1024) //6.childHandler(new ChannelInitializer<SocketChannel>() { //7@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new PrintServerHandler());}});ChannelFuture f = b.bind(port).sync(); //8f.channel().closeFuture().sync(); //9} finally {// 優雅退出,釋放線程池資源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {int port = 8080;new TimeServer().bind(port);} }我們來分析一下上面的這段代碼(下面的每一點對應上面的注釋)
1~2:首先我們創建了兩個NioEventLoopGroup實例,它是一個由Netty封裝好的包含NIO的線程組。為什么創建兩個?我想經過前面的學習大家應該都清楚了。對,因為Netty的底層是IO多路復用,bossGroup 是用于接收客戶端的連接,原理就是一個實現的Selector的Reactor線程。而workerGroup用于進行SocketChannel的網絡讀寫。
3:創建一個ServerBootstrap對象,可以把它想象成Netty的入口,通過這類來啟動Netty,將所需要的參數傳遞到該類當中,大大降低了的開發難度。
4:將兩個NioEventLoopGroup實例綁定到ServerBootstrap對象中。
5:創建Channel(典型的channel有NioSocketChannel,NioServerSocketChannel,OioSocketChannel,OioServerSocketChannel,EpollSocketChannel,EpollServerSocketChannel),這里創建的是NIOserverSocketChannel,它的功能可以理解為當接受到客戶端的連接請求的時候,完成TCP三次握手,TCP物理鏈路建立成功。并將該“通道”與workerGroup線程組的某個線程相關聯。
6:設置參數,這里設置的SO_BACKLOG,意思是客戶端連接等待隊列的長度為1024.
7:建立連接后的具體Handler。就是我們接受數據后的具體操作,例如:記錄日志,對信息解碼編碼等。
8:綁定端口,同步等待成功
9:等待服務端監聽端口關閉
綁定該服務端的Handler
public class PrintServerHandler extends ChannelHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {ByteBuf buf = (ByteBuf) msg; //1byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); //將緩存區的字節數組復制到新建的req數組中String body = new String(req, "UTF-8");System.out.println(body);String response= "打印成功";ByteBuf resp = Unpooled.copiedBuffer(response.getBytes()); ctx.write(resp); //2} @Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush(); //3}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {ctx.close();} }PrintServerHandler 繼承 ChannelHandlerAdapter ,在這里它的功能為 打印客戶端發來的數據并且返回客戶端打印成功。
我們只需要實現channelRead,exceptionCaught,前一個為接受消息具體邏輯的實現,后一個為發生異常后的具體邏輯實現。
1:我們可以看到,接受的消息被封裝為了Object ,我們將其轉換為ByteBuf ,前一章的講解中也說明了該類的作用。我們需要讀取的數據就在該緩存類中。
2~3:我們將寫好的數據封裝到ByteBuf中,然后通過write方法寫回到客戶端,這里的3調用flush方法的作用為,防止頻繁的發送數據,write方法并不直接將數據寫入SocketChannel中,而是把待發送的數據放到發送緩存數組中,再調用flush方法發送數據。
客戶端
public class PrintClient {public void connect(int port, String host) throws Exception {EventLoopGroup group = new NioEventLoopGroup(); //1try {Bootstrap b = new Bootstrap(); //2b.group(group) //3.channel(NioSocketChannel.class) //4.option(ChannelOption.TCP_NODELAY, true) //5.handler(new ChannelInitializer<SocketChannel>() { //6@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new PrintClientHandler());}});ChannelFuture f = b.connect(host, port).sync(); //7f.channel().closeFuture().sync(); //8} finally {// 優雅退出,釋放NIO線程組group.shutdownGracefully();}}/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {int port = 8080;new TimeClient().connect(port, "127.0.0.1");} }我們繼續來分析一下上面的這段代碼(下面的每一點對應上面的注釋)
1:區別于服務端,我們在客戶端只創建了一個NioEventLoopGroup實例,因為客戶端你并不需要使用I/O多路復用模型,需要有一個Reactor來接受請求。只需要單純的讀寫數據即可
2:區別于服務端,我們在客戶端只需要創建一個Bootstrap對象,它是客戶端輔助啟動類,功能類似于ServerBootstrap。
3:將NioEventLoopGroup實例綁定到Bootstrap對象中。
4:創建Channel(典型的channel有NioSocketChannel,NioServerSocketChannel,OioSocketChannel,OioServerSocketChannel,EpollSocketChannel,EpollServerSocketChannel),區別與服務端,這里創建的是NIOSocketChannel.
5:設置參數,這里設置的TCP_NODELAY為true,意思是關閉延遲發送,一有消息就立即發送,默認為false。
6:建立連接后的具體Handler。注意這里區別與服務端,使用的是handler()而不是childHandler()。handler和childHandler的區別在于,handler是接受或發送之前的執行器;childHandler為建立連接之后的執行器。
7:發起異步連接操作
8:當代客戶端鏈路關閉
綁定該客戶端的Handler
public class PrintClientHandler extends ChannelHandlerAdapter {private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());private final ByteBuf firstMessage;/*** Creates a client-side handler.*/public TimeClientHandler() {byte[] req = "你好服務端".getBytes();firstMessage = Unpooled.buffer(req.length); //1firstMessage.writeBytes(req);}@Overridepublic void channelActive(ChannelHandlerContext ctx) {ctx.writeAndFlush(firstMessage); //2 }@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) //3throws Exception {ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, "UTF-8");System.out.println("服務端回應消息 : " + body);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { //4// 釋放資源System.out.println("Unexpected exception from downstream : "+ cause.getMessage());ctx.close();} }PrintClientHandler 繼承 ChannelHandlerAdapter ,在這里它的功能為 發送數據并打印服務端發來的數據。
我們只需要實現channelActive,channelRead,exceptionCaught,第一個為建立連接后立即執行,后兩個與一個為接受消息具體邏輯的實現,另一個為發生異常后的具體邏輯實現。
1:將發送的信息封裝到ByteBuf中。
2:發送消息。
3:接受客戶端的消息并打印
4:發生異常時,打印異常信息,釋放客戶端資源
總結
這是一個入門程序,對應前面所講的I/O多路復用模型以及NIO的特性,能很有效的理解該模式的編程方式。如果這幾段代碼看著很費勁,那么可以看看之前博主的Netty基礎系列。
如果博主哪里說得有問題,希望大家提出來,一起進步~
轉載于:https://www.cnblogs.com/zhxiansheng/p/10830828.html
總結
以上是生活随笔為你收集整理的Netty入门系列(1) --使用Netty搭建服务端和客户端的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux下源码安装rabbitMq
- 下一篇: SqlServer中获取所有数据库,所有