基于netty+JBoss搭建的自定义开发框架,参考《netty权威指南第二版》
1.Maven依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.20.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling</artifactId>
<version>1.4.10.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
<version>1.4.10.Final</version>
</dependency>
2.MarshallingCodecFactory工厂
**
* @PackageName:com.netty.obj.demo3 Description
* @author:
* @date:2021/12/23
*/
public final class MarshallingCodecFactory {
/**
* 创建Jboss Marshaller
*
* @return
* @throws IOException
*/
public static Marshaller buildMarshalling() throws IOException {
final MarshallerFactory marshallerFactory = Marshalling
.getProvidedMarshallerFactory("serial");
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
Marshaller marshaller = marshallerFactory
.createMarshaller(configuration);
return marshaller;
}
/**
* 创建Jboss Unmarshaller
*
* @return
* @throws IOException
*/
public static Unmarshaller buildUnMarshalling() throws IOException {
final MarshallerFactory marshallerFactory = Marshalling
.getProvidedMarshallerFactory("serial");
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
final Unmarshaller unmarshaller = marshallerFactory
.createUnmarshaller(configuration);
return unmarshaller;
}
}
3.MarshallingEncoder编码器
import com.netty.obj.demo3.buff.ChannelBufferByteOutput;
import com.netty.obj.demo3.factory.MarshallingCodecFactory;
import io.netty.buffer.ByteBuf;
import org.jboss.marshalling.Marshaller;
import java.io.IOException;
/**
* @PackageName:com.netty.obj.demo2.code Description NettyMessage消息编码类
* @author:
* @date:2021/12/23
*/
public class MarshallingEncoder {
private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
Marshaller marshaller;
public MarshallingEncoder() throws IOException {
marshaller = MarshallingCodecFactory.buildMarshalling();
}
protected void encode(Object msg, ByteBuf out) throws Exception {
try {
int lengthPos = out.writerIndex();
out.writeBytes(LENGTH_PLACEHOLDER);
ChannelBufferByteOutput output = new ChannelBufferByteOutput(out);
marshaller.start(output);
marshaller.writeObject(msg);
marshaller.finish();
out.setInt(lengthPos, out.writerIndex() - lengthPos - 4);
} finally {
marshaller.close();
}
}
}
4.MarshallingDecoder编码器
package com.netty.obj.demo3.code;
import com.netty.obj.demo3.buff.ChannelBufferByteInput;
import com.netty.obj.demo3.factory.MarshallingCodecFactory;
import io.netty.buffer.ByteBuf;
import org.jboss.marshalling.ByteInput;
import org.jboss.marshalling.Unmarshaller;
import java.io.IOException;
/**
* @PackageName:com.netty.obj.demo3 Description
* @author:
* @date:2021/12/23
*/
public class MarshallingDecoder {
private final Unmarshaller unmarshaller;
/**
* Creates a new decoder whose maximum object size is {@code 1048576} bytes.
* If the size of the received object is greater than {@code 1048576} bytes,
* a {@link StreamCorruptedException} will be raised.
*
* @throws IOException
*/
public MarshallingDecoder() throws IOException {
unmarshaller = MarshallingCodecFactory.buildUnMarshalling();
}
protected Object decode(ByteBuf in) throws Exception {
int objectSize = in.readInt();
ByteBuf buf = in.slice(in.readerIndex(), objectSize);
ByteInput input = new ChannelBufferByteInput(buf);
try {
unmarshaller.start(input);
Object obj = unmarshaller.readObject();
unmarshaller.finish();
in.readerIndex(in.readerIndex() + objectSize);
return obj;
} finally {
unmarshaller.close();
}
}
}
5.Buffer
package com.netty.obj.demo3.buff;
import io.netty.buffer.ByteBuf;
import org.jboss.marshalling.ByteInput;
import java.io.IOException;
/**
* @PackageName:com.netty.obj.demo3 Description
* @author:
* @date:2021/12/23
*/
public class ChannelBufferByteInput implements ByteInput {
private final ByteBuf buffer;
public ChannelBufferByteInput(ByteBuf buffer) {
this.buffer = buffer;
}
@Override
public void close() throws IOException {
// nothing to do
}
@Override
public int available() throws IOException {
return buffer.readableBytes();
}
@Override
public int read() throws IOException {
if (buffer.isReadable()) {
return buffer.readByte() & 0xff;
}
return -1;
}
@Override
public int read(byte[] array) throws IOException {
return read(array, 0, array.length);
}
@Override
public int read(byte[] dst, int dstIndex, int length) throws IOException {
int available = available();
if (available == 0) {
return -1;
}
length = Math.min(available, length);
buffer.readBytes(dst, dstIndex, length);
return length;
}
@Override
public long skip(long bytes) throws IOException {
int readable = buffer.readableBytes();
if (readable < bytes) {
bytes = readable;
}
buffer.readerIndex((int) (buffer.readerIndex() + bytes));
return bytes;
}
}
package com.netty.obj.demo3.buff;
import io.netty.buffer.ByteBuf;
import org.jboss.marshalling.ByteOutput;
import java.io.IOException;
/**
* @PackageName:com.netty.obj.demo3 Description
* @author:
* @date:2021/12/23
*/
public class ChannelBufferByteOutput implements ByteOutput {
private final ByteBuf buffer;
/**
* Create a new instance which use the given {@link ByteBuf}
*/
public ChannelBufferByteOutput(ByteBuf buffer) {
this.buffer = buffer;
}
@Override
public void close() throws IOException {
// Nothing to do
}
@Override
public void flush() throws IOException {
// nothing to do
}
@Override
public void write(int b) throws IOException {
buffer.writeByte(b);
}
@Override
public void write(byte[] bytes) throws IOException {
buffer.writeBytes(bytes);
}
@Override
public void write(byte[] bytes, int srcIndex, int length) throws IOException {
buffer.writeBytes(bytes, srcIndex, length);
}
/**
* Return the {@link ByteBuf} which contains the written content
*
*/
ByteBuf getBuffer() {
return buffer;
}
}
6.自定义消息
package com.netty.obj.demo3.bean;
/**
* @PackageName:com.netty.obj.demo3
* Description 消息定义类
* @author:
* @date:2021/12/23
*/
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 +
", body=" + body +
'}';
}
}
7.自定义消息头
package com.netty.obj.demo3.bean;
import java.util.HashMap;
import java.util.Map;
/**
* @PackageName:com.netty.obj.demo3
* Description 消息头定义类
* @author:
* @date:2021/12/23
*/
public final class Header {
private int crcCode = 0xabef0101;
private int length;// 消息长度
private long sessionID;// 会话ID
private byte type;// 消息类型
private byte priority;// 消息优先级
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 +
'}';
}
}
8.消息编码器
package com.netty.obj.demo3.code;
import com.netty.obj.demo3.bean.NettyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.io.IOException;
import java.util.Map;
/**
* @PackageName:com.netty.obj.demo2.code Description NettyMessage消息编码类
* @author:
* @date:2021/12/23
*/
public class NettyMessageEncoder extends MessageToByteEncoder<NettyMessage> {
MarshallingEncoder marshallingEncoder;
public NettyMessageEncoder() throws IOException {
this.marshallingEncoder = new MarshallingEncoder();
}
@Override
protected void encode(ChannelHandlerContext ctx, NettyMessage msg, ByteBuf sendBuf) throws Exception {
if (null == msg || null == msg.getHeader()) {
throw new Exception("The encode message is null");
}
sendBuf.writeInt((msg.getHeader().getCrcCode()));
sendBuf.writeInt((msg.getHeader().getLength()));
sendBuf.writeLong((msg.getHeader().getSessionID()));
sendBuf.writeByte((msg.getHeader().getType()));
sendBuf.writeByte((msg.getHeader().getPriority()));
sendBuf.writeInt((msg.getHeader().getAttachment().size()));
String key = null;
byte[] keyArray = null;
Object value = null;
for (Map.Entry<String, Object> param : msg.getHeader().getAttachment()
.entrySet()) {
key = param.getKey();
keyArray = key.getBytes("UTF-8");
sendBuf.writeInt(keyArray.length);
sendBuf.writeBytes(keyArray);
value = param.getValue();
marshallingEncoder.encode(value, sendBuf);
}
key = null;
keyArray = null;
value = null;
if (msg.getBody() != null) {
marshallingEncoder.encode(msg.getBody(), sendBuf);
} else {
sendBuf.writeInt(0);
}
sendBuf.setInt(4, sendBuf.readableBytes() - 8);
}
}
9.消息解码器
package com.netty.obj.demo3.code;
import com.netty.obj.demo3.bean.Header;
import com.netty.obj.demo3.bean.NettyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @PackageName:com.netty.obj.demo3 Description
* @author:
* @date:2021/12/23
*/
public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder {
MarshallingDecoder marshallingDecoder;
public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset,
int lengthFieldLength) throws IOException {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
marshallingDecoder = new MarshallingDecoder();
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in)
throws Exception {
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(frame));
}
keyArray = null;
key = null;
header.setAttachment(attch);
}
if (frame.readableBytes() > 4) {
message.setBody(marshallingDecoder.decode(frame));
}
message.setHeader(header);
return message;
}
}
10.握手与安全认证
package com.netty.obj.demo3.handler;
import com.netty.obj.demo3.bean.Header;
import com.netty.obj.demo3.util.MessageType;
import com.netty.obj.demo3.bean.NettyMessage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @PackageName:com.netty.obj.demo3.handler Description
* @author:
* @date:2021/12/23
*/
public class LoginAuthReqHandler extends ChannelInboundHandlerAdapter {
private static final Log LOG = LogFactory.getLog(LoginAuthReqHandler.class);
/**
* Calls {@link ChannelHandlerContext#fireChannelActive()} to forward to the
* next {@link ChannelHandler} in the {@link ChannelPipeline}.
* <p/>
* Sub-classes may override this method to change behavior.
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(buildLoginReq());
}
/**
* Calls {@link ChannelHandlerContext#fireChannelRead(Object)} to forward to
* the next {@link ChannelHandler} in the {@link ChannelPipeline}.
* <p/>
* Sub-classes may override this method to change behavior.
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
NettyMessage message = (NettyMessage) msg;
// 如果是握手应答消息,需要判断是否认证成功
if (message.getHeader() != null
&& message.getHeader().getType() == MessageType.LOGIN_RESP) {
byte loginResult = (byte) message.getBody();
if (loginResult != (byte) 0) {
// 握手失败,关闭连接
ctx.close();
} else {
LOG.info("Login is ok : " + message);
ctx.fireChannelRead(msg);
}
} else{
//调用下一个channel链..
ctx.fireChannelRead(msg);
}
}
private NettyMessage buildLoginReq() {
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_REQ);
message.setHeader(header);
return message;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.fireExceptionCaught(cause);
}
}
package com.netty.obj.demo3.handler;
import com.netty.obj.demo3.bean.Header;
import com.netty.obj.demo3.util.MessageType;
import com.netty.obj.demo3.bean.NettyMessage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @PackageName:com.netty.obj.demo3.handler Description
* @author:
* @date:2021/12/23
*/
public class LoginAuthRespHandler extends ChannelInboundHandlerAdapter {
private final static Log LOG = LogFactory.getLog(LoginAuthRespHandler.class);
/**
* 本地缓存
*/
private Map<String, Boolean> nodeCheck = new ConcurrentHashMap<String, Boolean>();
private String[] whitekList = {"127.0.0.1", "192.168.1.104"};
/**
* Calls {@link ChannelHandlerContext#fireChannelRead(Object)} to forward to
* the next {@link ChannelHandler} in the {@link ChannelPipeline}.
* <p>
* Sub-classes may override this method to change behavior.
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
NettyMessage message = (NettyMessage) msg;
// 如果是握手请求消息,处理,其它消息透传
if (message.getHeader() != null
&& message.getHeader().getType() == MessageType.LOGIN_REQ) {
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 : whitekList) {
if (WIP.equals(ip)) {
isOK = true;
break;
}
}
loginResp = isOK ? buildResponse((byte) 0)
: buildResponse((byte) -1);
if (isOK){
nodeCheck.put(nodeIndex, true);
}
}
LOG.info("The login response is : " + loginResp
+ " body [" + loginResp.getBody() + "]");
ctx.writeAndFlush(loginResp);
} else {
ctx.fireChannelRead(msg);
}
}
private NettyMessage buildResponse(byte result) {
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_RESP);
message.setHeader(header);
message.setBody(result);
return message;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
nodeCheck.remove(ctx.channel().remoteAddress().toString());// 删除缓存
ctx.close();
ctx.fireExceptionCaught(cause);
}
}
11.心跳机制处理
package com.netty.obj.demo3.handler;
import com.netty.obj.demo3.bean.Header;
import com.netty.obj.demo3.util.MessageType;
import com.netty.obj.demo3.bean.NettyMessage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.concurrent.ScheduledFuture;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.concurrent.TimeUnit;
/**
* @PackageName:com.netty.obj.demo3.handler Description
* @author:
* @date:2021/12/23
*/
public class HeartBeatReqHandler extends ChannelInboundHandlerAdapter {
private static final Log LOG = LogFactory.getLog(HeartBeatReqHandler.class);
//使用定时任务发送
private volatile ScheduledFuture<?> heartBeat;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
NettyMessage message = (NettyMessage) msg;
// 当握手成功后,Login响应向下透传,主动发送心跳消息
if (message.getHeader() != null
&& message.getHeader().getType() == MessageType.LOGIN_RESP) {
//NioEventLoop是一个Schedule,因此支持定时器的执行,创建心跳计时器
heartBeat = ctx.executor().scheduleAtFixedRate(
new HeartBeatReqHandler.HeartBeatTask(ctx), 0, 5000,
TimeUnit.MILLISECONDS);
} else if (message.getHeader() != null
&& message.getHeader().getType() == MessageType.HEARTBEAT_RESP
) {
LOG.info("Client receive server heart beat message : ---> "
+ message);
} else{
ctx.fireChannelRead(msg);
}
}
//Ping消息任务类
private class HeartBeatTask implements Runnable {
private final ChannelHandlerContext ctx;
public HeartBeatTask(final ChannelHandlerContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
NettyMessage heatBeat = buildHeatBeat();
LOG.info("Client send heart beat messsage to server : ---> "
+ heatBeat);
ctx.writeAndFlush(heatBeat);
}
private NettyMessage buildHeatBeat() {
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.HEARTBEAT_REQ);
message.setHeader(header);
return message;
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
if (heartBeat != null) {
heartBeat.cancel(true);
heartBeat = null;
}
ctx.fireExceptionCaught(cause);
}
}
package com.netty.obj.demo3.handler;
import com.netty.obj.demo3.bean.Header;
import com.netty.obj.demo3.util.MessageType;
import com.netty.obj.demo3.bean.NettyMessage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @PackageName:com.netty.obj.demo3.handler Description
* @author:
* @date:2021/12/23
*/
public class HeartBeatRespHandler extends ChannelInboundHandlerAdapter {
private static final Log LOG = LogFactory.getLog(HeartBeatRespHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
NettyMessage message = (NettyMessage) msg;
// 返回心跳应答消息
if (message.getHeader() != null
&& message.getHeader().getType() == MessageType.HEARTBEAT_REQ) {
LOG.info("Receive client heart beat message : ---> "
+ message);
NettyMessage heartBeat = buildHeatBeat();
LOG.info("Send heart beat response message to client : ---> "
+ heartBeat);
ctx.writeAndFlush(heartBeat);
} else{
ctx.fireChannelRead(msg);
}
}
private NettyMessage buildHeatBeat() {
NettyMessage message = new NettyMessage();
Header header = new Header();
header.setType(MessageType.HEARTBEAT_RESP);
message.setHeader(header);
return message;
}
}
12.util工具类
package com.netty.obj.demo3.util;
/**
* @PackageName:com.netty.obj.demo3.bean Description
* @author:
* @date:2021/12/23
*/
public class MessageType {
/**
* 握手请求消息
*/
public static final byte LOGIN_REQ=3;
/**
* 握手应答消息
*/
public static final byte LOGIN_RESP=4;
/**
* 心跳请求
*/
public static final byte HEARTBEAT_REQ=5;
/**
* 心跳应答
*/
public static final byte HEARTBEAT_RESP=6;
}
package com.netty.obj.demo3.util;
/**
* @PackageName:com.netty.obj.demo3.util Description
* @author:
* @date:2021/12/23
*/
public final class NettyConstant {
public static final String REMOTEIP = "127.0.0.1";
public static final int PORT = 8085;
public static final int LOCAL_PORT = 12088;
public static final String LOCALIP = "127.0.0.1";
}
13.引导服务端
package com.netty.obj.demo3.server;
import com.netty.obj.demo3.code.NettyMessageDecoder;
import com.netty.obj.demo3.code.NettyMessageEncoder;
import com.netty.obj.demo3.handler.HeartBeatRespHandler;
import com.netty.obj.demo3.handler.LoginAuthRespHandler;
import com.netty.obj.demo3.util.NettyConstant;
import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
/**
* @PackageName:com.netty.obj.demo3.server Description
* @author:
* @date:2021/12/23
*/
public class NettyServer {
private static final Log LOG = LogFactory.getLog(NettyServer.class);
public void bind() throws Exception {
// 配置服务端的NIO线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws IOException {
ch.pipeline().addLast(
new NettyMessageDecoder(1024 * 1024, 4, 4));
ch.pipeline().addLast(new NettyMessageEncoder());
ch.pipeline().addLast("readTimeoutHandler",
new ReadTimeoutHandler(50));
ch.pipeline().addLast(new LoginAuthRespHandler());
ch.pipeline().addLast("HeartBeatHandler",
new HeartBeatRespHandler());
}
});
// 绑定端口,同步等待成功
b.bind(NettyConstant.REMOTEIP, NettyConstant.PORT).sync();
LOG.info("Netty server start ok : "
+ (NettyConstant.REMOTEIP + " : " + NettyConstant.PORT));
}
public static void main(String[] args) throws Exception {
new NettyServer().bind();
}
}
14.引导客户端
package com.netty.obj.demo3.client;
import com.netty.obj.demo3.code.NettyMessageDecoder;
import com.netty.obj.demo3.code.NettyMessageEncoder;
import com.netty.obj.demo3.handler.HeartBeatReqHandler;
import com.netty.obj.demo3.handler.LoginAuthReqHandler;
import com.netty.obj.demo3.util.NettyConstant;
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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @PackageName:com.netty.obj.demo3.client Description
* @author:
* @date:2021/12/23
*/
public class NettyClient {
private static final Log LOG = LogFactory.getLog(NettyClient.class);
private ScheduledExecutorService executor = Executors
.newScheduledThreadPool(1);
EventLoopGroup group = new NioEventLoopGroup();
public void connect(int port, String host) throws Exception {
// 配置客户端NIO线程组
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(
new NettyMessageDecoder(1024 * 1024, 4, 4));
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();
// 当对应的channel关闭的时候,就会返回对应的channel。
// Returns the ChannelFuture which will be notified when this channel is closed. This method always returns the same future instance.
future.channel().closeFuture().sync();
} finally {
// 所有资源释放完成之后,清空资源,再次发起重连操作
executor.execute(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
try {
connect(NettyConstant.PORT, NettyConstant.REMOTEIP);// 发起重连操作
} catch (Exception e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
new NettyClient().connect(NettyConstant.PORT, NettyConstant.REMOTEIP);
}
}
15.运行服务端,客户端
最后总结一下上面代码的功能,后续根据这几点文字补充
1.数据结构的定义
2.消息编解码
3.握手和安全认证
4.心跳检测机制
5.断开重连机制