分佈式網絡通信框架Netty實戰
Netty實現客戶端和服務器NIO通信
代碼實戰
田超凡 2019年10月28日
Netty線程組NioEventLoopGroup工作原理圖
代碼搭建Netty服務器和客戶端,基於NIO模型、ByteBuffer緩衝區、Netty內置編碼器解決TCP粘包拆包問題,實現IO通信:
package com.tcf.netty.demo.server;
import com.tcf.netty.demo.server.handle.NettyServerEventListenHandle;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringEncoder;
/***
* TODO TCF Netty服務器
* @author Hasee
*
*/
public class NettyServer {
//TODO TCF 服務器端口號:8080
private static int port=8080;
public static void main(String[] args)
{
//TODO TCF Boss線程組
NioEventLoopGroup bossGroup=new NioEventLoopGroup();
//TODO TCF 工作線程組
NioEventLoopGroup workGroup=new NioEventLoopGroup();
//TODO TCF Netty-NIO服務器
ServerBootstrap serverBootstrap=new ServerBootstrap();
serverBootstrap.group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
//TODO TCF 註冊事件處理器
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception
{
//TODO TCF Netty內置編碼器,解決TCP拆包粘包問題
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringEncoder());
//TODO TCF 自定義事件監聽處理器
socketChannel.pipeline().addLast(new NettyServerEventListenHandle());
}
});
//TODO TCF 接收客戶端發送的消息
try
{
//TODO TCF 同步處理方式-sync
ChannelFuture channelFuture=serverBootstrap.bind(port).sync();
System.out.println("Netty服務器啓動成功....");
//TODO TCF 等待服務器監聽端口-同步sync
channelFuture.channel().closeFuture().sync();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
//TODO TCF 關閉連接,釋放資源
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
package com.tcf.netty.demo.server.handle;
import java.nio.charset.Charset;
import org.apache.commons.lang.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/***
* TODO TCF Netty服務器-事件監聽處理器
* @author Hasee
*
*/
public class NettyServerEventListenHandle extends SimpleChannelInboundHandler<ByteBuf> {
//TODO TCF 接收到客戶端發送消息計數器
private static int count=0;
/***
* TODO TCF 監聽接收客戶端發送消息事件
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception
{
String message=byteBuf.toString(Charset.forName("UTF-8"));
if(StringUtils.isNotEmpty(message))
{
//TODO TCF Example 1 指定分隔符解決TCP拆包粘包問題
/*String[] arrays=message.split("\n");
if(arrays!=null && arrays.length>0)
{
System.out.println("服務器接收到的消息:");
for(String str:arrays)
{
System.out.println(str);
}
}*/
//TODO TCF Example 2 基於Netty內置編碼器解決TCP拆包粘包問題
System.out.println("服務器接收到的消息:"+message);
//TODO TCF 給客戶端返回響應
ctx.writeAndFlush(Unpooled.copiedBuffer("Response Message From NioServer"+count+"\n",Charset.forName("UTF-8")));
count++;
}
}
}
package com.tcf.netty.demo.client;
import java.net.InetSocketAddress;
import com.tcf.netty.demo.client.handle.NettyClientEventListenHandle;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/***
* TODO TCF Netty客戶端
* @author Hasee
*
*/
public class NettyClient {
public static void main(String[] args)
{
//TODO TCF 工作線程組
NioEventLoopGroup workGroup=new NioEventLoopGroup();
//TODO TCF Netty客戶端
Bootstrap client=new Bootstrap();
client.group(workGroup)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(8080))
.handler(new ChannelInitializer<SocketChannel>() {
//TODO TCF Netty客戶端初始化事件-自定義初始化事件處理器
@Override
protected void initChannel(SocketChannel channel) throws Exception
{
channel.pipeline().addLast(new NettyClientEventListenHandle());
}
});
try
{
//TODO TCF Netty客戶端處理通道
ChannelFuture channelFuture=client.connect().sync();
channelFuture.channel().closeFuture().sync();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
//TODO TCF 關閉線程組,釋放資源
workGroup.shutdownGracefully();
}
}
}
package com.tcf.netty.demo.client.handle;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/***
* TODO TCF Netty客戶端-事件監聽處理器
* @author Hasee
*
*/
public class NettyClientEventListenHandle extends SimpleChannelInboundHandler<ByteBuf> {
//TODO TCF 接收服務器返回的數據
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception
{
//TODO TCF 服務器返回的數據
String message=byteBuf.toString(Charset.forName("UTF-8"));
if(StringUtils.isNotEmpty(message))
{
System.out.println(message);
}
}
//TODO TCF 向服務器發送數據
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception
{
//TODO TCF Example 1 指定分隔符解決TCP拆包粘包問題
/*for(int i=0;i<10;i++)
{
ByteBuf byteBuf=Unpooled.copiedBuffer("發送消息-"+i+"\n",Charset.forName("UTF-8"));
ctx.writeAndFlush(byteBuf);
}*/
//TODO TCF Example 2 基於Netty服務器內置編碼器解決TCP拆包粘包問題
for(int i=0;i<10;i++)
{
ByteBuf byteBuf=Unpooled.copiedBuffer("發送消息-"+i+"\n",Charset.forName("UTF-8"));
ctx.writeAndFlush(byteBuf);
}
}
}