这篇是关于Netty Websocket服务器的使用,不废话直接上代码.
环境同样是spring boot , maven依赖 网上找.
首先是webSocketServer :
public class WebSocketServer {
public void startService() throws Exception {
//创建两个组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//加载一些配置信息----端口号 信道了什么的
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.TCP_NODELAY, true).handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new WebSocketServerInitializer());
Channel ch = b.bind(8080).sync().channel();
// 阻塞处理,等待服务端链路关闭之后main函数才退出
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
我把static的main 方法该成这样,启动的话可以到spring boot的启动类注入这个类进行启动:
@SpringBootApplication
@ComponentScan("com.zy") //包扫描
public class Application {
public static void main(String[] args) {
ApplicationContext app = SpringApplication.run(Application.class, args);
WebSocketServer webSocketServer = app.getBean(WebSocketServer.class);
try {
webSocketServer.startService();
} catch (Exception e) {
e.printStackTrace();
}
}
以上部分是服务器完整部分,下面这个处理器需要好好研究下,因为我把业务代码删除了.对于刚布置的人来说可能缺少点东西需要自己去添加(我看了下应该不会报错!!).
接下来是websocket的一个处理器:
package com.zy.service.netty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zy.service.netty.global.Global;
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.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
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;
import io.netty.util.CharsetUtil;
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger logger = LoggerFactory.getLogger(WebSocketFrameHandler.class.getName());
private WebSocketServerHandshaker handshaker;
/**
* websocket请求入口
*
* @param ctx
* @param msg
* @throws Exception
*/
public void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
// ping请求
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
}
// 只支持文本格式,不支持二进制消息
if (!(frame instanceof TextWebSocketFrame)) {
throw new Exception("仅支持文本格式");
}
// 客服端发送过来的消息
String request = ((TextWebSocketFrame) frame).text();
System.err.println("服务端收到:" + request);
}
/**
* http
*
* @param ctx
* @param msg
*/
public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
if (!req.decoderResult().isSuccess()) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
"ws:/" + ctx.channel() + "/websocket", null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
// 不支持
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), req);
}
}
private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
// 返回应答给客户端
if (res.status().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
}
// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
private boolean isKeepAlive(FullHttpRequest req) {
return false;
}
/**
* websocket的入口方法:客户端第一次请求websocket第一次请求发送的信息是OBject类的进入websocket服务器会默认第一个调用这个方法将请求分为http和websocket我们只需要在websocket的请求中进行业务处理就可以了http不用管,因为我们做的是websocket服务,可以发现代码中将Object类信息强转成了websocket类信息.*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
// ws://xxxx
handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
}
}
/**
* 客户端断开连接
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String key = ctx.channel().attr(Global.ATTRIBUTE_KEY).get();
if (key != null) {
Global.removeGroup(key, ctx.channel());
}
}
/**
* 心跳检测
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
/**
* 服务端断开连接
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 出异常的时候关闭channel
ctx.close();
}
}
服务器的初始化:
package com.zy.service.netty;
import io.netty.channel.ChannelInitializer;
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;
public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// ChannelPipeline pipeline = ch.pipeline();
ch.pipeline().addLast("http-codec", new HttpServerCodec());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
ch.pipeline().addLast("handler", new WebSocketFrameHandler());
}
}
前端页面:
<html>
<head>
<meta charset="UTF-8">
<title>netty</title>
</head>
<body>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080");
socket.onmessage = function(event) {
var d = document.getElementById('responseText');
d.value = d.value + "\n" + event.data;
};
socket.onopen = function(event) {
console.log("websocket 打开了");
};
socket.onclose = function(event) {
console.log("websocket 关闭了");
};
}
function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("The socket is not open.");
}
}
</script>
<h3>Output</h3>
<textarea id="responseText" style="width: 500px; height: 300px;" autocomplete="false"></textarea>
</body>
</html>
如果项目跑不起来应该是我删的东西删多了,我把对应的业务代码都删除了只剩下基本的一个websocket服务器,如果出什么错误可以根据我加的注释自己把缺失的部分补齐...