java
复制代码
package com.cetcnav.nioWebSocket;
import com.cetcnav.base.utils.PropertiesUtils;
import cn.hutool.core.lang.Console;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* @author wp
* 2021年4月14日下午3:43:54
* smartReceptionWS
* NioWebSocketServer.java
* 类说明:
*/
public class NioWebSocketServer implements Runnable {
@Override
public void run() {
init();
}
/**
* niowebsocket端口号
*/
private final static Integer port = PropertiesUtils.getInteger("nio.wensocket.port");
public void init(){
Console.log("正在启动websocket服务器");
NioEventLoopGroup boss=new NioEventLoopGroup();
NioEventLoopGroup work=new NioEventLoopGroup();
try {
ServerBootstrap bootstrap=new ServerBootstrap();
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);// 防止tcp数据积攒大块发送;有数据了就发送
bootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);
bootstrap.group(boss,work).localAddress(port);// 绑定监听端口;
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new NioWebSocketChannelInitializer());
// 服务器异步创建绑定
Channel channel = bootstrap.bind().sync().channel();
Console.log("webSocket服务器启动成功:"+channel);
//关闭服务器通道
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
Console.error("运行出错:"+e);
}finally {
boss.shutdownGracefully();
work.shutdownGracefully();
Console.log("websocket服务器已关闭");
}
}
public static void main(String[] args) {
new NioWebSocketServer().init();
}
}
java
复制代码
package com.cetcnav.nioWebSocket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
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.*;
import io.netty.util.CharsetUtil;
import java.util.Date;
import cn.hutool.core.lang.Console;
/**
* @author wp
* 2021年4月14日下午3:49:10
* smartReceptionWS
* NioWebSocketHandler.java
* 类说明:
*/
import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
/**
* @author wp
* 2021年4月15日下午3:43:31
* smartReceptionWS
* NioWebSocketHandler.java
* 类说明:
*/
public class NioWebSocketHandler extends SimpleChannelInboundHandler<Object> {
private WebSocketServerHandshaker handshaker;
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
Console.log("收到消息:"+msg);
if (msg instanceof FullHttpRequest){
//以http请求形式接入,但是走的是websocket
handleHttpRequest(ctx, (FullHttpRequest) msg);
}else if (msg instanceof WebSocketFrame){
//处理websocket客户端的消息
handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//添加连接
Console.log("客户端加入连接:"+ctx.channel());
ChannelHandlerPool.addChannel(ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//断开连接
Console.log("客户端断开连接:"+ctx.channel());
ChannelHandlerPool.removeChannel(ctx.channel());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame){
// 判断是否关闭链路的指令
if (frame instanceof CloseWebSocketFrame) {
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)) {
Console.log("本例程仅支持文本消息,不支持二进制消息");
throw new UnsupportedOperationException(String.format(
"%s frame types not supported", frame.getClass().getName()));
}
// 返回应答消息
String request = ((TextWebSocketFrame) frame).text();
Console.log("服务端收到:" + request);
TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()
+ ctx.channel().id() + ":" + request);
// 群发
// ChannelHandlerPool.send2All(tws);
// 返回【谁发的发给谁】
ctx.channel().writeAndFlush(tws);
}
/**
* 唯一的一次http请求,用于创建websocket
*/
private void handleHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req) {
//要求Upgrade为websocket,过滤掉get/Post
if (!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
//若不是websocket方式,则创建BAD_REQUEST的req,返回给客户端
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("", null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
//版本不支持
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), req);
}
}
/**
* 拒绝不合法的请求,并返回错误信息
* */
private static 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();
}
ChannelFuture f = ctx.channel().writeAndFlush(res);
// 如果是非Keep-Alive,关闭连接
if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
}
java
复制代码
package com.cetcnav.nioWebSocket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
/**
* @author wp
* 2021年4月14日下午3:50:52
* smartReceptionWS
* ChannelHandlerPool.java
* 类说明:通道组池,管理所有websocket连接
*/
public class ChannelHandlerPool {
private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
*
*/
private static ConcurrentMap<String, ChannelId> ChannelMap=new ConcurrentHashMap<String, ChannelId>();
/**
* 通道组池,管理所有websocket连接 含有具体的指定用户名称
*/
public static Set<Channel> channelGroup = Collections.synchronizedSet(new HashSet<>());
public static void addChannel(Channel channel){
group.add(channel);
ChannelMap.put(channel.id().asShortText(),channel.id());
}
public static void removeChannel(Channel channel){
group.remove(channel);
ChannelMap.remove(channel.id().asShortText());
/**
* 去除指定用户名称通道
*/
String channelId = channel.id().asShortText();
Iterator<Channel> it = channelGroup.iterator();
while(it.hasNext()){
Channel data = it.next();
String removeString = data.id().asShortText();
if (channelId.equals(removeString)){
it.remove();
}
}
}
public static Channel findChannel(String id){
return group.find(ChannelMap.get(id));
}
public static void send2All(TextWebSocketFrame tws){
group.writeAndFlush(tws);
}
public static String sendMessage(String parkId, String message){
List<Channel> channelList = getChannelByName(parkId);
if (channelList.size() <= 0) {
return "parkId" + parkId + "不在线!";
}
ByteBuf byteBuf = Unpooled.copiedBuffer(message.getBytes());
// channelList.forEach(channel -> channel.writeAndFlush(new BinaryWebSocketFrame(byteBuf)));
channelList.forEach(channel -> channel.writeAndFlush(new TextWebSocketFrame(message)));
return "success";
}
/**
* 根据用户parkId查找channel
*
* @param parkId
* @return
*/
public static List<Channel> getChannelByName(String parkId) {
AttributeKey<String> key = AttributeKey.valueOf("parkId");
return ChannelHandlerPool.channelGroup.stream().filter(channel -> channel.attr(key).get().equals(parkId))
.collect(Collectors.toList());
}
}
java
复制代码
package com.cetcnav.worker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cetcnav.cacheManage.MessageCahce;
import com.cetcnav.nioWebSocket.ChannelHandlerPool;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
/**
* @author wp
* @date 2021年6月3日下午8:11:47
* @projectName GRWebSocket
* @fileName SendMessage2WebWorker.java
* @tags 类说明: 主动向前端推送解析之后的数据信息
*/
public class SendMessage2WebWorker implements Runnable {
private static Logger log = LoggerFactory.getLogger(SendMessage2WebWorker.class);
@Override
public void run() {
while (true) {
String json = MessageCahce.take();
log.info(json);
ChannelHandlerPool.send2All(new TextWebSocketFrame(json));
if (json.contains("0060")){
log.info("已推送干扰数据:");
}
else if(json.contains("0061")){
log.info("已推送功放数据:");
}
}
}
}
xml
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Netty-Websocket</title>
</head>
<script src="webjars/jquery/3.4.1/jquery.js"></script>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
// uid字段要和后台WebSocketHandler类中的paramMap.get("uid")字段对应
socket = new WebSocket("ws://222.222.216.210:7777/websocket"); // 55代表用户id
socket.onmessage = function (event) {
//$("#websocket").html(event.data)
console.log(event.data)
};
socket.onopen = function (event) {
console.log("Netty-WebSocket服务器。。。。。。连接")
};
socket.onclose = function (event) {
console.log("Netty-WebSocket服务器。。。。。。关闭")
};
} else {
alert("您的浏览器不支持WebSocket协议!");
}
</script>
<body>
<div id="ws" class="ws">
</div>
</body>
</html>