实现和(女)朋友一起看电影 Java websocket

被异地()朋友缠着要一起看电影, 翻遍许多资源并没有发现比较好的, 索性自己写一个,

思路

一起看电影要的效果是:
	1. 同时间俩人看到的画面相同
	2. 一方点暂停(播放), 一方也暂停(播放)
	3. 一方能进行同步操作(将自己的画面转为最新)
所以不需要实时地传输视频流对象,只需同步控制video组件即可

代码的问题

1. 同步操作操纵的是video.currentTime, 那么同时会触发video.onseeked事件, 
	所以无法分清楚触发onseeked事件的是同步操作还是用户主动滑动时间轴(兴许是我太菜), 
	索性去掉时间轴, 这也导致用户无法滑动时间轴进行前进和后退
2.在谷歌浏览器上回报index.html:176 Uncaught (in promise) DOMException: play()
	 failed because the user didn't interact with the document first.错误
	 它禁止了页面加载完毕时自动播放音视频
3.对于防止盗链的视频链接没有做处理, 导致的结果是在本地进行测试是正确的, 在服务器上跑则会
	 出现403 forbidden问题(我测试的视频就是......)

前端页面的参考: 来源于GitHub
后端使用Netty来实现WebSocket

获取源代码后你需要做

1. var url = "ws://127.0.0.1:9587/hello";new NettyClientServer(9587).run();
	的端口保持一致, 127.0.0.1改为后端地址, 本地就不需要改

后端源代码

<!--pom-->
<dependencies>
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.33.Final</version>
    </dependency>
</dependencies>
//NettyClientServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
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.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * @author 原来你是小幸运
 */
public class NettyClientServer {
    private int port;

    public NettyClientServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boosGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new HttpServerCodec());
                            //是以块的方式写,添加ChunkedWriteHandler处理器
                            socketChannel.pipeline().addLast(new ChunkedWriteHandler());
                            //http数据在传输中是分段的,HttpObjectAggregator作用就是将其聚合
                            //这就是为什么,当浏览器发送大量数据的时候,会发生多次http请求
                            socketChannel.pipeline().addLast(new HttpObjectAggregator(8192));
                            //WebSocketServerProtocolHandler核心的功能是将http协议升级为ws协议,保持长连接
                            socketChannel.pipeline().addLast(new WebSocketServerProtocolHandler("/hello"));
                            //添加自定义的handler,.自定义逻辑处理
                            socketChannel.pipeline().addLast(new ChatGroupServerHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new NettyClientServer(9587).run();
    }
}
//ChatGroupServerHandler.java
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;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author TextWebSocketFrame:表示一个文本帧
 */
public class ChatGroupServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    /**用于记录和管理所有客户端的channel*/
    private static ChannelGroup clients =
            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**能否更改newTime,为了防止置0的时候,被别的线程更改*/
    private static boolean canChange = true;
    private static double newTime = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        //同步消息, 只是自己同步
        if ("同步".equals(textWebSocketFrame.text())) {
            //只发给自己 使用channelHandlerContext.channel()
            System.out.println("同步消息: " + newTime);
            channelHandlerContext.channel().writeAndFlush(
                    new TextWebSocketFrame("同步" + newTime)
            );
        } else if (isNumeric(textWebSocketFrame.text())) {
        	//定时任务传递的时间
            if (canChange) {
                setCanChange(Double.parseDouble(textWebSocketFrame.text()));
            } else {
                newTime = 0;
            }
        }
        if ("重新开始播放".equals(textWebSocketFrame.text()) || textWebSocketFrame.text().startsWith("换")) {
            //设置newTime
            canChange = false;
            newTime = -1;
            if ( textWebSocketFrame.text().startsWith("换")){
            //更换视频 和 重新开始都需要将newTime置0
                clients.writeAndFlush(
                        new TextWebSocketFrame(textWebSocketFrame.text())
                );
            }else {
                clients.writeAndFlush(
                        new TextWebSocketFrame("重新开始播放")
                );
            }
            
            newTime = -1;
            System.out.println(newTime);
            //间隔一秒钟, 等待前端重新开始完毕
            Thread.sleep(1000);
            System.out.println(newTime);
            newTime = 0.1;
            canChange = true;
        } else {
            clients.writeAndFlush(
                    new TextWebSocketFrame(textWebSocketFrame.text())
            );
        }
    }

    //当用户连接的时候触发
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        clients.add(ctx.channel());
        System.out.println("有用户连接啦,id是: " + ctx.channel().id().asLongText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        clients.remove(ctx.channel());
        if (clients.size()==0){
            newTime = 0;
        }
        System.out.println("有用户断开连接啦,id是: " + ctx.channel().id().asLongText());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
    
    //判断字符串是不是double
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]+[.]{0,1}[0-9]*[dD]{0,1}");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }
    
    public void setCanChange(double msg) {
        if (newTime != -1) {
            if ((msg - newTime) > 3 && newTime <= 0.1) {
                //解决重新播放时, 线程安全问题
                return;
            }
            newTime = Math.max(msg, newTime);
        }
    }
}

前端源代码

百度网盘:
	链接:https://pan.baidu.com/s/1FHiBVOy1hlFk19YIvJ7OaA 提取码:0000
or 蓝奏云:
	https://laiyongbin.lanzous.com/iWAIQnpuzxg

在这里插入图片描述

后续可升级的操作

1. 进度条  现在进度条在reset.css里15行进行了隐藏
2. 倍速播放
3. 语音通话
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原来你是小幸运

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值