package com.windchat.im.connector.ws.handler; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.windchat.common.channel.ChannelSession; import com.windchat.common.channel.WebChannelManager; import com.windchat.common.command.Command; import com.windchat.common.command.CommandResponse; import com.windchat.common.constant.CharsetCoding; import com.windchat.common.executor.AbstracteExecutor; import com.windchat.im.business.cache.WebSessionCache; import com.windchat.im.connector.codec.parser.ChannelConst; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; /** * 接受客户端发送的消息,客户端发送的消息组装成TextWebSocketFrame格式 * * @author Sam{@link an.guoyue254@gmail.com} * @since 2018-05-02 15:01:08 */ public class WsServerHandler extends SimpleChannelInboundHandler { private static Logger logger = LoggerFactory.getLogger(WsServerHandler.class); private static final String AKAXIN_WS_PATH = "/akaxin/ws"; // ws 握手 private WebSocketServerHandshaker wsHandshaker; private AbstracteExecutor executor; public WsServerHandler(AbstracteExecutor executor) { this.executor = executor; } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("ChannelId" + ctx.channel().id().asLongText()); } // 客户端连接上服务端 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channel active clientIp" + ctx.channel().remoteAddress().toString()); ctx.channel().attr(ChannelConst.CHANNELSESSION).set(new ChannelSession(ctx.channel())); } // 从客户端读取消息 @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { // http 请求握手 doHttpRequest(ctx, (FullHttpRequest) msg); } else if (msg instanceof WebSocketFrame) { // websocket 请求 doWSRequest(ctx, (WebSocketFrame) msg); } else { // 错误请求,关闭连接 ctx.close(); } } // 处理http请求,ws握手使用http请求 private void doHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) { if (request.decoderResult().isFailure()) { ctx.close(); return; } // 握手使用get方法,所以我们控制只接受get方法 if (HttpMethod.GET != request.method()) { ctx.close(); return; } String wsUrl = "ws://" + request.headers().get(HttpHeaderNames.HOST) + AKAXIN_WS_PATH; WebSocketServerHandshakerFactory webSocketFactory = new WebSocketServerHandshakerFactory(wsUrl, null, true); wsHandshaker = webSocketFactory.newHandshaker(request); if (wsHandshaker != null) { // ChannelFuture channelFuture = wsHandshaker.handshake(ctx.channel(), request); if (channelFuture.isSuccess()) { // 握手并且验证用户webSessionId QueryStringDecoder queryDecoder = new QueryStringDecoder(request.uri()); List sessionIds = queryDecoder.parameters().get("sessionId"); if (sessionIds != null && sessionIds.size() > 0) { String sessionId = sessionIds.get(0); String siteUserId = WebSessionCache.getSiteUserId(sessionId); // test siteUserId siteUserId = "77151873-0fc7-4cf1-8bd6-67d00190fcf6"; if (StringUtils.isNotBlank(siteUserId)) { ChannelSession channelSession = ctx.channel().attr(ChannelConst.CHANNELSESSION).get(); // siteUserId && sessionId 放入Channel缓存中 channelSession.setUserId(siteUserId); WebChannelManager.addChannelSession(siteUserId, channelSession); } else { // cant get authed message ,so close the channel // ctx.close(); } } else { ctx.close(); } System.out.println("client handshaker success parm=" + queryDecoder.parameters()); } } else { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } } // 处理ws请求 private void doWSRequest(ChannelHandlerContext ctx, WebSocketFrame wsFrame) { InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress(); String clientIp = socketAddress.getAddress().getHostAddress(); ChannelSession channelSession = ctx.channel().attr(ChannelConst.CHANNELSESSION).get(); Command command = new Command(); command.setSiteUserId(channelSession.getUserId()); command.setClientIp(clientIp); command.setStartTime(System.currentTimeMillis()); if (wsFrame instanceof TextWebSocketFrame) { TextWebSocketFrame textWsFrame = (TextWebSocketFrame) wsFrame; String webText = textWsFrame.text(); try { command.setParams(webText.getBytes(CharsetCoding.UTF_8)); } catch (UnsupportedEncodingException e) { logger.error("web message text=" + webText + " Charset code error"); } TextWebSocketFrame resFrame = new TextWebSocketFrame(textWsFrame.text()); ctx.channel().writeAndFlush(resFrame); executor.execute("WS-ACTION", command); } else if (wsFrame instanceof PingWebSocketFrame) { // ping/pong ctx.channel().writeAndFlush(new PongWebSocketFrame(wsFrame.content().retain())); logger.info("ws client siteUserId={} ping to server", command.getSiteUserId()); } else if (wsFrame instanceof CloseWebSocketFrame) { // close channel wsHandshaker.close(ctx.channel(), (CloseWebSocketFrame) wsFrame.retain()); WebChannelManager.delChannelSession(command.getSiteUserId()); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { ChannelSession channelSession = ctx.channel().attr(ChannelConst.CHANNELSESSION).get(); WebChannelManager.delChannelSession(channelSession.getUserId()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // ctx.close(); logger.error("ws channel exception happen", cause); } }