Netty(一) 簡單的Client-Server開發+TCP粘包/拆包解決代碼(換行與標識符兩種)

本節只介紹如何傳輸String類型的簡單的Client-Server模式的Netty代碼 和幾種處理TCP粘包/拆包的方式

 

1、NettyServer主邏輯

package com.back.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class SimpleNettyServer {

	public static void main(String[] args) {
		int port = 8888;
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			serverBootstrap.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_BACKLOG, 1024).childHandler(new RouteHandler());
			ChannelFuture future = serverBootstrap.bind(port).sync();
			System.out.println("server start!");
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			
		}finally{
			System.out.println("釋放線程池資源!");
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
		
	}
}

2、上面看到的RouteHandler代碼

package com.back.server;

import com.back.constant.NettyConstant;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
 * 設置請求 要經過哪些處理的handler  
 * 公共的handler邏輯等 可以在此處添加  如:記錄日誌、消息編解碼等
 * LineBasedFrameDecoder+StringDecoder 來解決TCP粘包拆包問題原理
 * 前者識別換行符,同時設置單行最大字節 所以這兩個組合就是按行切換的文本解碼器
 * DelimiterBasedFrameDecoder+StringDecoder 則是通過制定分隔符(同時設置最大字節)來區分 每次消息末尾都要加指定分隔符
 * FixedLengthFrameDecoder+StringDecoder 則代表不管怎樣 每次讀取指定長度 字節的包 不在演示
 * @author back
 *
 */
public class RouteHandler extends ChannelInitializer<SocketChannel> {

	@Override
	protected void initChannel(SocketChannel arg0) throws Exception {
		
		//arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
		ByteBuf delimiter = Unpooled.copiedBuffer(NettyConstant.DELIMITER.getBytes());
		arg0.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
		arg0.pipeline().addLast(new StringDecoder());
		arg0.pipeline().addLast(new MessageHandler());
	}

}

3、業務處理的邏輯類MessageHandler

package com.back.server;

import com.back.constant.NettyConstant;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;


/**
 * 真實處理信息的handler
 * @author Administrator
 *
 */
public class MessageHandler extends ChannelHandlerAdapter {

	private RequestHandler handler = RequestHandler.getRequestHandler();
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		String message = (String)msg;
		System.out.println("請求報文 : " + message);
		//String dilimiter = System.getProperty("line.separator");
		String resp = handler.simpleHandlerRequest(message)+NettyConstant.DELIMITER;
		ByteBuf copiedBuffer = Unpooled.copiedBuffer(resp.getBytes());
		ctx.writeAndFlush(copiedBuffer);
	}
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}

4、自定義的業務邏輯類

package com.back.server;

import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 隨便寫了個處理請求 和返回值得邏輯
 * @author back
 *
 */
public class RequestHandler {
 
	private RequestHandler(){};
	private static volatile RequestHandler handler;
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
	public static RequestHandler getRequestHandler(){
		if(handler == null){
			synchronized (RequestHandler.class) {
				if(null == handler){
					handler = new RequestHandler();
				}
			}
		}
		return handler;
	}
	
	public  String simpleHandlerRequest(String request){
		String response = null;
		switch (request) {
		case "time":
			response = sdf.format(new Date());
			break;

		default:
			break;
		}
		return response == null ? "badReq" :response;
	}
}

5、NettyClient 

package com.back.client;

import com.back.constant.NettyConstant;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class SimpleNettyClient {

	public static void main(String[] args) {
		
		NioEventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(group).channel(NioSocketChannel.class)
			.option(ChannelOption.TCP_NODELAY, true)
			.handler(new ClientRouteHandler());
			ChannelFuture future = bootstrap.connect("127.0.0.1", 8888).sync();
			System.out.println("client start!");
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
		}finally{
			group.shutdownGracefully();
		}
		
	}
}
class ClientRouteHandler extends ChannelInitializer<SocketChannel>{

	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		//ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
		ByteBuf delimiter = Unpooled.copiedBuffer(NettyConstant.DELIMITER.getBytes());
		ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
		ch.pipeline().addLast(new StringDecoder());
		ch.pipeline().addLast(new ClientMessageHandler());
	}
	
}

6、ClientMessageHandler 發送請求和拿到返回報文

package com.back.client;

import com.back.constant.NettyConstant;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class ClientMessageHandler extends ChannelHandlerAdapter {

	//byte[] req = ("time"+System.getProperty("line.separator")).getBytes();
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		for (int i = 0; i < 100; i++) {
			byte[] req = ("time"+NettyConstant.DELIMITER).getBytes();
			ByteBuf buf = Unpooled.buffer(req.length);
			buf.writeBytes(req);
			ctx.writeAndFlush(buf);
		}
	}
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		
		String response = (String)msg;
		System.out.println("接收到報文:"+response);
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章