很多视频播放都具备弹幕功能,A发了一个文字,B,C播放该视频若是开启了弹幕就能看到A发的。一开始挺好奇这个是怎么实现的,用JAVA是不是可以实现呢?答案肯定是可以的,最笨的用ajax 轮询。但是这种会给服务器造成很大压力,也浪费了服务器资源。netty 中 ChannelGroup可广播消息 netty+ websocket 则性能大大改善,废话不哔哔,下面直接贴代码了。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Netty WebSocket DEMO</title>
<script src="jquery.min.js"></script>
<style>
.tanmuContent {
justify-content: space-between;
}
.headImg img {
width: 100%;
height: 100%;
border-radius: 50%;
}
.headImg {
display: inline-block;
width: 30px;
height: 30px;
background: red;
border-radius: 41px;
}
</style>
</head>
<body>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/ws");
//连接创建成功时被回调
socket.onopen = function(event) {
// alert("websocket创建成功!");
};
//收到服务端的消息时被回调
socket.onmessage = function(event) {
showMsg(event.data);
};
socket.onclose = function(event) {
var ta = document.getElementById('responseText');
ta.value = ta.value + "连接被关闭";
};
} else {
alert("你的浏览器不支持!");
}
function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("连接没有开启.");
}
}
function showMsg(msg){
var id="div_"+new Date().getTime();
var html='<div id="'+id+'" class="tanmuContent" style="position: absolute;"><span class="headImg"><img src="tanmuhead.jpg"></span>'+msg+'<div class="praiseBox"><span class="t-praise "></span></div></div>';
var height=$(document).height();
var width=$(document.body).width();
html =$(html);
var t = Math.floor(Math.random()*(height-1+1)+1);
html.css("left",width*0.8+"px")
html.css("top",t+"px")
$("body").append(html);
$("#"+id).animate({"left":-width},20000,function(){
$("#"+id).remove();
console.log("sss")
});
}
</script>
<form onsubmit="return false;">
<input type="text" name="message" value="Hello, World!"><input
type="button" value="发送消息"
onclick="send(this.form.message.value)">
</form>
</body>
</html>
package com.xuyw;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.DefaultChannelGroup;
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.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
public class WebSocketService2 {
public void run(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
final ServerBootstrap sb = new ServerBootstrap();
sb.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(final SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new HttpResponseEncoder(),
new HttpRequestDecoder(),
new HttpObjectAggregator(65536),
new WebSocketServerProtocolHandler("/ws"),
new CustomTextFrameHandler());
}
}).option(ChannelOption.SO_BACKLOG, 65536)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true);
//.childOption(ChannelOption.SO_BROADCAST, true);
//bootstrap.setOption("child.reuseAddress", true);
//bootstrap.setOption("child.tcpNoDelay", true);
//bootstrap.setOption("child.keepAlive", true);
final Channel ch = sb.bind(port).sync().channel();
System.out.println("Web socket server started at port " + port);
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new WebSocketService2().run(port);
}
}
package com.xuyw;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
public class CustomTextFrameHandler extends
SimpleChannelInboundHandler<TextWebSocketFrame> {
private static ChannelGroup recipients = new DefaultChannelGroup(
"ChannelGroups", GlobalEventExecutor.INSTANCE);
public CustomTextFrameHandler() {
}
@Override
protected void messageReceived(ChannelHandlerContext ctx,
TextWebSocketFrame frame) throws Exception {
String request = frame.text();
// ctx.channel().writeAndFlush(new
// TextWebSocketFrame(request.toUpperCase()));
System.out.println("size:" + recipients.size());
recipients.writeAndFlush(new TextWebSocketFrame(request.toUpperCase()));
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
recipients.add(ctx.channel());
System.out.println("connect:" + recipients.size());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
try {
recipients.remove(ctx.channel());
System.out.println("删除channel成功" + recipients.size());
} catch (Exception ex) {
System.out.println("删除channel失败" + ex.getMessage());
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
因为是随手写的demo,比较烂,凑合着看吧
本例基于netty 5.0.0.Alpha2