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 ...
随机推荐
- iOS UIView控件的常用属性和方法的总结
一 UIVIew 常见属性1.frame 位置和尺寸(以父控件的左上角为原点(0,0))2.center 中点 (以父控件的左上角为原点(0,0))3.bounds 位置和尺寸(以自己的左上角为原点 ...
- js 中继承的几种方式
继承的方式一共有三种: 一.原型继承 通过prototype 来实现继承. function Person(name,age) { this.name=name; this.age=age; } ...
- atomic_cmpxchg()/Atomic_read()/Atomic_set()/Atomic_add()/Atomic_sub()/atomi
[ 1.atomic_read与atomic_set函数是原子变量的操作,就是原子读和原子设置的作用.2.原子操作,就是执行操作的时候,其数值不会被其它线程或者中断所影响3.原子操作是linux内核中 ...
- POJ3080 Blue Jeans —— 暴力枚举 + KMP / strstr()
题目链接:https://vjudge.net/problem/POJ-3080 Blue Jeans Time Limit: 1000MS Memory Limit: 65536K Total ...
- 推箱子 Sokoban(华中农业比赛)
点这里 打开题目链接 点击打开链接 题目就是我们玩过的推箱子: 一顿暴力广搜:加状态标记.状态压缩需要用到一个类似于康拓的思想来压缩:所以容易TLE,搜索就是用一个int型的数字来表示一个状态, ...
- I.MX6 不一定要设置BOOT_MODE进入烧录模式
/************************************************************************* * I.MX6 不一定要设置BOOT_MODE进入 ...
- I.MX6 sdio 设备注册及识别
/************************************************************************* * I.MX6 sdio 设备注册及识别 * 说明 ...
- maven+springmvc+spring+mybatis+mysql详细搭建整合过程讲解
转自:https://www.cnblogs.com/lmei/p/7190755.html?utm_source=itdadao&utm_medium=referral @_@ 写在最前 之 ...
- eclipse整合tomcat
首先确保jdk已经安装好 步骤1 获得服务器运行环境配置,Window/Preferences/Server/Runtime Environmen l步骤2 添加服务器 步骤3 选择服务器在硬盘的地址 ...
- 考拉定时任务框架kSchedule
此文已由作者杨凯明授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.背景 目前项目中使用的定时任务框架存在下面这些问题 没有统一的定时任务管理平台 目前项目中使用定时任务的 ...