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 ...
随机推荐
- ubuntu 本地和服务器scp文件传输
安装 SSH(Secure Shell) 服务以提供远程管理服务 sudo apt-get install ssh SSH 远程登入 Ubuntu 机 ssh username@192.168.0.1 ...
- 用jetty起maven工程debug报source not found
之前基本都是tomcat启maven本工程 惯性的处理方式是 直接点击lookup source 直接先删除default工程 然后选择导入java project 找到源码所在的工程 但是一般情况下 ...
- Oracle:通过pl/sql developer工具导入excel数据
1.在pl/sql developer中选择工具-->ODBC导入器 2.选择需要导入的EXCEL文件(CVS也可以):用户名.口令不用管,直接点“连接”,找到要导入的xls文件 3. 选择“导 ...
- Python小练习_数据库表数据导出到excel
需求:只要传入一个表名,就能把所有的数据导入出来,字段名是excel的表头 1.要动态获取到标的字段 cur.descrption能获取到表的字段 fileds = [filed[0] for fil ...
- Android 不同阶段 Logo 显示
/********************************************************************* * Android 不同阶段 Logo 显示 * 说明: ...
- SPOJ:Free tour II (树分治+启发式合并)
After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, ...
- [Java]手动编译Java
1.安装JDK 2.编写 Example.java 程序放到C 盘 public class Example { public static void main(string[] args) { sy ...
- 关于return
return ; 相当于执行完跳转url后停止,return无返回值仅作停止作用,是指停止当前方法,是方法的终点 return null ; 代表引用类型的空值
- 通过链接将JSP页面中一变量传到另一JSP界面中
A.jsp 发送 <a herf="B.jsp?name=<%=name%>">传递到B页面</a> B.jsp 接收 <%String ...
- oracle 查看表是否存在、包含某字段的表、表是否包含字段
表是否存在: select count(*) from user_tables where table_name = #{tablename} 包含某个字段的表 select * from user_ ...