175 lines
7.4 KiB
Java
Executable File
175 lines
7.4 KiB
Java
Executable File
/**
|
||
* Copyright 2018-2028 Akaxin Group
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
package com.windchat.im.connector.netty.handler;
|
||
|
||
import java.net.InetSocketAddress;
|
||
import java.util.Map;
|
||
|
||
import com.windchat.im.connector.codec.parser.ChannelConst;
|
||
import org.apache.commons.lang3.StringUtils;
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
|
||
import com.windchat.common.channel.ChannelManager;
|
||
import com.windchat.common.channel.ChannelSession;
|
||
import com.windchat.common.command.Command;
|
||
import com.windchat.common.command.CommandResponse;
|
||
import com.windchat.common.command.RedisCommand;
|
||
import com.windchat.common.constant.RequestAction;
|
||
import com.windchat.common.executor.AbstracteExecutor;
|
||
import com.windchat.common.logs.LogUtils;
|
||
import com.windchat.common.utils.StringHelper;
|
||
import com.akaxin.proto.core.CoreProto;
|
||
import com.windchat.im.connector.constant.AkxProject;
|
||
import com.windchat.im.connector.session.SessionManager;
|
||
|
||
import io.netty.channel.ChannelHandlerContext;
|
||
import io.netty.channel.SimpleChannelInboundHandler;
|
||
|
||
/**
|
||
* Netty处理TCP链接中接受客户端传入的消息
|
||
*
|
||
* @author Sam{@link an.guoyue254@gmail.com}
|
||
* @since 2017.11.07 16:56:36
|
||
*/
|
||
public class NettyServerHandler extends SimpleChannelInboundHandler<RedisCommand> {
|
||
private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
|
||
private AbstracteExecutor<Command, CommandResponse> executor;
|
||
|
||
public NettyServerHandler(AbstracteExecutor<Command, CommandResponse> executor) {
|
||
this.executor = executor;
|
||
}
|
||
|
||
@Override
|
||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||
InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
|
||
String clientIp = socketAddress.getAddress().getHostAddress();
|
||
ctx.channel().attr(ChannelConst.CHANNELSESSION).set(new ChannelSession(ctx.channel()));
|
||
logger.debug("{} client={} connect to Netty Server...", AkxProject.PLN, clientIp);
|
||
}
|
||
|
||
@Override
|
||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||
InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
|
||
String clientIp = socketAddress.getAddress().getHostAddress();
|
||
logger.debug("{} client={} close connection... ChannelSize={}", AkxProject.PLN, clientIp,
|
||
ChannelManager.getChannelSessionSize());
|
||
|
||
ChannelSession channelSession = ctx.channel().attr(ChannelConst.CHANNELSESSION).get();
|
||
if (channelSession.getCtype() == 1 && StringUtils.isNotEmpty(channelSession.getUserId())) {
|
||
ChannelManager.delChannelSession(channelSession.getDeviceId());
|
||
String siteUserId = channelSession.getUserId();
|
||
String deviceId = channelSession.getDeviceId();
|
||
boolean offRes = SessionManager.getInstance().setUserOffline(siteUserId, deviceId);
|
||
|
||
logger.debug("{} set client={} siteUserId={} deviceId={} offline-status:{} ChannelSize={}", AkxProject.PLN,
|
||
clientIp, siteUserId, deviceId, offRes, ChannelManager.getChannelSessionSize());
|
||
}
|
||
}
|
||
|
||
@Override
|
||
protected void channelRead0(ChannelHandlerContext ctx, RedisCommand redisCmd) throws Exception {
|
||
InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
|
||
String clientIp = socketAddress.getAddress().getHostAddress();
|
||
ChannelSession channelSession = ctx.channel().attr(ChannelConst.CHANNELSESSION).get();
|
||
|
||
// Channel不可用情况下,关闭连接事件
|
||
// disconnect tcp connection as channel is unavailable
|
||
if (channelSession.getChannel() == null || !channelSession.getChannel().isActive()) {
|
||
ctx.disconnect();// 断开连接事件(与对方的连接断开)
|
||
logger.warn("{} close client={} as its channel is not active ", AkxProject.PLN, clientIp);
|
||
}
|
||
|
||
String version = redisCmd.getParameterByIndex(0);// 网络协议版本1.0
|
||
String action = redisCmd.getParameterByIndex(1);
|
||
byte[] params = redisCmd.getBytesParamByIndex(2);
|
||
CoreProto.TransportPackageData packageData = CoreProto.TransportPackageData.parseFrom(params);
|
||
|
||
Map<Integer, String> header = packageData.getHeaderMap();
|
||
|
||
Command command = new Command();
|
||
command.setClientIp(clientIp);
|
||
command.setSiteUserId(channelSession.getUserId());
|
||
command.setDeviceId(channelSession.getDeviceId());
|
||
command.setAction(action);
|
||
command.setHeader(header);
|
||
if (header != null) {
|
||
command.setClientVersion(header.get(CoreProto.HeaderKey.CLIENT_SOCKET_VERSION_VALUE));
|
||
}
|
||
command.setParams(packageData.getData().toByteArray());
|
||
command.setChannelSession(channelSession);
|
||
command.setStartTime(System.currentTimeMillis());
|
||
|
||
if (!RequestAction.IM_CTS_PING.getName().equalsIgnoreCase(command.getAction())) {
|
||
logger.debug("{} client={} -> site version={} action={} params-length={}", AkxProject.PLN, clientIp,
|
||
version, action, params.length);
|
||
}
|
||
|
||
if (RequestAction.IM.getName().equals(command.getRety())) {
|
||
// 如果是syncFinish,则这里需要修改channel中的syncFinTime
|
||
channelSession.setActionForPsn(action);
|
||
// 单独处理im.site.hello && im.site.auth
|
||
if (RequestAction.SITE.getName().equalsIgnoreCase(command.getService())) {
|
||
String anoRequest = command.getRety() + "." + command.getService();
|
||
command.setRety(anoRequest);
|
||
}
|
||
CommandResponse response = this.executor.execute(command.getRety(), command);
|
||
|
||
if (!RequestAction.IM_CTS_PING.getName().equals(command.getAction())) {
|
||
// 输出IM请求结果
|
||
LogUtils.requestResultLog(logger, command, response);
|
||
}
|
||
} else if (RequestAction.API.getName().equalsIgnoreCase(command.getRety())) {
|
||
CommandResponse response = this.executor.execute(command.getRety(), command);
|
||
// 输出API请求结果
|
||
LogUtils.requestResultLog(logger, command, response);
|
||
} else {
|
||
/**
|
||
* <pre>
|
||
*
|
||
* pipeline.addLast("A", new AHandler());
|
||
* pipeline.addLast("B", new BHandler());
|
||
* pipeline.addLast("C", new CHandler());
|
||
*
|
||
* ctx.close() && channel().close();
|
||
* ctx.close(): close the channel in current handler,will start from the tail of the ChannelPipeline
|
||
* do A.close() ,B.close(),C.close();
|
||
* channel().close():close channel in all handler from pointer
|
||
* do C.close() , B.close(), A.close();
|
||
* </pre>
|
||
*/
|
||
ctx.channel().close();// 关闭channel事件(关闭自己的连接)
|
||
logger.error("{} client={} siteUserId={} action={} unknow request method", AkxProject.PLN,
|
||
command.getClientIp(), command.getSiteUserId(), command.getAction());
|
||
return;
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||
InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
|
||
String clientIp = socketAddress.getAddress().getHostAddress();
|
||
ctx.close();
|
||
logger.error(StringHelper.format("{} client{} channel exeception happen.", AkxProject.PLN, clientIp), cause);
|
||
}
|
||
|
||
@Override
|
||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||
// 设置idle,触发此方法
|
||
// logger.info("user event triggered evt={}", evt.toString());
|
||
}
|
||
|
||
} |