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的更多相关文章

  1. 【Netty】WebSocket

    一.前言 前面学习了codec和ChannelHandler之间的关系,接着学习WebSocket. 二.WebSocket 2.1. WebSocket介绍 WebSocket协议允许客户端和服务器 ...

  2. Netty对WebSocket的支持(五)

    Netty对WebSocket的支持(五) 一.WebSocket简介 在Http1.0和Http1.1协议中,我们要实现服务端主动的发送消息到网页或者APP上,是比较困难的,尤其是现在IM(即时通信 ...

  3. 使用Netty做WebSocket服务端

    使用Netty搭建WebSocket服务器 1.WebSocketServer.java public class WebSocketServer { private final ChannelGro ...

  4. Netty之WebSocket和四种IO介绍

    Netty简介 一.什么是netty? 高性能 事件驱动 异步非堵塞 基于NIO的客户端,服务器端编程框架 稳定性和伸缩性 二.Netty的使用场景 高性能领域   多线程并发领域   异步通信领域 ...

  5. netty实现websocket发送文本和二进制数据

    原文:https://huan1993.iteye.com/blog/2433552 最近在学习netty相关的知识,看到netty可以实现 websoket,因此记录一下在netty中实现webso ...

  6. Netty 搭建 WebSocket 服务端

    一.编码器.解码器 ... ... @Autowired private HttpRequestHandler httpRequestHandler; @Autowired private TextW ...

  7. netty系列之:使用netty搭建websocket服务器

    目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...

  8. netty系列之:使用netty搭建websocket客户端

    目录 简介 浏览器客户端 netty对websocket客户端的支持 WebSocketClientHandshaker WebSocketClientCompressionHandler netty ...

  9. Netty 实现 WebSocket 聊天功能

    上一次我们用Netty快速实现了一个 Java 聊天程序(见http://www.waylau.com/netty-chat/).现在,我们要做下修改,加入 WebSocket 的支持,使它可以在浏览 ...

  10. 基于netty的websocket例子

    nettyServer package com.atguigu.netty.websocket; import javax.annotation.PostConstruct; import org.s ...

随机推荐

  1. box-sizing: border-box;的作用

    box-sizing 属性可以被用来调整这些表现: content-box  是默认值.如果你设置一个元素的宽为100px,那么这个元素的内容区会有100px宽,并且任何边框和内边距的宽度都会被增加到 ...

  2. 使用pngcrush压缩png图片

    写在前面:         Pngcrush是一个优化的PNG(便携式网络图形)文件.它可以运行在MSDOS窗口中一个命令行,或从UNIX或LINUX命令行.其主要目的是为了 减少PNG IDAT数据 ...

  3. 【USACO】The Cow Prom

    [题目链接] 点击打开链接 [算法] tarjan求强连通分量 [代码] #include<bits/stdc++.h> #define MAXN 20005 using namespac ...

  4. holiday题解

    题目描述如下: 经过几个月辛勤的工作,FJ 决定让奶牛放假.假期可以在 1…N 天内任意选择一段(需要连续),每一天都有一个享受指数 W.但是奶牛的要求非常苛刻,假期不能短于 P 天,否则奶牛不能得到 ...

  5. jQuery测试结果

    您的回答: 1.下面哪种说法是正确的? 您的回答:jQuery 是 JavaScript 库 2.jQuery 使用 CSS 选择器来选取元素? 您的回答:正确 3.jQuery 的简写是? 您的回答 ...

  6. 【旧文章搬运】ZwQuerySystemInformation枚举内核模块及简单应用

    原文发表于百度空间,2008-10-24========================================================================== 简单说,即 ...

  7. auto_ptr智能指针

    C++的auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理. 使用std::auto_ptr,要#include <memory>.

  8. 在Emacs中使用ECB(转载)

    转自:http://joerong666.iteye.com/blog/1813876 By:             潘云登 Date:          2009-7-9 Email:       ...

  9. $(function() {}),即$(document).ready(function(),什么时候执行?以此为准

    $(function() { //执行操作 }); $(function() {}) 是$(document).ready(function()的简写. 这个函数什么时候执行的呢? 答案:DOM加载完 ...

  10. 纳尼,Java 存在内存泄泄泄泄泄泄漏吗?

    01. 怎么回事? 纳尼,Java 不是自动管理内存吗?怎么可能会出现内存泄泄泄泄泄泄漏! Java 最牛逼的一个特性就是垃圾回收机制,不用像 C++ 需要手动管理内存,所以作为 Java 程序员很幸 ...