握手的发起是在客户端和服务端TCP链路建立成功通道激活时,握手消息的接入和安全认证在服务端处理。
握手认证的客户端ChannelHandler,用于在通道激活时发起握手请求
package aggrement;
import java.awt.TrayIcon.MessageType;
importio.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/*
* 握手认证客户端,用于在通道激活时发起握手请求
*/
public class LoginAuthReqHandler extendsChannelHandlerAdapter{
/*
* 当客户端跟服务端TCP三次握手成功之后,由客户端构造握手请求消息发送给服务端
* 由于采用IP白名单认证机制,因此,不需要携带消息体,消息体为空,消息类型为3
* 握手请求消息,握手请求发送之后,按照协议规范,服务端需要返回握手应答消息
* (non-Javadoc)
* @seeio.netty.channel.ChannelHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
*/
publicvoid channelActive(ChannelHandlerContext ctx) throws Exception{
ctx.writeAndFlush(buildLoginReq());
}
/*
* 对握手应答消息进行处理,首先判断消息是否是握手应答消息
* 如果不是,直接透传给后面的ChannelHandler进行处理;首先判断是握手应答消息
* 对应答结果进行判断
* (non-Javadoc)
* @seeio.netty.channel.ChannelHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext,java.lang.Object)
*/
publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
NettyMessagemessage=(NettyMessage) msg;
//如果是握手应答消息,需要判断是否认证成功
if(message.getHeader()!=null)&&
message.getHeader().getType()==MessageType.LOGIN_RESP.value()){
byteloginResult=(byte) message.getBody();
if(loginResult!=(byte)0) {
//握手失败,关闭连接
ctx.close();
}else {
System.out.println("Loginis ok:"+message);
ctx.fireChannelRead(msg);
}
} else
ctx.fireChannelRead(msg);
}
privateNettyMessage buildLoginReq(){
NettyMessagemessage=new NettyMessage();
Headerheader=new Header();
header.setType(MessageType.LOGIN_REQ.value());
message.setHeader(header);
returnmessage;
}
publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throwsException{
ctx.fireExceptionCaught(cause);
}
}
package aggrement;
importio.netty.channel.ChannelHandlerAdapter;
importio.netty.channel.ChannelHandlerContext;
import java.awt.TrayIcon.MessageType;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/*
* 服务端的握手接入和安全认证
*/
public class LoginAuthRespHandler extendsChannelHandlerAdapter{
privateMap
//分别定义了重复登录保护和IP认证的白名单列表,主要用于提升握手的可靠性
privateString[] whitekList={"127.0.0.1","192.168.1.104"};
@Override
publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
NettyMessagemessage=(NettyMessage) msg;
/*
* 用于接入认证,首先根据客户端的原地址(/127.0.0.1:12088)进行重复登录判断
* 如果客户端已经登录成功,拒绝重复登录,以防止由于客户端重复登录导致的句柄泄漏
*
*/
//如果是握手请求消息,处理,其他消息透传
if(message.getHeader()!=null&&message.getHeader().getType()==MessageType.LOGIN_REQ.value)){
StringnodeIndex=ctx.channel().remoteAddress().toString();
NettyMessageloginResp=null;
//重复登录,拒绝
if(nodeCheck.containsKey(nodeIndex)){
loginResp=buildResponse((byte)-1);
} else{
//通过ChannelHandlerContext的Channel接口获取客户端的
//InetSocketAddress地址,从中取得发送方的原地址信息,通过原地址进行白名单校验
//校验通过握手成功,否则握手失败,
InetSocketAddressaddress=(InetSocketAddress) ctx.channel().remoteAddress();
Stringip=address.getAddress().getHostAddress();
booleanisOK=false;
for(StringWIP:whiteList){
if(WIP.equals(ip)){
isIK=true;
break;
}
}
//通过buildResponse构造握手应答消息返回客户端
loginResp=isOK?buildResponse((byte)0):buildResponse((byte) -1);
if(isOK)
nodeCheck.put(nodeIndex,true);
}
System.out.println("Thelogin responseis:"+loginResp+"body["+loginResp.getBody()+"]");
ctx.writeAndFlush(loginResp);
}else {
ctx.fireChannelRead(msg);
}
}
privateNettyMessage buildResponse(byte result){
NettyMessagemessge=new NettyMessage();
Headerheader=new Header();
header.setType(MessageType.LOGIN_RESP.value());
message.setHeader(header);
message.setBody(result);
returnmessage;
}
publicvoid exceptionCautht(ChannelHandlerContext ctx,Throwable cause) throwsException{
nodeCheck.remove(ctx.channel().remoteAddress().toString());
ctx.close();
ctx.fireExceptionCautht(cause);
}
}
当发生异常关闭链路的时候,需要将客户端的消息从登录注册表中去注册,以保证后续客户端可以重连成功。