Netty实现WebSocket
package com.qmtt.server; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; @Service
public class NettyServer {
private static final Logger log = LoggerFactory.getLogger(NettyServer.class);
EventLoopGroup bossGroup;
EventLoopGroup workGroup;
Channel channel; // public static void main(String[] args) {
// new NettyServer().run();
// } @PostConstruct
public void run() {
log.info("启动netty");
bossGroup = new NioEventLoopGroup();
workGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new ChildChannelHandler());
channel = b.bind(7397).sync().channel();
// channel.closeFuture().sync();
} catch (Exception e) {
log.error("", e);
} finally {
// bossGroup.shutdownGracefully();
// workGroup.shutdownGracefully();
}
} @PreDestroy
public void stop() {
log.info("关闭netty");
if (null == channel) {
log.error("server channel is null");
}
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
channel.closeFuture().syncUninterruptibly();
bossGroup = null;
workGroup = null;
channel = null;
}
}
package com.qmtt.server; import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate; import com.qmtt.tools.JsonUtils;
import com.qmtt.tools.SpringUtil;
import com.qmtt.websocket.GameFunction2; 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.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;
import io.netty.util.CharsetUtil; public class MyWebSocket2 extends SimpleChannelInboundHandler<Object> {
private static final Logger log = LoggerFactory.getLogger(MyWebSocket2.class);
private WebSocketServerHandshaker handshaker; private static Map<String, ChannelHandlerContext> webSocketMap = new Hashtable<String, ChannelHandlerContext>(); public GameFunction2 gameFunction; RedisTemplate redisTemplate; @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("客户端与服务端连接开启");
gameFunction = SpringUtil.getBean(GameFunction2.class);
redisTemplate = (RedisTemplate) SpringUtil.getBean("redisTemplate");
// 添加
// Global.group.add(ctx.channel());
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 移除
// Global.group.remove(ctx.channel());
log.info("客户端与服务端连接关闭");
String key = null;
Iterator iterator = webSocketMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ChannelHandlerContext> entry = (Entry<String, ChannelHandlerContext>) iterator.next();
key = entry.getKey();
if (entry.getValue().equals(ctx)) {
key = entry.getKey();
break;
}
}
log.info("<{}>断开连接", key);
if (key != null) {
webSocketMap.remove(key);
}
gameFunction.close(key);
} @Override
protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, ((FullHttpRequest) msg));
} else if (msg instanceof WebSocketFrame) {
handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
}
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
// 判断是否关闭链路的指令
if (frame instanceof CloseWebSocketFrame) {
log.info("连接开闭");
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
return;
}
// 判断是否ping消息
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
}
// 本例程仅支持文本消息,不支持二进制消息
if (!(frame instanceof TextWebSocketFrame)) {
log.info("不支持二进制消息");
return;
}
// 返回应答消息
String message = ((TextWebSocketFrame) frame).text();
if (!message.contains("msgType")) {
return;
}
log.info("服务端收到消息:" + message);
try {
Map map = JsonUtils.json2map(message);
String msgTpye = map.get("msgType").toString();
String openid = map.get("openid").toString();
// 开始游戏
if (msgTpye.equals("start")) {
webSocketMap.put(openid, ctx);
// String rankValue = map.get("rankValue").toString();
gameFunction.joinGame(openid);
return;
}
// 回答问题
if (msgTpye.equals("answer")) {
gameFunction.answer(map);
return;
}
// 游戏结束
if (msgTpye.equals("gameover")) {
gameFunction.gameover(map);
return;
}
// 发出邀请等待对手
if (msgTpye.equals("wait")) {
webSocketMap.put(openid, ctx);
gameFunction.waitEnter(openid);
return;
}
// 发出邀请对手进入
if (msgTpye.equals("waitEnter")) {
webSocketMap.put(openid, ctx);
String inviteOpenid = (String) map.get("inviteOpenid");
// 要判断用户是否已经开始在玩游戏 了,是否已经离开
gameFunction.checkUserStatus(openid, inviteOpenid);
return;
}
// 发出邀请对手进入
if (msgTpye.equals("waitStart")) {
gameFunction.waitStart(openid);
return;
}
// 再来一局
if (msgTpye.equals("playAgain")) {
gameFunction.playAgain(openid);
return;
} } catch (Exception e) {
log.error("", e);
}
// TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()
// + ctx.channel().id() + ":" + request);
// // 群发
// Global.group.writeAndFlush(tws);
// 返回【谁发的发给谁】
// ctx.channel().writeAndFlush(tws);
} private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
if (!req.getDecoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
// 注意,这条地址别被误导了,其实这里填写什么都无所谓,WS协议消息的接收不受这里控制
// 消息分发可以通过Req中获取uri处理
// WebSocketServerHandshakerFactory wsFactory = new
// WebSocketServerHandshakerFactory("ws://127.0.0.1:7397/websocket",
// null,
// false);
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("", null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), req);
}
} private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
// 返回应答给客户端
if (res.getStatus().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
}
// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!isKeepAlive(req) || res.getStatus().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
} private static boolean isKeepAlive(FullHttpRequest req) {
return false;
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} public static Map<String, ChannelHandlerContext> getWebSocketMap() {
return webSocketMap;
} public static int sendMsg(String id, Object msg) {
try {
String ret = JsonUtils.toJsonStringIgnoreNull(msg);
ChannelHandlerContext socket = MyWebSocket2.getWebSocketMap().get(id);
if (socket != null) {
log.info("给<{}>发送消息:{}", id, ret);
socket.writeAndFlush(new TextWebSocketFrame(ret));
return 1;
} else {
log.info("<{}>连接不存在,不处理", id);
}
} catch (Exception ex) {
log.error("", ex);
}
return 0;
} public static int sendMsg(String id, String msg) {
try {
ChannelHandlerContext socket = MyWebSocket2.getWebSocketMap().get(id);
if (socket != null) {
log.info("给<{}>发送消息:{}", id, msg);
socket.writeAndFlush(new TextWebSocketFrame(msg));
return 1;
} else {
log.info("连接不存在,不处理");
}
} catch (Exception ex) {
log.error("", ex);
}
return 0;
}
}
package com.qmtt.server; 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 ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel e) throws Exception { e.pipeline().addLast("http-codec", new HttpServerCodec());
e.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
e.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
e.pipeline().addLast("handler", new MyWebSocket2());
}
}
此代码为诗词荣耀websocket的实现,解决了tomcat实现的websocket连接不稳定的问题
Netty实现WebSocket的更多相关文章
- 【Netty】WebSocket
一.前言 前面学习了codec和ChannelHandler之间的关系,接着学习WebSocket. 二.WebSocket 2.1. WebSocket介绍 WebSocket协议允许客户端和服务器 ...
- Netty对WebSocket的支持(五)
Netty对WebSocket的支持(五) 一.WebSocket简介 在Http1.0和Http1.1协议中,我们要实现服务端主动的发送消息到网页或者APP上,是比较困难的,尤其是现在IM(即时通信 ...
- 使用Netty做WebSocket服务端
使用Netty搭建WebSocket服务器 1.WebSocketServer.java public class WebSocketServer { private final ChannelGro ...
- Netty之WebSocket和四种IO介绍
Netty简介 一.什么是netty? 高性能 事件驱动 异步非堵塞 基于NIO的客户端,服务器端编程框架 稳定性和伸缩性 二.Netty的使用场景 高性能领域 多线程并发领域 异步通信领域 ...
- netty实现websocket发送文本和二进制数据
原文:https://huan1993.iteye.com/blog/2433552 最近在学习netty相关的知识,看到netty可以实现 websoket,因此记录一下在netty中实现webso ...
- Netty 搭建 WebSocket 服务端
一.编码器.解码器 ... ... @Autowired private HttpRequestHandler httpRequestHandler; @Autowired private TextW ...
- netty系列之:使用netty搭建websocket服务器
目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...
- netty系列之:使用netty搭建websocket客户端
目录 简介 浏览器客户端 netty对websocket客户端的支持 WebSocketClientHandshaker WebSocketClientCompressionHandler netty ...
- Netty 实现 WebSocket 聊天功能
上一次我们用Netty快速实现了一个 Java 聊天程序(见http://www.waylau.com/netty-chat/).现在,我们要做下修改,加入 WebSocket 的支持,使它可以在浏览 ...
- 基于netty的websocket例子
nettyServer package com.atguigu.netty.websocket; import javax.annotation.PostConstruct; import org.s ...
随机推荐
- 解析腾讯企业邮箱到自己域名,设置mail的cname
之前注册了腾讯企业邮的免费邮箱,后来想把企业邮箱和域名绑定起来,发现了一些问题. 先来看正常的部分,假设你已经注册过了腾讯企业邮箱免费版,并且已经绑定好了域名. 然后在域名提供商那里设置域名解析的MX ...
- Swift(二)控制流
要处理条件逻辑,使用 if 和 switch ,要处理循环逻辑,使用 for-in, for, while, 和 do-while .包着条件或者循环的括号可加可不加.处理逻辑体的花括弧是必须加的. ...
- putty与emacs
win环境下putty登录到linux并使用emacs时,需要折腾好配置才能正常工作.下面是折腾过程中碰到的问题与解决步骤: 1)要在putty控制台中启用鼠标,只需要在.emacs配置文件中启用xt ...
- 用HTTP操作和文件操作把网页下载到sd卡
这里先把代码贴到这里做一个存档,写到SD卡的是一个txt文件,改成HTML格式之后会出现百度主页,但是中文是乱码,这一点先暂时不去研究了. 代码: package com.larry.gotcha; ...
- .NETFramework:Stream
ylbtech-.NETFramework:Stream 1.返回顶部 1. #region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, Publi ...
- kvm_虚拟机迁移
virsh domblklist 虚拟机名称 #查看虚拟磁盘文件 一.kvm虚拟机静态迁移 1.静态迁移就是虚拟机在关机状态下,拷贝虚拟机虚拟磁盘文件与配置文件到目标虚拟主机中,实现的迁移. (1)虚 ...
- ASP.NET Core MVC 2.x 全面教程__ASP.NET Core MVC 19. XSS & CSRF
存库之前先净化,净化之后再提交到数据库 刚才插入的那笔数据 把默认的Razor引擎默认的EnCode去掉.Razor默认会开启htmlEnCodding 数据恢复回来 插入数据库之前对插入的数据进行净 ...
- Cocoapods fatal: Remote branch #{s.version} not found in upstream origin
遇到一个错误: fatal: Remote branch #{s.version} not found in upstream origin 解决办法 网上搜了很多,都无效 可能仅适用我的情况,分享出 ...
- 纳尼,Java 存在内存泄泄泄泄泄泄漏吗?
01. 怎么回事? 纳尼,Java 不是自动管理内存吗?怎么可能会出现内存泄泄泄泄泄泄漏! Java 最牛逼的一个特性就是垃圾回收机制,不用像 C++ 需要手动管理内存,所以作为 Java 程序员很幸 ...
- E20170425-gg
margin n(网页设置,CSS) 外铺,(金融,外汇交易)保证金 property n(计算机)类的属性变量 , 资产