1、数据结构定义
1)netty消息:NettyMessage
1 package com.cherry.netty.demo.protocolstack.pojo; 2 3 import com.cherry.netty.utils.JsonUtil; 4 5 public final class NettyMessage { 6 private Header header; 7 private Object body; 8 public final Header getHeader() { 9 return header; 10 } 11 public final void setHeader(Header header) { 12 this.header = header; 13 } 14 public final Object getBody() { 15 return body; 16 } 17 public final void setBody(Object body) { 18 this.body = body; 19 } 20 @Override 21 public String toString() { 22 return "NettyMessage[header="+JsonUtil.toJson(header)+"]"; 23 } 24 25 26 27 }
2)消息头:Header
1 package com.cherry.netty.demo.protocolstack.pojo; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import com.cherry.netty.utils.JsonUtil; 7 8 public final class Header { 9 private int crcCode = 0Xabef0101; 10 private int length; 11 private long sessionID; 12 private byte type; 13 private byte priority; 14 private Map<String, Object> attachment = new HashMap<String,Object>(); 15 public final int getCrcCode() { 16 return crcCode; 17 } 18 public final void setCrcCode(int crcCode) { 19 this.crcCode = crcCode; 20 } 21 public final int getLength() { 22 return length; 23 } 24 public final void setLength(int length) { 25 this.length = length; 26 } 27 public final long getSessionID() { 28 return sessionID; 29 } 30 public final void setSessionID(long sessionID) { 31 this.sessionID = sessionID; 32 } 33 public final byte getType() { 34 return type; 35 } 36 public final void setType(byte type) { 37 this.type = type; 38 } 39 public final byte getPriority() { 40 return priority; 41 } 42 public final void setPriority(byte priority) { 43 this.priority = priority; 44 } 45 public final Map<String, Object> getAttachment() { 46 return attachment; 47 } 48 public final void setAttachment(Map<String, Object> attachment) { 49 this.attachment = attachment; 50 } 51 @Override 52 public String toString() { 53 return "Header:"+JsonUtil.toJson(this); 54 } 55 56 57 58 }
另补充MessageType、NettyConstant
1 package com.cherry.netty.demo.protocolstack.pojo; 2 3 public enum MessageType { 4 LOGIN_REQ((byte) 1), LOGIN_RESP((byte) 2), HEARTBEAT_REQ((byte) 3), HEARTBEAT_RESP((byte) 4),; 5 6 public byte value; 7 8 MessageType(byte v) { 9 this.value = v; 10 } 11 12 public byte value() { 13 return value; 14 } 15 16 }
1 package com.cherry.netty.demo.protocolstack.pojo; 2 3 4 public final class NettyConstant { 5 6 public static String LOCALIP = "127.0.0.1"; 7 public static Integer LOCAL_PORT = 9090; 8 public static String REMOTEIP = "127.0.0.1"; 9 public static Integer REMOTE_PORT = 8080; 10 11 }
2、消息编解码
① pom.xml文件。学习期间 org.jboss.marshalling的2.0.6.Final创建channel会异常。
WARN io.netty.channel.ChannelInitializer - Failed to initialize a channel. Closing: [id: 0xd6032f4c]
java.util.ServiceConfigurationError: org.jboss.marshalling.ProviderDescriptor: Provider org.jboss.marshalling.serial.SerialProviderDescriptor not found
将jboss-marshalling和jboss-marshalling-serial的版本号更新为小于2.0.6的即可。
1 <?xml version="1.0"?> 2 <project 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" 4 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 5 <modelVersion>4.0.0</modelVersion> 6 <parent> 7 <groupId>com.example</groupId> 8 <artifactId>netty</artifactId> 9 <version>0.0.1-SNAPSHOT</version> 10 </parent> 11 <groupId>com.example</groupId> 12 <artifactId>netty-demo</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>netty-demo</name> 15 <url>http://maven.apache.org</url> 16 17 <packaging>jar</packaging> 18 <properties> 19 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 20 </properties> 21 <dependencies> 22 <dependency> 23 <groupId>junit</groupId> 24 <artifactId>junit</artifactId> 25 <scope>test</scope> 26 </dependency> 27 <dependency> 28 <groupId>org.springframework.boot</groupId> 29 <artifactId>spring-boot-starter-web</artifactId> 30 </dependency> 31 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-starter-tomcat</artifactId> 35 <scope>provided</scope> 36 </dependency> 37 <dependency> 38 <groupId>org.springframework.boot</groupId> 39 <artifactId>spring-boot-starter-test</artifactId> 40 <scope>test</scope> 41 </dependency> 42 43 <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 --> 44 <dependency> 45 <groupId>org.slf4j</groupId> 46 <artifactId>slf4j-log4j12</artifactId> 47 <scope>test</scope> 48 </dependency> 49 50 <dependency> 51 <groupId>net.sf.json-lib</groupId> 52 <artifactId>json-lib</artifactId> 53 <version>2.4</version> 54 <classifier>jdk15</classifier> 55 </dependency> 56 57 <!-- https://mvnrepository.com/artifact/io.netty/netty-all --> 58 <dependency> 59 <groupId>io.netty</groupId> 60 <artifactId>netty-all</artifactId> 61 <version>5.0.0.Alpha2</version> 62 </dependency> 63 64 65 <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java --> 66 <dependency> 67 <groupId>com.google.protobuf</groupId> 68 <artifactId>protobuf-java</artifactId> 69 <version>3.6.1</version> 70 </dependency> 71 72 <!-- https://mvnrepository.com/artifact/org.jibx/jibx-run --> 73 <dependency> 74 <groupId>org.jibx</groupId> 75 <artifactId>jibx-run</artifactId> 76 <version>1.3.1</version> 77 </dependency> 78 79 <!-- https://mvnrepository.com/artifact/org.jibx/jibx-tools --> 80 <dependency> 81 <groupId>org.jibx</groupId> 82 <artifactId>jibx-tools</artifactId> 83 <version>1.3.1</version> 84 </dependency> 85 86 <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> 87 <dependency> 88 <groupId>com.google.code.gson</groupId> 89 <artifactId>gson</artifactId> 90 <version>2.8.5</version> 91 </dependency> 92 93 94 <!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling --> 95 <dependency> 96 <groupId>org.jboss.marshalling</groupId> 97 <artifactId>jboss-marshalling</artifactId> 98 <!-- <version>1.4.10.Final</version> --> 99 <version>2.0.3.Final</version> 100 </dependency> 101 102 <dependency> 103 <groupId>org.jboss.marshalling</groupId> 104 <artifactId>jboss-marshalling-serial</artifactId> 105 <!-- <version>1.4.10.Final</version> --> 106 <version>2.0.3.Final</version> 107 </dependency> 108 109 110 111 </dependencies> 112 113 <build> 114 <pluginManagement> 115 <plugins> 116 <plugin> 117 <!-- 生成jibx class信息 --> 118 <groupId>org.jibx</groupId> 119 <artifactId>jibx-maven-plugin</artifactId> 120 <version>1.3.1</version> 121 <configuration> 122 <schemaBindingDirectory>${basedir}/target/classes/jibx</schemaBindingDirectory> 123 <includeSchemaBindings> 124 <includeSchemaBindings>*binding.xml</includeSchemaBindings> 125 </includeSchemaBindings> 126 <verbose>true</verbose> 127 </configuration> 128 <executions> 129 <execution> 130 <id>jibx-bind</id> 131 <!-- 把jibx绑定到了comile编译阶段 --> 132 <phase>compile</phase> 133 <goals> 134 <goal>bind</goal> 135 </goals> 136 </execution> 137 </executions> 138 </plugin> 139 </plugins> 140 </pluginManagement> 141 </build> 142 143 </project>
②MarshallingCodeCFactory
1 package com.cherry.netty.demo.protocolstack.utils; 2 3 import java.io.IOException; 4 5 import org.jboss.marshalling.Marshaller; 6 import org.jboss.marshalling.MarshallerFactory; 7 import org.jboss.marshalling.Marshalling; 8 import org.jboss.marshalling.MarshallingConfiguration; 9 import org.jboss.marshalling.Unmarshaller; 10 11 public final class MarshallingCodeCFactory { 12 13 public static Marshaller buildMarshalling() throws IOException { 14 final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial"); 15 final MarshallingConfiguration configuration = new MarshallingConfiguration(); 16 configuration.setVersion(5); 17 Marshaller marshaller = marshallerFactory.createMarshaller(configuration); 18 return marshaller; 19 } 20 21 22 public static Unmarshaller buildUnMarshalling() throws IOException { 23 final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial"); 24 final MarshallingConfiguration configuration = new MarshallingConfiguration(); 25 configuration.setVersion(5); 26 final Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration); 27 return unmarshaller; 28 } 29 30 }
③NettyMessageEncoder、MarshallingEncoder、ChannelBufferByteOutput
1 package com.cherry.netty.demo.protocolstack.encode; 2 3 import java.io.IOException; 4 import java.util.Map; 5 6 import com.cherry.netty.demo.protocolstack.pojo.NettyMessage; 7 8 import io.netty.buffer.ByteBuf; 9 import io.netty.channel.ChannelHandlerContext; 10 import io.netty.handler.codec.MessageToByteEncoder; 11 12 public final class NettyMessageEncoder extends MessageToByteEncoder<NettyMessage> { 13 MarshallingEncoder marshallingEncoder; 14 15 public NettyMessageEncoder() throws IOException { 16 this.marshallingEncoder = new MarshallingEncoder(); 17 } 18 19 @SuppressWarnings("unused") 20 @Override 21 protected void encode(ChannelHandlerContext ctx, NettyMessage msg, ByteBuf sendBuf) throws Exception { 22 if (null == msg || null == msg.getHeader()) { 23 throw new Exception("The encode message is null"); 24 } 25 sendBuf.writeInt(msg.getHeader().getCrcCode()); 26 sendBuf.writeInt(msg.getHeader().getLength()); 27 sendBuf.writeLong(msg.getHeader().getSessionID()); 28 sendBuf.writeByte(msg.getHeader().getType()); 29 sendBuf.writeByte(msg.getHeader().getPriority()); 30 sendBuf.writeInt(msg.getHeader().getAttachment().size()); 31 32 String key = null; 33 byte[] keyArray = null; 34 Object value = null; 35 for (Map.Entry<String, Object> param : msg.getHeader().getAttachment() 36 .entrySet()) { 37 key = param.getKey(); 38 keyArray = key.getBytes("UTF-8"); 39 sendBuf.writeInt(keyArray.length); 40 sendBuf.writeBytes(keyArray); 41 value = param.getValue(); 42 } 43 key = null; 44 keyArray = null; 45 value = null; 46 47 if (msg.getBody() != null) { 48 marshallingEncoder.encode(msg.getBody(), sendBuf); 49 } else{ 50 sendBuf.writeInt(0); 51 }
// 此处需要特别注意 52 sendBuf.setInt(4, sendBuf.readableBytes() - 8); 53 } 54 }
1 package com.cherry.netty.demo.protocolstack.encode; 2 3 4 5 6 import java.io.IOException; 7 8 import org.jboss.marshalling.Marshaller; 9 10 import com.cherry.netty.demo.protocolstack.utils.MarshallingCodeCFactory; 11 12 import io.netty.buffer.ByteBuf; 13 14 public class MarshallingEncoder { 15 16 private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; 17 18 Marshaller marshaller; 19 20 21 public MarshallingEncoder() throws IOException { 22 marshaller = MarshallingCodeCFactory.buildMarshalling(); 23 } 24 25 26 protected void encode(Object msg, ByteBuf out) throws IOException { 27 try { 28 int lengthPos = out.writerIndex(); 29 out.writeBytes(LENGTH_PLACEHOLDER); 30 ChannelBufferByteOutput output = new ChannelBufferByteOutput(out); 31 marshaller.start(output); 32 marshaller.writeObject(msg); 33 marshaller.finish(); 34 out.setInt(lengthPos, out.writerIndex() - lengthPos - 4); 35 } finally { 36 marshaller.close(); 37 } 38 } 39 40 }
1 package com.cherry.netty.demo.protocolstack.encode; 2 3 import io.netty.buffer.ByteBuf; 4 import org.jboss.marshalling.ByteOutput; 5 6 import java.io.IOException; 7 8 class ChannelBufferByteOutput implements ByteOutput { 9 10 private final ByteBuf buffer; 11 12 public ChannelBufferByteOutput(ByteBuf buffer) { 13 this.buffer = buffer; 14 } 15 16 @Override 17 public void close() throws IOException { 18 } 19 20 @Override 21 public void flush() throws IOException { 22 } 23 24 @Override 25 public void write(int b) throws IOException { 26 buffer.writeByte(b); 27 } 28 29 @Override 30 public void write(byte[] bytes) throws IOException { 31 buffer.writeBytes(bytes); 32 } 33 34 @Override 35 public void write(byte[] bytes, int srcIndex, int length) throws IOException { 36 buffer.writeBytes(bytes, srcIndex, length); 37 } 38 39 40 ByteBuf getBuffer() { 41 return buffer; 42 } 43 }
④NettyMessageDecoder、MarshallingDecoder、ChannelBufferByteInput
1 package com.cherry.netty.demo.protocolstack.decode; 2 3 import java.io.IOException; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import com.cherry.netty.demo.protocolstack.pojo.Header; 8 import com.cherry.netty.demo.protocolstack.pojo.NettyMessage; 9 10 import io.netty.buffer.ByteBuf; 11 import io.netty.channel.ChannelHandlerContext; 12 import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 13 14 public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder { 15 MarshallingDecoder marshallingDecoder; 16 17 public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, 18 int lengthFieldLength) throws IOException { 19 super(maxFrameLength, lengthFieldOffset, lengthFieldLength); 20 marshallingDecoder = new MarshallingDecoder(); 21 } 22 23 @Override 24 protected Object decode(ChannelHandlerContext ctx, ByteBuf in) 25 throws Exception { 26 ByteBuf frame = (ByteBuf) super.decode(ctx, in); 27 if (frame == null) { 28 return null; 29 } 30 31 NettyMessage message = new NettyMessage(); 32 Header header = new Header(); 33 header.setCrcCode(frame.readInt()); 34 header.setLength(frame.readInt()); 35 header.setSessionID(frame.readLong()); 36 header.setType(frame.readByte()); 37 header.setPriority(frame.readByte()); 38 39 int size = frame.readInt(); 40 if (size > 0) { 41 Map<String, Object> attch = new HashMap<String, Object>(size); 42 int keySize = 0; 43 byte[] keyArray = null; 44 String key = null; 45 for (int i = 0; i < size; i++) { 46 keySize = frame.readInt(); 47 keyArray = new byte[keySize]; 48 frame.readBytes(keyArray); 49 key = new String(keyArray, "UTF-8"); 50 attch.put(key, marshallingDecoder.decode(frame)); 51 } 52 keyArray = null; 53 key = null; 54 header.setAttachment(attch); 55 } 56 if (frame.readableBytes() > 4) { 57 message.setBody(marshallingDecoder.decode(frame)); 58 } 59 message.setHeader(header); 60 return message; 61 } 62 }
1 package com.cherry.netty.demo.protocolstack.decode; 2 3 4 import java.io.IOException; 5 6 import org.jboss.marshalling.ByteInput; 7 import org.jboss.marshalling.Unmarshaller; 8 9 import com.cherry.netty.demo.protocolstack.utils.MarshallingCodeCFactory; 10 11 import io.netty.buffer.ByteBuf; 12 13 public class MarshallingDecoder { 14 15 private final Unmarshaller unmarshaller; 16 17 18 19 public MarshallingDecoder() throws IOException { 20 unmarshaller = MarshallingCodeCFactory.buildUnMarshalling(); 21 } 22 23 protected Object decode(ByteBuf in) throws IOException, ClassNotFoundException { 24 int objectSize = in.readInt(); 25 ByteBuf buf = in.slice(in.readerIndex(), objectSize); 26 ByteInput input = new ChannelBufferByteInput(buf); 27 try { 28 unmarshaller.start(input); 29 Object obj = unmarshaller.readObject(); 30 unmarshaller.finish(); 31 in.readerIndex(in.readerIndex() + objectSize); 32 return obj; 33 } finally { 34 unmarshaller.close(); 35 } 36 } 37 38 }
1 package com.cherry.netty.demo.protocolstack.decode; 2 3 import io.netty.buffer.ByteBuf; 4 import org.jboss.marshalling.ByteInput; 5 6 import java.io.IOException; 7 8 9 class ChannelBufferByteInput implements ByteInput { 10 11 private final ByteBuf buffer; 12 13 public ChannelBufferByteInput(ByteBuf buffer) { 14 this.buffer = buffer; 15 } 16 17 @Override 18 public void close() throws IOException { 19 // nothing to do 20 } 21 22 @Override 23 public int available() throws IOException { 24 return buffer.readableBytes(); 25 } 26 27 @Override 28 public int read() throws IOException { 29 if (buffer.isReadable()) { 30 return buffer.readByte() & 0xff; 31 } 32 return -1; 33 } 34 35 @Override 36 public int read(byte[] array) throws IOException { 37 return read(array, 0, array.length); 38 } 39 40 @Override 41 public int read(byte[] dst, int dstIndex, int length) throws IOException { 42 int available = available(); 43 if (available == 0) { 44 return -1; 45 } 46 47 length = Math.min(available, length); 48 buffer.readBytes(dst, dstIndex, length); 49 return length; 50 } 51 52 @Override 53 public long skip(long bytes) throws IOException { 54 int readable = buffer.readableBytes(); 55 if (readable < bytes) { 56 bytes = readable; 57 } 58 buffer.readerIndex((int) (buffer.readerIndex() + bytes)); 59 return bytes; 60 } 61 62 }
3、握手和安全认证
1 package com.cherry.netty.demo.protocolstack.loginhandler; 2 3 import org.apache.log4j.Logger; 4 5 import com.cherry.netty.demo.protocolstack.pojo.Header; 6 import com.cherry.netty.demo.protocolstack.pojo.MessageType; 7 import com.cherry.netty.demo.protocolstack.pojo.NettyMessage; 8 9 import io.netty.channel.ChannelHandlerAdapter; 10 import io.netty.channel.ChannelHandlerContext; 11 12 public class LoginAuthReqHandler extends ChannelHandlerAdapter { 13 14 private static final Logger logger = Logger.getLogger(LoginAuthReqHandler.class); 15 16 @Override 17 public void channelActive(ChannelHandlerContext ctx) throws Exception { 18 ctx.writeAndFlush(buildLoginReq()); 19 } 20 21 22 @Override 23 public void channelRead(ChannelHandlerContext ctx, Object msg) 24 throws Exception { 25 NettyMessage message = (NettyMessage) msg; 26 27 if (message.getHeader() != null 28 && message.getHeader().getType() == MessageType.LOGIN_RESP.value()) { 29 byte loginResult = (byte) message.getBody(); 30 if (loginResult != (byte) 0) { 31 ctx.close(); 32 } else { 33 logger.info("Login is ok : " + message); 34 ctx.fireChannelRead(msg); 35 } 36 } else 37 ctx.fireChannelRead(msg); 38 } 39 40 41 private NettyMessage buildLoginReq() { 42 NettyMessage message = new NettyMessage(); 43 Header header = new Header(); 44 header.setType(MessageType.LOGIN_REQ.value()); 45 message.setHeader(header); 46 return message; 47 } 48 49 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 50 throws Exception { 51 ctx.fireExceptionCaught(cause); 52 } 53 54 }
1 package com.cherry.netty.demo.protocolstack.loginhandler; 2 3 import java.net.InetSocketAddress; 4 import java.util.Map; 5 import java.util.concurrent.ConcurrentHashMap; 6 7 import org.apache.log4j.Logger; 8 9 import com.cherry.netty.demo.protocolstack.pojo.Header; 10 import com.cherry.netty.demo.protocolstack.pojo.MessageType; 11 import com.cherry.netty.demo.protocolstack.pojo.NettyMessage; 12 13 import io.netty.channel.ChannelHandlerAdapter; 14 import io.netty.channel.ChannelHandlerContext; 15 16 public class LoginAuthRespHandler extends ChannelHandlerAdapter { 17 18 private static final Logger logger = Logger.getLogger(LoginAuthRespHandler.class); 19 20 private Map<String, Boolean> nodeCheck = new ConcurrentHashMap<String, Boolean>(); 21 private String[] whitekList = { "127.0.0.1", "192.168.11.246", "192.168.31.242" }; 22 23 @Override 24 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 25 NettyMessage message = (NettyMessage) msg; 26 27 if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_REQ.value()) { 28 String nodeIndex = ctx.channel().remoteAddress().toString(); 29 NettyMessage loginResp = null; 30 // 重复登陆,拒绝 31 if (nodeCheck.containsKey(nodeIndex)) { 32 loginResp = buildResponse((byte) -1); 33 } else { 34 InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress(); 35 String ip = address.getAddress().getHostAddress(); 36 boolean isOK = false; 37 for (String WIP : whitekList) { 38 if (WIP.equals(ip)) { 39 isOK = true; 40 break; 41 } 42 } 43 loginResp = isOK ? buildResponse((byte) 0) : buildResponse((byte) -1); 44 if (isOK) 45 nodeCheck.put(nodeIndex, true); 46 } 47 logger.info("The login response is : " + loginResp + " body [" + loginResp.getBody() + "]"); 48 ctx.writeAndFlush(loginResp); 49 } else { 50 ctx.fireChannelRead(msg); 51 } 52 } 53 54 private NettyMessage buildResponse(byte result) { 55 NettyMessage message = new NettyMessage(); 56 Header header = new Header(); 57 header.setType(MessageType.LOGIN_RESP.value()); 58 message.setHeader(header); 59 message.setBody(result); 60 return message; 61 } 62 63 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 64 cause.printStackTrace(); 65 nodeCheck.remove(ctx.channel().remoteAddress().toString()); 66 ctx.close(); 67 ctx.fireExceptionCaught(cause); 68 } 69 }
4、心跳检测机制
1 package com.cherry.netty.demo.protocolstack.hearthandler; 2 3 import java.util.concurrent.TimeUnit; 4 5 import com.cherry.netty.demo.protocolstack.pojo.Header; 6 import com.cherry.netty.demo.protocolstack.pojo.MessageType; 7 import com.cherry.netty.demo.protocolstack.pojo.NettyMessage; 8 9 import io.netty.channel.ChannelHandlerAdapter; 10 import io.netty.channel.ChannelHandlerContext; 11 import io.netty.util.concurrent.ScheduledFuture; 12 13 14 public class HeartBeatReqHandler extends ChannelHandlerAdapter { 15 16 private volatile ScheduledFuture<?> heartBeat; 17 18 @Override 19 public void channelActive(ChannelHandlerContext ctx) throws Exception { 20 // TODO Auto-generated method stub 21 super.channelActive(ctx); 22 } 23 24 @Override 25 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 26 // TODO Auto-generated method stub 27 NettyMessage message = (NettyMessage) msg; 28 if(message.getHeader() != null && message.getHeader().getType()==MessageType.LOGIN_RESP.value()){ 29 heartBeat = ctx.executor().scheduleAtFixedRate(new HeartBeatReqHandler.HeartBeatTask(ctx), 0, 5000, TimeUnit.MILLISECONDS); 30 }else if(message.getHeader() !=null && message.getHeader().getType()==MessageType.HEARTBEAT_RESP.value()){ 31 System.out.println("Client receive server heart beat message:---> "+message); 32 }else{ 33 ctx.fireChannelRead(msg); 34 } 35 } 36 37 private class HeartBeatTask implements Runnable{ 38 39 private final ChannelHandlerContext ctx; 40 41 public HeartBeatTask(final ChannelHandlerContext ctx) { 42 this.ctx = ctx; 43 } 44 45 @Override 46 public void run() { 47 NettyMessage heatBeat = buildHeatBeat(); 48 System.out.println("Client send heart beat message to server:---> "+heatBeat); 49 ctx.writeAndFlush(heatBeat); 50 } 51 52 private NettyMessage buildHeatBeat() { 53 NettyMessage message = new NettyMessage(); 54 Header header = new Header(); 55 header.setType(MessageType.HEARTBEAT_REQ.value()); 56 message.setHeader(header); 57 return message; 58 } 59 } 60 61 @Override 62 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 63 if(heartBeat != null){ 64 heartBeat.cancel(true); 65 heartBeat = null; 66 } 67 ctx.fireExceptionCaught(cause); 68 } 69 70 71 }
1 package com.cherry.netty.demo.protocolstack.hearthandler; 2 3 import com.cherry.netty.demo.protocolstack.pojo.Header; 4 import com.cherry.netty.demo.protocolstack.pojo.MessageType; 5 import com.cherry.netty.demo.protocolstack.pojo.NettyMessage; 6 7 import io.netty.channel.ChannelHandlerAdapter; 8 import io.netty.channel.ChannelHandlerContext; 9 10 public class HeartBeatRespHandler extends ChannelHandlerAdapter { 11 12 @Override 13 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 14 NettyMessage message = (NettyMessage) msg; 15 if(message.getHeader() != null && message.getHeader().getType()==MessageType.HEARTBEAT_REQ.value()){ 16 System.out.println("Server receive client heart beat message:---> "+message); 17 NettyMessage heartBeat = buildHeatBeat(); 18 System.out.println("Server send heart beat response message to client:---> "+heartBeat); 19 ctx.writeAndFlush(heartBeat); 20 }else{ 21 ctx.fireChannelRead(msg); 22 } 23 } 24 25 private NettyMessage buildHeatBeat() { 26 NettyMessage message = new NettyMessage(); 27 Header header = new Header(); 28 header.setType(MessageType.HEARTBEAT_RESP.value()); 29 message.setHeader(header); 30 return message; 31 } 32 33 34 }
5、客户端
1 package com.cherry.netty.demo.protocolstack; 2 3 import java.net.InetSocketAddress; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.ScheduledExecutorService; 6 import java.util.concurrent.TimeUnit; 7 8 import com.cherry.netty.demo.protocolstack.decode.NettyMessageDecoder; 9 import com.cherry.netty.demo.protocolstack.encode.NettyMessageEncoder; 10 import com.cherry.netty.demo.protocolstack.hearthandler.HeartBeatReqHandler; 11 import com.cherry.netty.demo.protocolstack.loginhandler.LoginAuthReqHandler; 12 import com.cherry.netty.demo.protocolstack.pojo.NettyConstant; 13 14 import io.netty.bootstrap.Bootstrap; 15 import io.netty.channel.ChannelFuture; 16 import io.netty.channel.ChannelInitializer; 17 import io.netty.channel.ChannelOption; 18 import io.netty.channel.EventLoopGroup; 19 import io.netty.channel.nio.NioEventLoopGroup; 20 import io.netty.channel.socket.SocketChannel; 21 import io.netty.channel.socket.nio.NioSocketChannel; 22 import io.netty.handler.timeout.ReadTimeoutHandler; 23 24 public class NettyClient { 25 26 private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 27 28 29 public void connect(int port,String host) throws InterruptedException{ 30 EventLoopGroup group = new NioEventLoopGroup(); 31 try { 32 Bootstrap b = new Bootstrap(); 33 b.group(group).channel(NioSocketChannel.class) 34 .option(ChannelOption.TCP_NODELAY, true) 35 .handler(new ChannelInitializer<SocketChannel>() { 36 37 @Override 38 protected void initChannel(SocketChannel ch) throws Exception { 39 ch.pipeline().addLast(new NettyMessageDecoder(1024*1024, 4, 4)); 40 ch.pipeline().addLast("meaageEncoder",new NettyMessageEncoder()); 41 ch.pipeline().addLast("readTimeoutHandler",new ReadTimeoutHandler(50)); 42 ch.pipeline().addLast("loginAuthHandler",new LoginAuthReqHandler()); 43 ch.pipeline().addLast("heartBeatHandler",new HeartBeatReqHandler()); 44 45 } 46 47 }); 48 // 发起异步连接操作 49 ChannelFuture future = b.connect( 50 new InetSocketAddress(host, port), 51 new InetSocketAddress(NettyConstant.LOCALIP, NettyConstant.LOCAL_PORT)).sync(); 52 System.out.println("Netty client start ok . remoteAddress( "+host+":"+port+"),localAddress("+NettyConstant.LOCALIP+":"+NettyConstant.LOCAL_PORT+")"); 53 54 future.channel().closeFuture().sync(); 55 56 } finally { 57 executor.execute(new Runnable() { 58 59 @Override 60 public void run() { 61 try { 62 TimeUnit.SECONDS.sleep(5); 63 try { 64 // 发起重连操作 65 connect(NettyConstant.REMOTE_PORT, NettyConstant.REMOTEIP); 66 } catch (Exception e) { 67 System.out.println("NettyClient 发起重连操作异常"); 68 e.printStackTrace(); 69 } 70 } catch (InterruptedException e) { 71 e.printStackTrace(); 72 } 73 } 74 }); 75 } 76 } 77 public static void main(String[] args) throws InterruptedException { 78 new NettyClient().connect(NettyConstant.REMOTE_PORT, NettyConstant.REMOTEIP); 79 } 80 }
6、服务端
1 package com.cherry.netty.demo.protocolstack; 2 3 import com.cherry.netty.demo.protocolstack.decode.NettyMessageDecoder; 4 import com.cherry.netty.demo.protocolstack.encode.NettyMessageEncoder; 5 import com.cherry.netty.demo.protocolstack.hearthandler.HeartBeatRespHandler; 6 import com.cherry.netty.demo.protocolstack.loginhandler.LoginAuthRespHandler; 7 import com.cherry.netty.demo.protocolstack.pojo.NettyConstant; 8 9 import io.netty.bootstrap.ServerBootstrap; 10 import io.netty.channel.ChannelFuture; 11 import io.netty.channel.ChannelInitializer; 12 import io.netty.channel.ChannelOption; 13 import io.netty.channel.EventLoopGroup; 14 import io.netty.channel.nio.NioEventLoopGroup; 15 import io.netty.channel.socket.SocketChannel; 16 import io.netty.channel.socket.nio.NioServerSocketChannel; 17 import io.netty.handler.logging.LogLevel; 18 import io.netty.handler.logging.LoggingHandler; 19 import io.netty.handler.timeout.ReadTimeoutHandler; 20 21 public class NettyServer { 22 23 public void bind() throws InterruptedException{ 24 EventLoopGroup bossGroup = new NioEventLoopGroup(); 25 EventLoopGroup workerGroup = new NioEventLoopGroup(); 26 ServerBootstrap b = new ServerBootstrap(); 27 b.group(bossGroup, workerGroup) 28 .channel(NioServerSocketChannel.class) 29 .option(ChannelOption.SO_BACKLOG, 100) 30 .handler(new LoggingHandler(LogLevel.INFO)) 31 .childHandler(new ChannelInitializer<SocketChannel>() { 32 33 @Override 34 protected void initChannel(SocketChannel ch) throws Exception { 35 ch.pipeline().addLast(new NettyMessageDecoder(1024*1024, 4, 4)); 36 ch.pipeline().addLast(new NettyMessageEncoder()); 37 ch.pipeline().addLast("readTimeoutHandler",new ReadTimeoutHandler(50)); 38 ch.pipeline().addLast(new LoginAuthRespHandler()); 39 ch.pipeline().addLast("HeartBeatHandler",new HeartBeatRespHandler()); 40 } 41 }); 42 43 ChannelFuture future = b.bind(NettyConstant.REMOTEIP,NettyConstant.REMOTE_PORT).sync(); 44 System.out.println("Netty server start ok : "+(NettyConstant.REMOTEIP+":"+NettyConstant.REMOTE_PORT)); 45 46 future.channel().closeFuture().sync(); 47 } 48 public static void main(String[] args) throws InterruptedException { 49 new NettyServer().bind(); 50 } 51 52 }