場景
Netty在IDEA中搭建HelloWorld服務端並對Netty執行流程與重要組件進行介紹:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108592418
上面對於搭建Netty的HelloWorld已經實現,下面介紹怎樣使用Netty進行Socket編程,並搭建服務端與客戶端進行通信傳輸數據。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程序猿
獲取編程相關電子書、教程推送與免費下載。
實現
Socket服務端搭建
在src下新建包,包下新建Socket類作為服務端,並新建main方法
package com.badao.nettySocket; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class SocketServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try{ //服務端啟動類-Netty提供的啟動服務端的類 ServerBootstrap serverBootstrap = new ServerBootstrap(); //參數為兩個事件組 前面的用來接收請求 后面的用來處理 //childHandler 設置請求到了之后進行處理的處理器 serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class) .childHandler(new SocketServerInitializer()); //綁定端口 ChannelFuture channelFuture = serverBootstrap.bind(70).sync(); channelFuture.channel().closeFuture().sync(); }finally { //關閉事件組 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
同上面的博客的流程一樣,在服務端中也需要一個Socket服務端的初始化器SocketServerInitializer()
所以新建類SocketServerInitializer作為服務端的初始化器。
package com.badao.nettySocket; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; public class SocketServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new SocketServerHandler()); } }
在初始化器中只需要將其繼承ChannelInitializer並且泛型的類型為SocketChannnel。
然后需要重寫其initChannel方法,對通道進行初始化。
在方法中添加一些Netty自帶的處理器,主要是編碼解碼的格式設置。
然后最后再添加自定義的處理器對請求進行處理。
所以再新建一個自定義的服務端處理類SockerServerHandler
package com.badao.nettySocket; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class SocketServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("服務端收到來自"+ctx.channel().remoteAddress()+"的"+msg); ctx.channel().writeAndFlush("服務端發送的數據:(公眾號:霸道的程序猿)"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
在處理器中需要繼承SimpleChannelInboundHandler,泛型的類型就是傳輸數據的類型String
然后重寫其chanelRead0方法,此方法就是具體的處理的方法。
在方法中接受客戶端的數據並向客戶端發送數據。
然后再重寫exceptionCaught方法,用來捕獲異常並輸出,一旦出現異常則關閉連接。
Socket客戶端搭建
與搭建Socket服務端一致,搭建Socket客戶端也是同樣的流程。
新建SocketClient作為客戶端類並新增main方法
package com.badao.nettySocket; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class SocketClient { public static void main(String[] args) throws Exception { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class) .handler(new SocketClientInitializer()); //綁定端口 ChannelFuture channelFuture = bootstrap.connect("localhost", 70); channelFuture.channel().closeFuture().sync(); } finally { //關閉事件組 eventLoopGroup.shutdownGracefully(); } } }
在main方法中連接上面服務端綁定的70端口,注意這里的啟動類使用的是Bootstrap,並且
客戶端使用的是handler方法,注意與服務端的不同。
然后在客戶端中也使用了客戶端的初始化器,所以新建SocketClientInitializer
package com.badao.nettySocket; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; public class SocketClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new SocketClientHandler()); } }
所添加的處理器與服務端的一致,同樣添加了一個自定義的處理器SocketClientHandler
所以新建類SocketClientHandler
package com.badao.nettySocket; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class SocketClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("客戶端收到來自"+ctx.channel().remoteAddress()+"的"+msg); ctx.channel().writeAndFlush("客戶端向服務端發送的數據:(公眾號:霸道的程序猿)"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
至此服務端和客戶端已經搭建完成。
依次運行SocketServer的main方法和SocketClient的main方法,如果沒有報錯則是搭建成功。
但是此時雖然客戶端和服務端已經建立連接但是沒有任何的交互傳輸數據和輸出。
這里因為客戶端或者服務端並沒有發出請求。
所以在客戶端的處理器SocketClientHandler中
重寫channelActive方法,此方法會在通道激活即建立連接后執行
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush("客戶端發來消息"); }
在此方法中向服務端發送字符串消息。
那么服務端的處理器中的channelRead0方法就會激活並執行,然后將數據輸出並向客戶端發送數據。
那么客戶端的處理器中的channelRead0也會被激活並執行,那么客戶端會輸出收到服務端的數據並向服務端發送數據。
所以客戶端和服務端一直發送數據。
依次運行服務端和客戶端的main方法,查看輸出