Netty-SocketIO实时视频弹幕交互功能
前言Netty-SocketIO是一个开源的、基于Netty的、Java版的即时消息推送项目。通过Netty-SocketIO,我们可以轻松的实现服务端主动向客户端推送消息的场景,比如说股票价格变化、K线图、消息提醒等。它和websocket有相同的作用,只不过Netty-SocketIO可支持所有的浏览器。传输流程代码实现pom<!--docker io 通信--><depen
·
前言
Netty-SocketIO是一个开源的、基于Netty的、Java版的即时消息推送项目。通过Netty-SocketIO,我们可以轻松的实现服务端主动向客户端推送消息的场景,比如说股票价格变化、K线图、消息提醒等。它和websocket有相同的作用,只不过Netty-SocketIO可支持所有的浏览器。
传输流程
代码实现
pom
<!-- docker io 通信 -->
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.19</version>
</dependency>
Event
/**
* @author lanys
* @Description:
* @date 26/8/2021 上午11:13
*/
public interface Event {
/*****************************************聊天消息相关[start]********************************************/
/**
* 派对消息发送
*/
String ChatAcceptLineMsg = "partyAcceptLineMsg";
/*****************************************聊天消息相关[end]********************************************/
}
NettySocketIoConfig
/**
* @author lanys
* @Description: 配置 Socket 服务器
* @date 26/8/2021 上午11:19
*/
@Configuration
public class NettySocketIoConfig {
/**
* 配置 Socket 服务器
*
* @return SocketIOServer
*/
@Bean
public SocketIOServer socketIOServer(){
com.corundumstudio.socketio.Configuration configuration = new com.corundumstudio.socketio.Configuration();
configuration.setHostname("127.0.0.1");
configuration.setPort(9094);
return new SocketIOServer(configuration);
}
/**
* SpringAnnotationScanner 用于扫描 Netty-socketIo @OnConnect @OnEvent
*
* @return Spring 注解扫描器
*/
@Bean
public SpringAnnotationScanner springAnnotationScanner(){
return new SpringAnnotationScanner(socketIOServer());
}
}
Message
/**
* @author lanys
* @Description: socket 接收实体
* @date 26/8/2021 上午11:42
*/
@Data
public class Message {
private String name;
private String msg;
}
PartySocketIoStoreName
/**
* @author lanys
* @Description: 派对套接字key存储
* @date 23/8/2021 上午10:44
*/
public class PartySocketIoStoreName {
/**
* 房间名
*/
public static String ROOM = "room";
/**
* 房间名
*/
public static String ROOM_USER = "roomUser";
}
ChatRoomEventHandler
/**
* @author lanys
* @Description: 聊天事件
* @date 26/8/2021 上午11:02
*/
@Component
@Slf4j
public class ChatRoomEventHandler {
@Autowired
private SocketIOServer socketIOServer;
/**
* 连接
*
* @param client client
*/
@OnConnect
public void clientChatOnConnect(SocketIOClient client){
String id = client.getHandshakeData().getSingleUrlParam("id");
//存储SocketIOClient
client.joinRoom(PartySocketIoStoreName.ROOM);
//回发消息
client.sendEvent("message", "onConnect back");
System.out.println("客户端:" + client.getSessionId() + "已连接,id=" + id);
}
/**
* 客户端关闭连接时触发
*
* @param client client
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
System.out.println("客户端:" + client.getSessionId() + "断开连接");
}
/**
* 客户端事件
*
* @param client 客户端信息
* @param request 请求信息
* @param data 客户端发送数据
*/
@OnEvent(value = Event.ChatAcceptLineMsg)
public void onEvent(SocketIOClient client, AckRequest request, Message data) {
System.out.println(data.getName() + "发来消息:" + data.getMsg());
request.sendAckData(Event.ChatAcceptLineMsg, "我是服务器发来的信息");
//广播消息
socketIOServer.getRoomOperations(PartySocketIoStoreName.ROOM).sendEvent(Event.ChatAcceptLineMsg, data.getName() + ": " + data.getMsg());
}
}
client.joinRoom(PartySocketIoStoreName.ROOM); 类似一个HashMap,将client信息就行存储,等触发客户端事件,根据组的用户信息进行广播发送
SocketIoBootApplication
/**
* @author lanys
* @Description: 启动类
* @date 26/8/2021 上午9:47
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class SocketIoBootApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SocketIoBootApplication.class,args);
}
@Autowired
private SocketIOServer socketIOServer;
@Override
public void run(String... args) throws Exception {
socketIOServer.start();
System.out.println("socket.io启动成功!");
}
}
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Netty视频弹幕实现 Author:Binhao Liu</title>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<style type="text/css" media="screen">
* {
margin: 0px;
padding: 0px
}
html, body {
height: 100%
}
body {
overflow: hidden;
background-color: #FFF;
text-align: center;
}
.flex-column {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.flex-row {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.wrap {
overflow: hidden;
width: 70%;
height: 600px;
margin: 100px auto;
padding: 20px;
background-color: transparent;
box-shadow: 0 0 9px #222;
border-radius: 20px;
}
.wrap .box {
position: relative;
width: 100%;
height: 90%;
background-color: #000000;
border-radius: 10px
}
.wrap .box span {
position: absolute;
top: 10px;
left: 20px;
display: block;
padding: 10px;
color: #336688
}
.wrap .send {
display: flex;
width: 100%;
height: 10%;
background-color: #000000;
border-radius: 8px
}
.wrap .send input {
width: 40%;
height: 60%;
border: 0;
outline: 0;
border-radius: 5px 0px 0px 5px;
box-shadow: 0px 0px 5px #d9d9d9;
text-indent: 1em
}
.wrap .send .send-btn {
width: 100px;
height: 60%;
background-color: #fe943b;
color: #FFF;
text-align: center;
border-radius: 0px 5px 5px 0px;
line-height: 30px;
cursor: pointer;
}
.wrap .send .send-btn:hover {
background-color: #4cacdc
}
</style>
</head>
<body>
<div class="wrap flex-column">
<div class="box">
<video src="./imgs/s1.mp4" width="100%" height="100%" controls autoplay></video>
</div>
<div class="send flex-row">
<input type="text" class="con" placeholder="弹幕发送[]~(^v^)~*"/>
<div class="send-btn" onclick="javascript:sendMsg(document.querySelector('.con').value)">发送</div>
</div>
<button id="connect" onClick='connect()'>Connect</button>
</div>
<div><p id="broadcast">广播区域</p></div>
<div><p id="status">等待连接</p></div>
<div><p id="message">你好!</p></div>
<script type="text/javascript">
/**
* 前端js的 socket.emit("事件名","参数数据")方法,是触发后端自定义消息事件的时候使用的,
* 前端js的 socket.on("事件名",匿名函数(服务器向客户端发送的数据))为监听服务器端的事件
**/
var socket;
//请求连接
function connect() {
if(socket!=null)return;
socket = io.connect("http://localhost:9094?id=123456")
//监听服务器连接事件
socket.on('connect', function()
{ status_update("连接到服务器");
});
//监听服务器关闭服务事件
socket.on('disconnect', function(){
status_update("从服务器中断开");
});
//监听服务器端发送消息事件
socket.on('partyAcceptLineMsg', function(data) {
message(data)
});
// //监听服务器广播事件
// socket.on('Broadcast', function(data) {
// Broadcast(data)
// });
}
//断开连接
function disconnect() {
socket.disconnect();
socket=null;
}
//显示服务器发来的消息
function message(data) {
// document.getElementById('responseText').innerHTML = "Server says: " + data;
//var ta = document.getElementById('responseText');
//ta.value += event.data+"\r\n";
createEle(data);
}
//显示当前状态
function status_update(txt){
document.getElementById('status').innerHTML = txt;
}
//显示服务器广播
function Broadcast(data) {
// document.getElementById('responseText').innerHTML = "广播: " + data;
}
//点击发送消息触发
function send() {
var msg = document.getElementById('input').value;
socket.emit('partyAcceptLineMsg', {name: '小明', msg: msg});
var ta = document.getElementById('responseText');
ta.value += "小明: "+msg+"\r\n";
};
function sendMsg(msg) {
console.log("开始发送");
console.log(msg);
socket.emit('partyAcceptLineMsg', {name: '小明', msg: msg});
console.log("发送结束");
}
//1.获取元素
var oBox = document.querySelector('.box'); //获取.box元素
var cW = oBox.offsetWidth; //获取box的宽度
var cH = oBox.offsetHeight; //获取box的高度
function createEle(txt) {
//动态生成span标签
var oMessage = document.createElement('span'); //创建标签
oMessage.innerHTML = txt; //接收参数txt并且生成替换内容
oMessage.style.left = cW + 'px'; //初始化生成位置x
oBox.appendChild(oMessage); //把标签塞到oBox里面
roll.call(oMessage, {
//call改变函数内部this的指向
timing: ['linear', 'ease-out'][~~(Math.random() * 2)],
color: '#' + (~~(Math.random() * (1 << 24))).toString(16),
top: random(0, cH),
fontSize: random(16, 32)
});
}
function roll(opt) {
//弹幕滚动
//如果对象中不存在timing 初始化
opt.timing = opt.timing || 'linear';
opt.color = opt.color || '#fff';
opt.top = opt.top || 0;
opt.fontSize = opt.fontSize || 16;
this._left = parseInt(this.offsetLeft); //获取当前left的值
this.style.color = opt.color; //初始化颜色
this.style.top = opt.top + 'px';
this.style.fontSize = opt.fontSize + 'px';
this.timer = setInterval(function () {
if (this._left <= 100) {
clearInterval(this.timer); //终止定时器
this.parentNode.removeChild(this);
return; //终止函数
}
switch (opt.timing) {
case 'linear': //如果匀速
this._left += -2;
break;
case 'ease-out': //
this._left += (0 - this._left) * .01;
break;
}
this.style.left = this._left + 'px';
}.bind(this), 1000 / 60);
}
function random(start, end) {
//随机数封装
return start + ~~(Math.random() * (end - start));
}
var aLi = document.querySelectorAll('li'); //10
function forEach(ele, cb) {
for (var i = 0, len = aLi.length; i < len; i++) {
cb && cb(ele[i], i);
}
}
forEach(aLi, function (ele, i) {
ele.style.left = i * 100 + 'px';
});
//产生闭包
var obj = {
num: 1,
add: function () {
this.num++; //obj.num = 2;
(function () {
console.log(this.num);
})
}
};
obj.add();//window
</script>
</body>
</html>
效果展示
总结
开始入门,就是这么简单!!
更多推荐
已为社区贡献5条内容
所有评论(0)