最近有做一个h5项目,有如下场景需求:
1.实现实时聊天。
2.多人登录,有一个教师角色可控制其他角色用户的视频播放(播放,暂停,快进)。
最初考虑视频控制的实现方向是推流,但是由于没有接触过这方面,着实头疼了一阵。最后,经过一夜梦中思考,次日上午,采用指令的方式实现对视频的控制。
是不是听着很高大上?其实很简单,以上两个功能都是通过onmessage函数实现,将教师端的页面加上监控视频的事件,以指令的方式发送至学生端。
以下是一些核心代码:
后天:
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class WebSocketMessageInboundPool {
//保存连接的MAP容器
private static final Map<String,WebSocketMessageInbound > connections = new HashMap<String,WebSocketMessageInbound>();
//向连接池中添加连接
public static void addMessageInbound(WebSocketMessageInbound inbound){
//添加连接
System.out.println("user : " + inbound.getUser() + " join..");
connections.put(inbound.getUser(), inbound);
}
//获取所有的在线用户
public static Set<String> getOnlineUser(){
return connections.keySet();
}
public static void removeMessageInbound(WebSocketMessageInbound inbound){
//移除连接
System.out.println("user : " + inbound.getUser() + " exit..");
connections.remove(inbound.getUser());
}
public static void sendMessageToUser(String user,String message){
try {
//向特定的用户发送数据
System.out.println("send message to user : " + user + " ,message content : " + message);
WebSocketMessageInbound inbound = connections.get(user);
if(inbound != null){
inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
}
} catch (IOException e) {
e.printStackTrace();
}
}
//向所有的用户发送消息
public static void sendMessage(String message){
try {
Set<String> keySet = connections.keySet();
for (String key : keySet) {
WebSocketMessageInbound inbound = connections.get(key);
if(inbound != null){
System.out.println("send message to user : " + key + " ,message content : " + message);
inbound.getWsOutbound().writeTextMessage(CharBuffer.wrap(message));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import net.sf.json.JSONObject;
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;
public class WebSocketMessageInbound extends MessageInbound {
//当前连接的用户名称
private final String user;
public WebSocketMessageInbound(String user) {
this.user = user;
}
public String getUser() {
return this.user;
}
//建立连接的触发的事件
@Override
protected void onOpen(WsOutbound outbound) {
// 触发连接事件,在连接池中添加连接
JSONObject result = new JSONObject();
result.element("type", "user_join");
result.element("user", this.user);
//向所有在线用户推送当前用户上线的消息
// WebSocketMessageInboundPool.sendMessage(result.toString());
result = new JSONObject();
result.element("type", "get_online_user");
result.element("list", WebSocketMessageInboundPool.getOnlineUser());
//向连接池添加当前的连接对象
WebSocketMessageInboundPool.addMessageInbound(this);
//向当前连接发送当前在线用户的列表
// WebSocketMessageInboundPool.sendMessageToUser(this.user, result.toString());
}
@Override
protected void onClose(int status) {
// 触发关闭事件,在连接池中移除连接
WebSocketMessageInboundPool.removeMessageInbound(this);
/*JSONObject result = new JSONObject();
result.element("type", "user_leave");
result.element("user", this.user);
//向在线用户发送当前用户退出的消息
WebSocketMessageInboundPool.sendMessage(result.toString());*/
}
@Override
protected void onBinaryMessage(ByteBuffer message) throws IOException {
throw new UnsupportedOperationException("Binary message not supported.");
}
//客户端发送消息到服务器时触发事件
@Override
protected void onTextMessage(CharBuffer message) throws IOException {
JSONObject jsStr = JSONObject.fromObject(message.toString());
System.out.println(message.toString());
if(jsStr.containsKey("to")){
String uid = jsStr.getString("to");
WebSocketMessageInboundPool.sendMessageToUser(uid, message.toString());
}else{
//向所有在线用户发送消息
WebSocketMessageInboundPool.sendMessage(message.toString());
}
}
}
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
public class HelloWebSocketServlet extends WebSocketServlet {
private static final long serialVersionUID = 1L;
private final AtomicInteger connectionIds = new AtomicInteger(0);
//跟平常Servlet不同的是,需要实现createWebSocketInbound,在这里初始化自定义的WebSocket连接对象
@Override
protected StreamInbound createWebSocketInbound(String subProtocol,HttpServletRequest request) {
return new WebSocketMessageInbound(this.getUser(request));
}
public String getUser(HttpServletRequest request){
return (String) request.getParameter("id");
}
}
前端js:
教师端
var websocket;
var audio = document.getElementById('event_video');
//var wsUrl = "ws://daxin.tunnel.qydev.com/yanl-websocket/websocket/say?id=";
var wsUrl = "ws://"+ipaddr+"/yanl-websocket/websocket/say?id=";
//var wsUrl = "ws://127.0.0.1:8080/yanl-websocket/websocket/say?id=";
//视频控制
var video = {
timer:null,
audioStat : false,
reset : function(){
audio.currentTime = 0;
},
play : function(){
if(audio!==null){
video.audioStat = true;
user_ws.allSendInstructions(video_flag.play + Math.ceil(audio.currentTime));
}
},
monitoring : function (){
//关闭之前开启的定时事件
if(video.timer != null){
window.clearInterval(video.timer);
}
var time1 = audio.currentTime;
video.timer = setInterval(function(){
time2 = time1;
time1 = audio.currentTime;
console.info(time1-time2);
if((time1-time2)>1){
console.info("快进了");
user_ws.allSendInstructions(video_flag.go_to + Math.ceil(time1));
}else if(audio.paused && video.audioStat){
video.audioStat = false;
user_ws.allSendInstructions(video_flag.pause + Math.ceil(time1));
}
if(time1 == audio.duration || time1 == 0){
window.clearInterval(video.timer);
console.info("暂停了");
}
},300);
}
}
var user_ws = {
crid : "",
userList : [],
user_guid : "",
name : "",
send : function(toId,content,instructions){
var data={};
data.to = toId;
data.from = user_ws.user_guid;
data.content = content;
data.type = instructions == null ? 'message':instructions;
websocket.send(JSON.stringify(data));
},
allSendInstructions : function(content){
for ( var u in users.list) {
if(users.list[u].realizeUsers != null && users.list[u].realizeUsers != ""){
for ( var uu in users.list[u].realizeUsers) {
user_ws.send(users.list[u].realizeUsers[uu].su_id,content,'instructions');
}
}
}
},
allSend : function(content){
for ( var u in users.list) {
if(users.list[u].realizeUsers != null && users.list[u].realizeUsers != ""){
for ( var uu in users.list[u].realizeUsers) {
user_ws.send(u.user_suid,content);
}
}
}
},
eventChange : function(){
user_ws.allSendInstructions("eventChange");
}
}
$(document).ready(function() {
var suid = sessionStorage.getItem("suid");
suid = JSON.parse(suid);
//用户的全局唯一标识:个人id
user_ws.user_guid = suid.data.su_id;
ws_com.user_guid = suid.data.su_id;
user_ws.name = suid.data.su_name;
user_ws.crid = sessionStorage.getItem("crid");
//初始化websocket
if ('WebSocket' in window) {
websocket = new WebSocket(wsUrl + user_ws.user_guid);
}
websocket.onmessage = function(event) {
var data=JSON.parse(event.data);
if(data.type == "message"){
if(!($(this).hasClass('active'))) {
$("#message").addClass("active");
$(".message").css("visibility", "visible");
$(".message").css("z-index",10);
}
//接收聊天内容
var content = JSON.parse(data.content);
ws_com.createRecord(content);
}
};
});
function yp(){
var ypv = $("#ylpx").val();
user_ws.allSendInstructions(ypv);
}
$("#content_c").mousedown(function(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
});
$("#messAll").mousedown(function(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
});
$("#userList_c").mousedown(function(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
});
学生端:
// 初始化websocket
var websocket;
var audio = document.getElementById('v');
var wsUrl = "ws://"+ipaddr+"/yanl-websocket/websocket/say?id=";
//var wsUrl = "ws://127.0.0.1:8080/yanl-websocket/websocket/say?id=";
//var wsUrl = "ws://daxin.tunnel.qydev.com/yanl-websocket/websocket/say?id=";
var video = {
pause : function (time){
audio.currentTime = time;
audio.pause();
},
play : function(time){
if (audio !== null) {
if (audio.currentTime == 0) {
audio.play();
} else {
audio.currentTime = time;
audio.play();
}
}
},
go_to : function(time){
audio.currentTime = time;
audio.play();
}
}
$(function(){
var suid = sessionStorage.getItem("suid");
suid = JSON.parse(suid);
//用户的全局唯一标识:事件id+个人id,中间以逗号隔开
var user_guid = suid.data.su_id;
ws_com.user_guid = user_guid;
//初始化websocket
if ('WebSocket' in window) {
websocket = new WebSocket(wsUrl + user_guid);
}
websocket.onmessage = function(event) {
var data = JSON.parse(event.data);
if(data.type == "instructions"){
if (data.content.indexOf(video_flag.play) > -1) {
var time = data.content.split(video_flag.play)[1];
video.play(time);
} else if (data.content.indexOf(video_flag.pause) > -1) {
var time = data.content.split(video_flag.pause)[1];
video.pause(time);
} else if (data.content.indexOf(video_flag.go_to) > -1) {
var time = data.content.split(video_flag.go_to)[1];
video.go_to(time);
} else if (data.content.indexOf("eventChange") > -1) {
//刷新页面数据
events.getEventsList();
}else if (data.content.indexOf("wd_show") > -1) {
//培训/演练切换
$("#newShow").show();
$("#wd").show();
}else if (data.content.indexOf("wd_hide") > -1) {
//培训/演练切换
$("#newShow").hide();
$("#wd").hide();
}
}else if(data.type == "message"){
if(!($(this).hasClass('active'))) {
communication.openWindow();
$("#message").addClass("active");
$(".message").css("visibility", "visible");
$(".message").css("z-index",10);
}
//接收聊天内容
var content = JSON.parse(data.content);
ws_com.createRecord(content);
}
};
});
//阻止事件冒泡
$("#content_c").mousedown(function(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
});
$("#messAll").mousedown(function(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
});
$("#userList").mousedown(function(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
});
注意!!!
Tomcat7和8对于websocket的支持是不同的,以上代码是使用的Tomcat7,友情提示,避免踩坑