netty自定义协议

《netty权威指南》一书中关于自定义协议开发的源码中有一部分错误导致代码无法运行,加了一点改变可以完美运行了,

package nettyAgreement.decoder;

import java.util.HashMap;
import java.util.Map;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import nettyAgreement.Header;
import nettyAgreement.MarshallingCodecFactory;
import nettyAgreement.NettyMessage;

/**
 * 消息解码类
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月19日
 * @Version 
 * @Description
 */
public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder {
	
	NettyMarshallingDecoder marshallingDecoder;
	
	public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
		super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
		// TODO Auto-generated constructor stub
		marshallingDecoder = MarshallingCodecFactory.buMarshallingDecoder();
	}

	public NettyMessageDecoder(int maxFrameLength,
            int lengthFieldOffset, int lengthFieldLength,
            int lengthAdjustment, int initialBytesToStrip) {
		// TODO Auto-generated constructor stub
		super( maxFrameLength,
	             lengthFieldOffset,  lengthFieldLength,
	             lengthAdjustment,  initialBytesToStrip);
		marshallingDecoder = MarshallingCodecFactory.buMarshallingDecoder();
	}

	@Override
	protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
		// TODO Auto-generated method stub
		ByteBuf frame = (ByteBuf)super.decode(ctx, in);
		if (frame == null) {
			return null;
		}
		
		NettyMessage message = new NettyMessage();
		Header header = new Header();
		header.setCrcCode(frame.readInt());
		header.setLength(frame.readInt());
		header.setSessionID(frame.readLong());
		header.setType(frame.readByte());
		header.setPriority(frame.readByte());
		
		int size = frame.readInt();
		if (size>0) {
			Map<String, Object> attch = new HashMap<String,Object>(size);
			int keySize = 0;
			byte[] keyArray = null;
			String key = null;
			for (int i = 0; i < size; i++) {
				keySize = frame.readInt();
				keyArray = new byte[keySize];
				frame.readBytes(keyArray);
				key = new String(keyArray, "UTF-8");
				attch.put(	key, marshallingDecoder.decode(ctx, frame));
			}
			keyArray = null;
			key = null;
			header.setAttachment(attch);
		}
		if (frame.readableBytes() > 4) {
			message.setBody(marshallingDecoder.decode(ctx, frame));
		}
		message.setHeader(header);
		return message;
	}
}

package nettyAgreement.encoder;

import java.util.List;
import java.util.Map.Entry;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import nettyAgreement.MarshallingCodecFactory;
import nettyAgreement.NettyMessage;

/**
 * 消息编码
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月19日
 * @Version 
 * @Description
 */
public class NettyMessageEncoder extends MessageToMessageEncoder<NettyMessage>{

	private NettyMarshallingEncoder marshallingEncoder;
	
	public NettyMessageEncoder() {
		this.marshallingEncoder = MarshallingCodecFactory.buildMarshallingEncoder();
	}

	@Override
	public void encode(ChannelHandlerContext ctx, NettyMessage msg, List<Object> out) throws Exception {
		// TODO Auto-generated method stub
		if (msg == null || msg.getHeader() == null) {
			throw new Exception("This encode message is null");
		}
		
		ByteBuf sendBuffer = Unpooled.buffer();
		sendBuffer.writeInt(msg.getHeader().getCrcCode());
		sendBuffer.writeInt(msg.getHeader().getLength());
		sendBuffer.writeLong(msg.getHeader().getSessionID());
		sendBuffer.writeByte(msg.getHeader().getType());
		sendBuffer.writeByte(msg.getHeader().getPriority());
		sendBuffer.writeInt(msg.getHeader().getAttachment().size());
		
		String key = null;
		byte[] keyArray = null;
		Object value = null;
		for (Entry<String, Object> param : msg.getHeader().getAttachment().entrySet()) {
			 key = param.getKey();
			 keyArray = key.getBytes("UTF-8");
			 sendBuffer.writeInt(keyArray.length);
			 sendBuffer.writeBytes(keyArray);
			 value = param.getValue();
			 marshallingEncoder.encode(ctx, value, sendBuffer);
		}
		
		key = null;
		keyArray = null;
		value = null;
		if (msg.getBody() != null) {
			marshallingEncoder.encode(ctx, msg.getBody(), sendBuffer);
		}else
			sendBuffer.writeInt(0);  
			// 在第4个字节出写入Buffer的长度  
			int readableBytes = sendBuffer.readableBytes();  
			sendBuffer.setInt(4, readableBytes);  
			
			// 把Message添加到List传递到下一个Handler   
			out.add(sendBuffer);  
	}
	
}

package nettyAgreement;
/**
 * 定义消息头
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月19日
 * @Version 
 * @Description
 */

import java.util.HashMap;
import java.util.Map;

public final class Header {
	
	private int crcCode = 0xabef0101;//长度32,
	private int length;//消息长度32
	private long sessionID;//会话ID64
	private byte type;//消息类型8
	private byte priority;//消息优先级8
	private Map<String, Object> attachment = new HashMap<String,Object>();//附件
	public final int getCrcCode() {
		return crcCode;
	}
	public final void setCrcCode(int crcCode) {
		this.crcCode = crcCode;
	}
	public final int getLength() {
		return length;
	}
	public final void setLength(int length) {
		this.length = length;
	}
	public final long getSessionID() {
		return sessionID;
	}
	public final void setSessionID(long sessionID) {
		this.sessionID = sessionID;
	}
	public final byte getType() {
		return type;
	}
	public final void setType(byte type) {
		this.type = type;
	}
	public final byte getPriority() {
		return priority;
	}
	public final void setPriority(byte priority) {
		this.priority = priority;
	}
	public final Map<String, Object> getAttachment() {
		return attachment;
	}
	public final void setAttachment(Map<String, Object> attachment) {
		this.attachment = attachment;
	}
	@Override
	public String toString() {
		return "Header [crcCode=" + crcCode + ", length=" + length + ", sessionID=" + sessionID + ", type=" + type
				+ ", priority=" + priority + ", attachment=" + attachment + "]";
	}
	
	
}

package nettyAgreement;

import java.util.concurrent.TimeUnit;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.ScheduledFuture;
import nettyAgreement.Header;
import nettyAgreement.MessageType;
import nettyAgreement.NettyMessage;

/**
 * 客户端心跳消息
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月20日
 * @Version 
 * @Description
 */
public class HeartBeatReqHandler extends ChannelHandlerAdapter {
	
	private volatile ScheduledFuture<?> heartBeat;
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		NettyMessage message = (NettyMessage)msg;
		//握手成功,主动发送心跳消息
		if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.type) {
			heartBeat = ctx.executor().scheduleAtFixedRate(new HeartBeatReqHandler.HeartBeatTask(ctx), 0, 5000, TimeUnit.MILLISECONDS);
		}else if (message.getHeader() != null && message.getHeader().getType() == MessageType.HEART_RESP.type) {
			System.out.println("Client receive server heart beat message : -->"+message);
		}else 
			ctx.fireChannelRead(msg);
	}
	//心跳任务
	private class HeartBeatTask implements Runnable{
		private final ChannelHandlerContext ctx;
		
		public HeartBeatTask(final ChannelHandlerContext ctx) {
			this.ctx = ctx;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			NettyMessage heartBeat = buildHeartBeat();
			System.out.println("Client send heart beat message to server : -->"+heartBeat);
			ctx.writeAndFlush(heartBeat);
		}
		
		private NettyMessage buildHeartBeat(){
			NettyMessage message = new NettyMessage();
			Header header = new Header();
			header.setType(MessageType.HEART_REQ.type);
			message.setHeader(header);
			return message;
		}
	}
	
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		if (heartBeat != null) {
			heartBeat.cancel(true);
			heartBeat=null;
		}
		ctx.fireExceptionCaught(cause);
		
	}
}

package nettyAgreement;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * 服务端心跳消息
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月20日
 * @Version 
 * @Description
 */
public class HeartBeatRespHandler extends ChannelHandlerAdapter {
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		NettyMessage message = (NettyMessage)msg;
		if (message.getHeader() != null && message.getHeader().getType() == MessageType.HEART_REQ.type) {
			System.out.println("Receive client beat message : -->"+message);
			NettyMessage heartBeat = buildHeartBeat();
			System.out.println("Send heart beat message to client : -->"+heartBeat);
			ctx.writeAndFlush(heartBeat);
		}else 
			
		ctx.fireChannelRead(msg);
	}
	
	private NettyMessage buildHeartBeat(){
		NettyMessage message = new NettyMessage();
		Header header = new Header();
		header.setType(MessageType.HEART_RESP.type);
		message.setHeader(header);
		return message;
	}
	
}

package nettyAgreement;


import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * 客户端握手认证
 * 
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月20日
 * @Version
 * @Description
 */
public class LoginAuthReqHandler extends ChannelHandlerAdapter {

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		// TODO Auto-generated method stub
		ctx.writeAndFlush(buildLoginReq());
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		NettyMessage message = (NettyMessage)msg;
		//如果是握手应答消息,需要判断是否认证成功
		if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.type) {
			byte loginResult = (byte)message.getBody();
			if (loginResult != (byte)0) {
				//握手失败,关闭连接;0表示认证通过,握手成功
				ctx.close();
			}else {
				System.out.println("login is ok :"+message);
				ctx.fireChannelRead(msg);
			}
		}else {
			
			ctx.fireChannelRead(msg);
		}
		
	}

	private NettyMessage buildLoginReq() {
		// TODO Auto-generated method stub
		NettyMessage message = new NettyMessage();
		Header header = new Header();
		header.setType(MessageType.LOGIN_REQ.type);
		message.setHeader(header);
		return message;
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		ctx.fireExceptionCaught(cause);
	}
}

package nettyAgreement;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
 * 服务端握手请求,安全认证
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月20日
 * @Version 
 * @Description
 */
public class LoginAuthRespHandler extends ChannelHandlerAdapter {

	private Map<String, Boolean> nodeCheck = new ConcurrentHashMap<String,Boolean>();
	private String[] whiteList = {"127.0.0.1","192.168.1.104"};//白名单
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		NettyMessage message = (NettyMessage)msg;
		//如果是握手请求消息处理,其它消息透传
		if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_REQ.type) {
			String nodeIndex = ctx.channel().remoteAddress().toString();//拿到远程地址
			NettyMessage loginResp = null;
			//重复登录,拒绝
			if (nodeCheck.containsKey(nodeIndex)) {
				loginResp = buildResponse((byte)-1);
			}else {
				InetSocketAddress address = (InetSocketAddress)ctx.channel().remoteAddress();
				String ip = address.getAddress().getHostAddress();
				boolean isOK = false;
				for (String Wip : whiteList) {
					if (Wip.equals(ip)) {
						isOK = true;
						break;
					}
				}
				loginResp = isOK ? buildResponse((byte)0) : buildResponse((byte)-1);
				if (isOK) 
					nodeCheck.put(nodeIndex, true);
					ConnectList.list.put(ip, ctx);
				}
				
				System.out.println("The login response is:"+loginResp+"body["+loginResp.getBody()+"]");
				ctx.writeAndFlush(loginResp);
			}else {
				ctx.fireChannelRead(msg);
			}
		}
	

	private NettyMessage buildResponse(byte result) {
		// TODO Auto-generated method stub
		NettyMessage message = new NettyMessage();
		Header header = new Header();
		header.setType(MessageType.LOGIN_RESP.type);
		message.setHeader(header);
		message.setBody(result);
		return message;
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		nodeCheck.remove(ctx.channel().remoteAddress().toString());
		ctx.fireExceptionCaught(cause);
	}
}

package nettyAgreement;
/**
 * 消息类型
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月20日
 * @Version 
 * @Description
 */
public enum MessageType {
	
	BUSINESS_REQ((byte)0,"业务请求消息"),
	BUSINESS_RESP((byte)1,"业务应答消息"),
	ONE_WAY((byte)2,"业务ONE WAY消息"),
	LOGIN_REQ((byte)3,"握手请求消息"),
	LOGIN_RESP((byte)4,"握手应答消息"),
	HEART_REQ((byte)5,"心跳请求消息"),
	HEART_RESP((byte)6,"心跳应答消息");
	
	public byte type;
	String describe;
	private MessageType(byte type, String describe) {
		this.type = type;
		this.describe = describe;
	}
	
	
}

package nettyAgreement;

public class NettyConstant {
	
	public static final String REMOTEIP = "127.0.0.1";
	public static final int PORT = 8080;
	public static final int LOCAL_PORT = 12088;
	public static final String LOCALIP = "127.0.0.1";
}

package nettyAgreement;
/**
 * 消息定义
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月18日
 * @Version 
 * @Description
 */
public final class NettyMessage {
	private Header header;//消息头
	private Object body;//消息体
	public final Header getHeader() {
		return header;
	}
	public final void setHeader(Header header) {
		this.header = header;
	}
	public final Object getBody() {
		return body;
	}
	public final void setBody(Object body) {
		this.body = body;
	}
	@Override
	public String toString() {
		return "NettyMessage [header=" + header + "]";
	}
	
}

package nettyAgreement.client;
/**
 * 客户端
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月21日
 * @Version 
 * @Description
 */

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import nettyAgreement.HeartBeatReqHandler;
import nettyAgreement.LoginAuthReqHandler;
import nettyAgreement.NettyConstant;
import nettyAgreement.decoder.NettyMessageDecoder;
import nettyAgreement.encoder.NettyMessageEncoder;

public class NettyClient {

	private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
	
	EventLoopGroup group = new NioEventLoopGroup();
	
	public void connect(int port,String host) throws InterruptedException{
		
		try {
			Bootstrap b = new Bootstrap();
			b.group(group)
			.channel(NioSocketChannel.class)
			.option(ChannelOption.TCP_NODELAY, true)
			.handler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					// TODO Auto-generated method stub
					ch.pipeline().addLast(new NettyMessageDecoder(1024 * 1024, 4, 4,-8,0));
					ch.pipeline().addLast("MessageEncoder",new NettyMessageEncoder());
					ch.pipeline().addLast("readTimeOutHandler",new ReadTimeoutHandler(50));
					ch.pipeline().addLast("LoginAuthHandler",new LoginAuthReqHandler());
					ch.pipeline().addLast("HeartBeatHandler",new HeartBeatReqHandler());
				}
			});
			//发起异步连接操作
			ChannelFuture future = b.connect(
					new InetSocketAddress(host, port), 
					new InetSocketAddress(NettyConstant.LOCALIP, NettyConstant.LOCAL_PORT)).sync();
			future.channel().closeFuture().sync();
		}
		finally {
			//所有资源释放完成之后,清空资源,再次发起重连操作
			executor.execute(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						TimeUnit.SECONDS.sleep(5);
						try {
							connect(NettyConstant.PORT, NettyConstant.REMOTEIP);//发起重连操作
						} catch (Exception e) {
							// TODO: handle exception
							e.printStackTrace();
						}
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			});
		} 
	}
	public static void main(String[] args) throws InterruptedException {
		new NettyClient().connect(NettyConstant.PORT, NettyConstant.REMOTEIP);
	}
}

package nettyAgreement.server;


import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import nettyAgreement.HeartBeatRespHandler;
import nettyAgreement.LoginAuthRespHandler;
import nettyAgreement.NettyConstant;
import nettyAgreement.decoder.NettyMessageDecoder;
import nettyAgreement.encoder.NettyMessageEncoder;

/**
 * 服务端
 * @author <font color="red"><b>Gong.YiYang</b></font>
 * @Date 2017年7月21日
 * @Version 
 * @Description
 */
public class NettyServer {
	
	public void build() throws InterruptedException{
		NioEventLoopGroup bossGroup = new NioEventLoopGroup();
		NioEventLoopGroup workGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_BACKLOG, 100)
			.handler(new LoggingHandler(LogLevel.INFO))
			.childHandler(new ChannelInitializer<SocketChannel>() {
				
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					// TODO Auto-generated method stub
					ch.pipeline().addLast(new NettyMessageDecoder(1024 * 1024, 4, 4,-8,0));
					ch.pipeline().addLast(new NettyMessageEncoder());
					ch.pipeline().addLast("readTimeOutHandler",new ReadTimeoutHandler(50));
					ch.pipeline().addLast(new LoginAuthRespHandler());
					ch.pipeline().addLast("HeartBeatHandler",new HeartBeatRespHandler());
				}
			});
			
			//绑定端口,等待同步
			 ChannelFuture future = b.bind(NettyConstant.PORT).sync();
			System.out.println("Netty server start ok :"+(NettyConstant.REMOTEIP+":"+NettyConstant.PORT));
			future.channel().closeFuture().sync();
		} finally{
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		new NettyServer().build();
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值