对于只想了解基于netty的WebSocket协议可以看这篇 基于Netty最简单的WebSocket通讯 ,现在我们是在项目添加,会对其中的一些内容进行修改,
if (msg instanceof FullHttpRequest) {
// 传统的HTTP接入
handleHttpMessage(ctx, msg);
} else if (msg instanceof WebSocketFrame) {
// WebSocket接入
handleWebSocketMessage(ctx, msg);
} else if (msg instanceof IMessage) {
// 这里已经通过WebSocketFrameToIMessageDecoder进行解码,获得我们设置好的IMessage类了
consumer.consume((IMessage) msg, ctx.channel());
}
/*
* Copyright (C), 2015-2018
* FileName: WebSocketChannelInitializer
* Author: zhao
* Date: 2018/8/10 11:42
* Description: webSocket的channel
* History:
*
package com.lizhaoblog.server.channel.websocket;
import com.lizhaoblog.base.message.codec.IMessageToWebSocketFrameEncoder;
import com.lizhaoblog.base.message.codec.WebSocketFrameToIMessageDecoder;
import com.lizhaoblog.server.pojo.ServerConfig;
import org.springframework.stereotype.Component;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* 〈一句话功能简述〉
* 〈webSocket的channel〉
*
* @author zhao
* @date 2018/8/10 11:42
* @since 1.0.1
*/
@Component
public class WebSocketChannelInitializer extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("http-codec", new HttpServerCodec()); // Http消息编码解码
pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); // Http消息组装
pipeline.addLast("http-chunked", new ChunkedWriteHandler()); // WebSocket通信支持
// 消息编解码
pipeline.addLast("encoder", new IMessageToWebSocketFrameEncoder());
pipeline.addLast("decoder", new WebSocketFrameToIMessageDecoder());
WebSocketHandler webSocketHandler = (WebSocketHandler) ServerConfig.getInstance().getApplicationContext()
.getBean("webSocketHandler");
pipeline.addLast(webSocketHandler);
}
}
/*
* Copyright (C), 2015-2018
* FileName: WebSocketHandler
* Author: zhao
* Date: 2018/8/10 11:44
* Description: websocket的消息处理
* History:
*
package com.lizhaoblog.server.channel.websocket;
import com.lizhaoblog.base.constant.ConstantValue;
import com.lizhaoblog.base.exception.MessageCodecException;
import com.lizhaoblog.base.message.IMessage;
import com.lizhaoblog.base.message.codec.MessageDecoder;
import com.lizhaoblog.base.message.impl.ByteMessage;
import com.lizhaoblog.base.message.impl.MessageFactory;
import com.lizhaoblog.base.network.customer.INetworkConsumer;
import com.lizhaoblog.base.network.listener.INetworkEventListener;
import com.lizhaoblog.base.session.SessionManager;
import com.lizhaoblog.base.util.HttpResponseUtil;
import com.lizhaoblog.server.pojo.ServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
/**
* 〈一句话功能简述〉
* 〈websocket的消息处理〉
*
* @author zhao
* @date 2018/8/10 11:44
* @since 1.0.1
*/
@Component
@Scope("prototype")
public class WebSocketHandler extends SimpleChannelInboundHandler
/*
* Copyright (C), 2015-2018
* FileName: WebSocketFrameEncoder
* Author: zhao
* Date: 2018/8/13 17:12
* Description: WebSocketFrameEncoder编码器
* History:
*
package com.lizhaoblog.base.message.codec;
import com.lizhaoblog.base.message.IMessage;
import java.util.List;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
/**
* 〈一句话功能简述〉
* 〈WebSocketFrameEncoder编码器〉
*
* @author zhao
* @date 2018/8/13 17:12
* @since 1.0.1
*/
public class IMessageToWebSocketFrameEncoder extends MessageToMessageEncoder {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, IMessage iMessage, List
/*
* Copyright (C), 2015-2018
* FileName: WebSocketFrameToIMessageDecoder
* Author: zhao
* Date: 2018/8/13 19:25
* Description: WebSocketFrame转换成IMessage
* History:
*
package com.lizhaoblog.base.message.codec;
import com.lizhaoblog.base.constant.ConstantValue;
import com.lizhaoblog.base.message.IMessage;
import com.lizhaoblog.server.pojo.ServerConfig;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
/**
* 〈一句话功能简述〉
* 〈WebSocketFrame转换成IMessage〉
*
* @author zhao
* @date 2018/8/13 19:25
* @since 1.0.1
*/
public class WebSocketFrameToIMessageDecoder extends MessageToMessageDecoder
/*
* Copyright (C), 2015-2018
* FileName: HttpResponseUtil
* Author: zhao
* Date: 2018/8/11 12:16
* Description: 处理http的工具类
* History:
*
package com.lizhaoblog.base.util;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.util.CharsetUtil;
/**
* 〈一句话功能简述〉
* 〈处理http的工具类〉
*
* @author zhao
* @date 2018/8/11 12:16
* @since 1.0.1
*/
public final class HttpResponseUtil {
/**
* Http返回
*
* @param ctx
* @param request
* @param response
*/
public static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
// 返回应答给客户端
if (response.status().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
response.content().writeBytes(buf);
buf.release();
HttpUtil.setContentLength(response, response.content().readableBytes());
}
// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(response);
if (!HttpUtil.isKeepAlive(request) || response.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
private HttpResponseUtil() {
}
}
/*
* Copyright (C), 2015-2018
* FileName: WebSocketClientHandlerTest
* Author: zhao
* Date: 2018/8/2 17:32
* Description:
* History:
*
package com.lizhaoblog.server.channel.websocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.CharsetUtil;
/**
* 〈一句话功能简述〉
* 〈〉
*
* @author zhao
* @date 2018/8/2 17:32
* @since 1.0.1
*/
public class WebSocketClientHandlerTest extends SimpleChannelInboundHandler {
private static final Logger logger = LoggerFactory.getLogger(WebSocketClientHandlerTest.class);
private final WebSocketClientHandshaker handshaker;
private ChannelPromise handshakeFuture;
public WebSocketClientHandlerTest(WebSocketClientHandshaker handshaker) {
this.handshaker = handshaker;
}
public ChannelFuture handshakeFuture() {
return handshakeFuture;
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
handshakeFuture = ctx.newPromise();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable arg1) {
logger.info("异常发生", arg1);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
// logger.info("数据内容:data=" + data);
Channel ch = ctx.channel();
if (!handshaker.isHandshakeComplete()) {
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
System.out.println("WebSocket Client connected!");
handshakeFuture.setSuccess();
return;
}
if (msg instanceof FullHttpResponse) {
FullHttpResponse response = (FullHttpResponse) msg;
throw new IllegalStateException(
"Unexpected FullHttpResponse (getStatus=" + response.getStatus() +
", content=" + response.content().toString(CharsetUtil.UTF_8) + ')');
}
WebSocketFrame frame = (WebSocketFrame) msg;
if (frame instanceof TextWebSocketFrame) {
TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
System.out.println("WebSocket Client received message: " + textFrame.text());
} else if (frame instanceof PongWebSocketFrame) {
System.out.println("WebSocket Client received pong");
} else if (frame instanceof CloseWebSocketFrame) {
System.out.println("WebSocket Client received closing");
ch.close();
}
//关闭链路
// ctx.close();
// channelInactive(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("客户端连接建立");
// 在通道连接成功后发送握手连接
handshaker.handshake(ctx.channel());
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("客户端连接断开");
super.channelInactive(ctx);
}
}
/*
* Copyright (C), 2015-2018
* FileName: WebSocketClientTest
* Author: zhao
* Date: 2018/8/2 17:32
* Description: websocket测试
* History:
*
package com.lizhaoblog.server.channel.websocket;
import com.lizhaoblog.base.message.codec.IMessageToWebSocketFrameEncoder;
import com.lizhaoblog.base.message.codec.WebSocketFrameToIMessageDecoder;
import com.lizhaoblog.base.message.impl.ByteMessage;
import com.lizhaoblog.common.CommonValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
/**
* 〈一句话功能简述〉
* 〈websocket测试〉
*
* @author zhao
* @date 2018/8/2 17:32
* @since 1.0.1
*/
public class WebSocketClientTest {
private static final Logger logger = LoggerFactory.getLogger(WebSocketClientTest.class);
private static EventLoopGroup group = new NioEventLoopGroup();
private static WebSocketClientHandlerTest handler;
private String uriStr = "ws//" + CommonValue.IP + ":" + CommonValue.PORT;
public void run() throws InterruptedException, URISyntaxException {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
// 主要是为handler(自己写的类)服务
URI wsUri = new URI(uriStr);
WebSocketClientHandshaker webSocketClientHandshaker = WebSocketClientHandshakerFactory
.newHandshaker(wsUri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders(), 100 * 1024 * 1024);
handler = new WebSocketClientHandlerTest(webSocketClientHandshaker);
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast("encoder", new IMessageToWebSocketFrameEncoder());
pipeline.addLast("decoder", new WebSocketFrameToIMessageDecoder());
// pipeline.addLast("encoder", new MessageEncoder());
// pipeline.addLast("decoder", new MessageDecoder(ConstantValue.MESSAGE_CODEC_MAX_FRAME_LENGTH,
// ConstantValue.MESSAGE_CODEC_LENGTH_FIELD_LENGTH, ConstantValue.MESSAGE_CODEC_LENGTH_FIELD_OFFSET,
// ConstantValue.MESSAGE_CODEC_LENGTH_ADJUSTMENT, ConstantValue.MESSAGE_CODEC_INITIAL_BYTES_TO_STRIP,
// false, ServerConfig.getInstance().getMessageType()));
pipeline.addLast(handler);
}
});
ByteMessage byteMessage = new ByteMessage();
byteMessage.setMessageId(com.lizhaoblog.server.biz.constant.CommonValue.CM_MSG_TEST_BYTE);
byteMessage.setStatusCode(com.lizhaoblog.server.biz.constant.CommonValue.MSG_STATUS_CODE_SUCCESS);
byteMessage.addAttr(2);
// 连接服务端
// ChannelFuture channelFuture = bootstrap.connect(CommonValue.IP, CommonValue.PORT).sync();
ChannelFuture channelFuture = bootstrap.connect(CommonValue.IP, CommonValue.PORT).sync();
handler.handshakeFuture().sync();
// channelFuture.channel().writeAndFlush(byteMessage);
// WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[] { 8, 1, 8, 1 }));
// WebSocketFrame frame = new TextWebSocketFrame("aaa");
// channelFuture.channel().writeAndFlush(frame);
// byte[] bytes = new byte[14];
// bytes[0] = 0;
// bytes[1] = 1;
// bytes[2] = 0;
// bytes[3] = 1;
// bytes[4] = 0;
// bytes[5] = 0;
// bytes[6] = 0;
// bytes[7] = 6;
// bytes[8] = 2;
// bytes[9] = 0;
// bytes[10] = 0;
// bytes[11] = 0;
// bytes[12] = 0;
// bytes[13] = 1;
//// channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer(bytes));
// BinaryWebSocketFrame frame = new BinaryWebSocketFrame(Unpooled.copiedBuffer(bytes));
// channelFuture.channel().writeAndFlush(frame);
channelFuture.channel().writeAndFlush(byteMessage);
logger.info("向Socket服务器发送数据:" + byteMessage);
channelFuture.channel().closeFuture().sync();
}
}
上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
以加qq群一起探讨Java游戏服务器开发的相关知识 676231564