【PHP】五分钟教你编写一个实时弹幕网站

news/2024/5/10 9:14:41/文章来源:https://blog.csdn.net/weixin_34061482/article/details/86257703

由于博主是个忠实的英雄联盟粉丝,所以经常观看一些明星大神的直播。而一谈到直播,肯定会看到满屏幕飘来飘去的弹幕。那么问题来了,这些视频弹幕网站如何做到实时同步的?PHP如何开发一个类似的网站?

首先要搞定的是前端页面,最起码得有个框,让弹幕飞起来吧。一想到前台,博主头就大(毕竟我不喜欢去扣前端代码,而且做出来的东西还巨丑)。那咱们就百度一下吧,看看有什么好用的弹幕插件,现在开源的东西那么多。

经过搜索,找到了一个jQuery.danmu.js的开源项目。看了一下star的人还挺多。https://github.com/chiruom/jquery.danmu.js

于是乎,管他三七二十一,先down下来再说。

git clone https://github.com/chiruom/jquery.danmu.js.git
  • 1

大致一看目录结构如下:

图示

进入demo目录,先运行一下例子看看结果呗。

果然,点开以后出现了一个高大上的页面,略看一下功能还挺多。但是问题来了,为啥我点击开始,一点反应也木有呢。

寻找原因ing。

原来是源文件中的jQuery插件的问题。在src目录下,并没有该文件

  <script src="../src/jquery-2.1.4.min.js"></script>
  • 1

算了还是调用百度的在线jQuery插件吧

  <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
  • 1

再一刷新,不出预料,成功运行。

图示

很有意思,有木有,很激动有木有。然而重点才刚刚开始。

后端,那就先来说说弹幕的原理吧。弹幕,就相当于一个公共聊天室,都是一个客户端发送消息给服务端,服务端再将收到的消息广播给其他的客户端。

用传统的ajax轮询吗?不行,这样效率太低,想想各大火爆的直播平台都是同一时间几万人在线,几千人同时发弹幕,如果靠ajax轮询一个PHP接口的话服务器会吃不消的。且弹幕消息存储方案略显复杂,有人问为什么要存储呢?因为ajax使用的HTTP协议是无状态协议,A客户端和B客户端之间对于服务器来说没有任何标志,如果服务器要确保A客户端和B客户端分别在两次请求的时候服务器只返回这两个客户端没有获取过的弹幕消息,那么服务器端就必须使用一个缓存来标识某某客户端看过哪条弹幕消息。综上所述ajax可以实现小规模的弹幕通信方案,但是很麻烦。

好在最新的HTML5中加入了WebSocket协议,我们可以通WebSocket这种基于HTTP协议之上的即时通信协议来替代ajax这种传统的我问你答的老旧通信模式。而我们是PHPer,对于我们这种只懂PHP的人该如何编写WebSocket服务端呢?好在我们又得知PHP有一个Swoole扩展,我们在PHP语言中使用它可以很方便的构建一个WebSocket服务端。

关于Swoole,下面这段是其官网上的话:

PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。
Swoole可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用PHP+Swoole作为网络通信框架,可以使企业IT研发团队的效率大大提升,更加专注于开发创新产品。

跟详细的东西请自行参考官网文档。这里就不在废话了。

http://wiki.swoole.com/wiki/page/479.html

还有一个问题需要解决,那就是,这个jquery.danmu.js是基于弹幕运行时间的一个插件。那又要如何做到实时呢。开始博主想的是在服务器端规定一个时间(即其连接时间),当有客户端连接时,返回服务器的当前时间戳,然后以此为依据开始计时。但是遇到的问题如下:

  • 该弹幕插件是按十分之秒计时制度。
  • 各浏览器上js的定时器的运行时间略有差异。
  • 时间不能完全同步。

好吧,博主走弯路子了(没做过这方面的东西,缺乏经验)。这个时候,就需要转变一种思路了。

websocket是实时通信的,哎,那所有客户端的时间,不一致就不一致吧,弹幕发的时间根据各个客户端的为准呗,都以当前各个客户端的时间来发,websocket只传递不包含时间的数据(好吧有点绕,我自己都感觉说饶了),咱们直接来上代码吧。

index.html

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>弹幕made by diligentyang</title> <style> body { font-family: "Microsoft YaHei" ! important; font-color:#222; } pre { line-height: 2em; font-family: "Microsoft YaHei" ! important; } h4 { line-height: 2em; } #danmuarea { position: relative; background: #222; width:800px; height: 445px; margin-left: auto; margin-right: auto; } .center { text-align: center; } .ctr { font-size: 1em; line-height: 2em; } </style> <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script src="../dist/jquery.danmu.min.js"></script> </head> <body class="center"> Demo<br><br> <!--黑背景和弹幕区--> <div id="danmuarea"> <div id="danmu" > </div> </div> <!--控制区--> <div class="ctr" > <button type="button" onclick="pauser()">弹幕暂停</button> &nbsp;&nbsp;&nbsp;&nbsp; <button type="button" onclick="resumer() ">弹幕继续</button>&nbsp;&nbsp;&nbsp;&nbsp; 显示弹幕:<input type='checkbox' checked='checked' id='ishide' value='is' onchange='changehide()'> &nbsp;&nbsp;&nbsp;&nbsp; 弹幕透明度: <input type="range" name="op" id="op" onchange="op()" value="100"> <br> 当前弹幕运行时间(秒):<span id="time"></span>&nbsp;&nbsp; <!--设置当前弹幕时间(秒): <input type="text" id="set_time" max=20 /> <button type="button" onclick="settime()">设置</button>--> <br> 发弹幕: <select name="color" id="color" > <option value="white">白色</option> <option value="red">红色</option> <option value="green">绿色</option> <option value="blue">蓝色</option> <option value="yellow">黄色</option> </select> <select name="size" id="text_size" > <option value="1">大文字</option> <option value="0">小文字</option> </select> <select name="position" id="position" > <option value="0">滚动</option> <option value="1">顶端</option> <option value="2">底端</option> </select> <input type="textarea" id="text" max=300 /> <button type="button" onclick="send()">发送</button> </div> <script> //WebSocket var wsServer = 'ws://123.206.61.229:9505'; var websocket= new WebSocket(wsServer); websocket.onopen = function (evt) { console.log("Connected to WebSocket server."); /*websocket.send("gaga");*/ //连上之后就打开弹幕 $('#danmu').danmu('danmuResume'); }; websocket.onclose = function (evt) { console.log("Disconnected"); }; websocket.onmessage = function (evt) { console.log('Retrieved data from server: ' + evt.data); var time = $('#danmu').data("nowTime")+1; var text_obj= evt.data +',"time":'+time+'}';//获取加上当前时间 console.log(text_obj); var new_obj=eval('('+text_obj+')'); $('#danmu').danmu("addDanmu",new_obj);//添加弹幕 }; websocket.onerror = function (evt, e) { console.log('Error occured: ' + evt.data); }; //初始化 $("#danmu").danmu({ left:0, top:0, height:"100%", width:"100%", speed:20000, opacity:1, font_size_small:16, font_size_big:24, top_botton_danmu_time:6000 }); //一个定时器,监视弹幕时间并更新到页面上 function timedCount(){ $("#time").text($('#danmu').data("nowTime")); t=setTimeout("timedCount()",50) } timedCount(); function starter(){ $('#danmu').danmu('danmuStart'); } function pauser(){ $('#danmu').danmu('danmuPause'); } function resumer(){ $('#danmu').danmu('danmuResume'); } function stoper(){ $('#danmu').danmu('danmuStop'); } function getime(){ alert($('#danmu').data("nowTime")); } function getpaused(){ alert($('#danmu').data("paused")); } //发送弹幕,使用了文档README.md第7节中推荐的方法 function send(){ var text = document.getElementById('text').value; var color = document.getElementById('color').value; var position = document.getElementById('position').value; //var time = $('#danmu').data("nowTime")+1; var size =document.getElementById('text_size').value; //var text_obj='{ "text":"'+text+'","color":"'+color+'","size":"'+size+'","position":"'+position+'","time":'+time+'}'; //为了处理简单,方便后续加time,和isnew,就先酱紫发一半吧。 //注:time为弹幕出来的时间,isnew为是否加边框,自己发的弹幕,常理上来说是有边框的。 var text_obj='{ "text":"'+text+'","color":"'+color+'","size":"'+size+'","position":"'+position+'"'; //利用websocket发送 websocket.send(text_obj); //清空相应的内容 document.getElementById('text').value=''; } //调整透明度函数 function op(){ var op=document.getElementById('op').value; $('#danmu').danmu("setOpacity",op/100); } //调隐藏 显示 function changehide() { var op = document.getElementById('op').value; op = op / 100; if (document.getElementById("ishide").checked) { $("#danmu").danmu("setOpacity",1) } else { $("#danmu").danmu("setOpacity",0) } } //设置弹幕时间 function settime(){ var t=document.getElementById("set_time").value; t=parseInt(t) $('#danmu').danmu("setTime",t); } </script> </body> </html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192

上述代码需要注意的是websocket的建立和接收,以及send方法中对弹幕的处理。

ws_server.php

<?php 
//创建websocket服务器对象,监听0.0.0.0:9505端口
$ws = new swoole_websocket_server("0.0.0.0", 9505); //监听WebSocket连接打开事件 $ws->on('open', function ($ws, $request) { //var_dump($request->fd, $request->get, $request->server); //相当于记录一个日志吧,有连接时间和连接ip echo $request->fd.'-----time:'.date("Y-m-d H:i:s",$request->server['request_time']).'--IP--'.$request->server['remote_addr'].'-----'; }); //监听WebSocket消息事件 $ws->on('message', function ($ws, $frame) { //记录收到的消息,可以写到日志文件中 echo "Message: {$frame->data}\n"; //遍历所有连接,循环广播 foreach($ws->connections as $fd){ //如果是某个客户端,自己发的则加上isnew属性,否则不加 if($frame->fd == $fd){ $ws->push($frame->fd, $frame->data.',"isnew":""'); }else{ $ws->push($fd, "{$frame->data}"); } } }); //监听WebSocket连接关闭事件 $ws->on('close', function ($ws, $fd) { echo "client-{$fd} is closed\n"; }); $ws->start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

运行方法:

输入php ws_server.php 先启动服务器端的websocket。如果要后台运行,且不随用户终端关闭而断开,需要创建一个log.txt用于存取上述输出的东西,然后输入nohup php ws_server.php > log.txt & 即可。

然后,

这里写图片描述

注,如果要用此项目,需要自行修改自己的服务器ip地址。只需要修改var wsServer = 'ws://123.206.61.229:9505'; 处即可,后台代码不需要做任何处理。

github地址:https://github.com/diligentyang/danmu

原文博主:http://blog.csdn.net/qq_28602957

如需转载请明示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_764144.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

django搭建一个小型的服务器运维网站

前言 不管是运维还是开发抑或是测试&#xff0c;工作中不免会和Linux服务器打交道&#xff0c;常见的操作譬如&#xff1a;查看CPU或内存状态、查看和修改服务器时间、查看或者修改服务器配置文件、实时查看或回看系统的日志、重启服务器中某个进程等。如果不在服务器旁&#x…

请求https前缀的网站验证SSL证书的解决方案之一

from requests.packages.urllib3.exceptions import InsecureRequestWarning# 禁用安全请求警告requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 转载于:https://www.cnblogs.com/weiwei-python/p/10118999.html

基于 vue+element ui 的cdn网站(多页面,都是各种demo)

前言&#xff1a;这个网站持续更新中...&#xff0c;有网上预览&#xff0c;github上也有源码&#xff0c;喜欢记得star哦&#xff0c;欢迎留言讨论。 网站地址&#xff1a;我的个人vueelement ui demo网站 github地址&#xff1a;yuleGH github (喜欢记得star哦) 附&#xff…

Zabbix监控Low level discovery实时监控网站URL状态

今天我们来聊一聊Low level discovery这个功能&#xff0c;我们为什么要用到loe level discovery这个功能呢&#xff1f; 很多时候&#xff0c;在使用zabbix监控一些东西&#xff0c;需要对类似于Itens进行添加&#xff0c;这些items具有一些共同特性&#xff0c; 如果说某些特…

P9架构师讲解从单机至亿级流量大型网站系统架构的演进过程

阶段一、单机构建网站 网站的初期&#xff0c;我们经常会在单机上跑我们所有的程序和软件。此时我们使用一个容器&#xff0c;如tomcat、jetty、jboos&#xff0c;然后直接使用JSP/servlet技术&#xff0c;或者使用一些开源的框架如mavenspringstructhibernate、mavenspringspr…

让你高效工作的7大工具类网站

一 Smallpdf——最好的PDF在线无损转换工具 对于专业的 PDF 工作者或 PDF 的重度使用者来说&#xff0c;目前比较流行的几款PDF处理工具&#xff0c;包括著名的Adobe acrobat原版工具以及国内流行的福昕PDF处理套件。但是对于普通的学生党和上班族来说&#xff0c;平时都是用PD…

网站如何防止sql注入攻击的解决办法

首先我们来了解下什么是SQL注入&#xff0c;SQL注入简单来讲就是将一些非法参数插入到网站数据库中去&#xff0c;执行一些sql命令&#xff0c;比如查询数据库的账号密码&#xff0c;数据库的版本&#xff0c;数据库服务器的IP等等的一些操作&#xff0c;sql注入是目前网站漏洞…

从团购网的漏洞看网站安全性问题

自从9月份在同事推荐下在某团购网买了一份火锅的套餐后&#xff0c;就迷上了&#xff0c;几乎每天必去浏览一遍&#xff0c;看看有什么又便宜又好吃的。元旦期间当然也不例外&#xff0c;1号那天上午&#xff0c;看到了XXX团购网的“VIP会员0元领红包”活动&#xff0c;0元&…

社会工程学攻击之网站钓鱼

前言网络给了我们方便的同时&#xff0c;但也并不总是那么美好。还记得邀请苍蝇到它的客厅做客的蜘蛛吗&#xff1f;还记得帮助蝎子渡河的乌龟吗&#xff1f;这些故事都包含了猎物的天真和猎手的肮脏。互联网也是如此&#xff0c;其中中充斥着诱惑的陷阱、阴暗的角落、恶意的行…

[技术分享] 20110803,Web 代理客户端通过 TMG ISA 不能访问新浪微博等网站

Web 代理客户端通过 TMG/ISA 不能访问新浪微博等网站&#xff0c;Web 客户端返回错误&#xff1a;" Error Code: 502 Proxy Error. The request is not supported (50)”。 这类问题一般是由于网站回复的数据是压缩的所导致。遇到此类问题&#xff0c;您可以尝试以下方法…

网站建设流程图

网站建设流程 网站建设包括域名注册查询、网站策划、网页设计、网站功能、网站优化技术、网站内容整理、网站推广、网站评估、网站运营、网站整体优化、网站改版等&#xff0c;这里用一张图概括了网站建设的基本流程&#xff0c;需要的朋友可以参考下&#xff0c; 常见的前端产…

收藏网站制作常用经典css.div.布局.设计实例打包下载2

点击下载&#xff1a;多功能导航Demo.rar 点击下载&#xff1a;发一个选项卡动态增删的效果.rar 点击下载&#xff1a;仿yahoo的特色服务内容切换效果.rar 点击下载&#xff1a;给图片加上边框[1].(仿胶卷).rar 点击下载&#xff1a;国外网站的一个效果.rar 点击下载&#xff1…

协助用户搜寻您的网站 { 创建一个OpenSearch }

导言 现代浏览器其中一个最美好的特点&#xff0c; Microsoft Internet Explorer 和 Mozilla Firefox 的浏览器右上角有一个搜索栏在。有了这个工具&#xff0c;您可以很快的进行搜索任何数目的网站&#xff0c;无须首次访问他们的搜索页。 如何搜索您的网站是存在一个XM…

大型网站技术架构(六)网站的伸缩性架构

2019独角兽企业重金招聘Python工程师标准>>> 网站系统的伸缩性架构最重要的技术手段就是使用服务器集群功能&#xff0c;通过不断地向集群中添加服务器来增强整个集群的处理能力。“伸”即网站的规模和服务器的规模总是在不断扩大。 1、网站架构的伸缩性设计 网站的…

《Flash建站技术》系列6-LoadVars数据提交与表单处理

1.LoadVars vs getURLsend()getURL有很多的限制&#xff0c;LoadVars则弥补了这一点Flash时间轴脚本:<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />System.useCodepagetrue;signBtn.onReleasefunction(){var formVars:LoadVar…

C++在线编程网站

1、推荐 https://wandbox.org/ 2、https://www.tutorialspoint.com/compile_cpp_online.php 3、https://www.anycodes.cn/#/OnlineCode 4、https://tool.lu/coderunner/ 测试&#xff1a; #include <iostream>using namespace std;int main() {cout<<"input s…

我记录网站综合系统 -- 技术原理解析[11:ActionProcessor流程-wojilu核心]

这篇文章不是专门讲数据缓存的&#xff0c;但是这里有大量缓存内容&#xff0c;wojilu的缓存系统近期升级了&#xff0c;可能我的理解不是最正确的。 改日将会写专门的缓存文章。 源代码位置&#xff1a;\Web\Mvc\Processors&#xff1a;ActionProcessor.cs Config -> wojil…

大型网站的高可用分析

本文主要分析网站的高可用性&#xff0c;从应用需求、用户角度展开分析。 1.1 高可用性 “高可用性”(High Availability) 通常用来描述一个系统&#xff0c;经过特殊设计&#xff0c;减少停止服务的时间&#xff0c;从而使其服务保持高度的可使用性。 计算机系统的可靠性用平均…

网站安全技术—预防脚本攻击

阅读目录 一&#xff1a;脚本攻击 二&#xff1a;模拟脚本攻击 三&#xff1a;运行效果 四&#xff1a;预防脚本攻击的主要方法 一&#xff1a;脚本攻击 脚本攻击是指将恶意的字符插入到网页中来&#xff0c;浏览器无法验证这些插入的字符&#xff0c;并且会将它们作为网页的一…

We7网站群的共享机制

2019独角兽企业重金招聘Python工程师标准>>> 1.独立部署共享服务器 独立布署共享服务器 共享服务器作为一个独立的服务&#xff0c;允许布署在不同的物理服务器上&#xff0c;从而满足更大负载与更佳性能的要求。 技术支撑&#xff1a;信息打包技术 信息通过数据…