php:使用socket函数创建WebSocket服务
一、前言
闲来无事,最近捣鼓了下websocket,但是不希望安装第三方类库,所以打算用socket基础函数创建个服务。
二、构建websocket服务端
<?phpclass SocketService
{// 默认的监听地址和端口private $address = '0.0.0.0';private $port = 8083;private $_sockets;/*** 构造函数,初始化地址和端口** @param string $address 监听的地址,默认 '0.0.0.0'* @param int $port 监听的端口,默认 8083*/public function __construct($address = '', $port = ''){if (!empty($address)) {$this->address = $address;}if (!empty($port)) {$this->port = $port;}}/*** 初始化服务,创建套接字并开始监听*/public function service(){// 获取 TCP 协议号$tcp = getprotobyname("tcp");// 创建 TCP 套接字$sock = socket_create(AF_INET, SOCK_STREAM, $tcp);// 设置套接字选项,允许地址重用socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);// 如果创建失败,抛出异常if ($sock < 0) {throw new Exception("failed to create socket: " . socket_strerror($sock) . "\n");}// 绑定地址和端口socket_bind($sock, $this->address, $this->port);// 开始监听socket_listen($sock, $this->port);echo "listen on $this->address $this->port ... \n";// 保存套接字$this->_sockets = $sock;}/*** 运行 WebSocket 服务* * 该方法会进入一个无限循环,处理所有客户端连接*/public function run(){// 启动服务$this->service();// 存储客户端套接字$clients[] = $this->_sockets;// 无限循环监听客户端连接while (true) {$changes = $clients;$write = NULL;$except = NULL;// 监听可读的套接字socket_select($changes, $write, $except, NULL);// 处理每个连接的套接字foreach ($changes as $key => $_sock) {// 判断是否是新连接if ($this->_sockets == $_sock) {// 接受新连接if (($newClient = socket_accept($_sock)) === false) {die('failed to accept socket: ' . socket_strerror($_sock) . "\n");}// 读取客户端发送的数据$line = trim(socket_read($newClient, 1024));// 执行 WebSocket 握手$this->handshaking($newClient, $line);// 获取客户端 IPsocket_getpeername($newClient, $ip);// 将新连接的客户端保存$clients[$ip] = $newClient;// 输出客户端 IP 和消息echo "Client ip:{$ip} \n";echo "Client msg:{$line} \n";} else {// 处理已连接的客户端消息socket_recv($_sock, $buffer, 2048, 0);// 解码接收到的消息$msg = $this->message($buffer);// 在这里处理业务逻辑echo "{$key} client msg: {$msg}\n";// 等待用户输入响应fwrite(STDOUT, 'Please input a argument:');$response = trim(fgets(STDIN));// 发送响应给客户端$this->send($_sock, $response);echo "{$key} response to Client: {$response}\n";}}}}/*** WebSocket 握手处理* * @param resource $newClient 新连接的客户端套接字* @param string $line 接收到的握手请求头* @return int 返回写入的字节数*/public function handshaking($newClient, $line){$headers = array();$lines = preg_split("/\r\n/", $line);// 解析请求头foreach ($lines as $line) {$line = chop($line);if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {$headers[$matches[1]] = $matches[2];}}// 获取客户端的 Sec-WebSocket-Key$secKey = $headers['Sec-WebSocket-Key'];// 生成 Sec-WebSocket-Accept$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));// 构造握手响应$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" ."Upgrade: websocket\r\n" ."Connection: Upgrade\r\n" ."WebSocket-Origin: $this->address\r\n" ."WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n" ."Sec-WebSocket-Accept:$secAccept\r\n\r\n";// 发送握手响应return socket_write($newClient, $upgrade, strlen($upgrade));}/*** 解析接收到的 WebSocket 消息* * @param string $buffer 接收到的 WebSocket 数据* @return string 解码后的消息*/public function message($buffer){$len = $masks = $data = $decoded = null;$len = ord($buffer[1]) & 127;// 根据消息长度处理掩码和数据if ($len === 126) {$masks = substr($buffer, 4, 4);$data = substr($buffer, 8);} else if ($len === 127) {$masks = substr($buffer, 10, 4);$data = substr($buffer, 14);} else {$masks = substr($buffer, 2, 4);$data = substr($buffer, 6);}// 解码消息for ($index = 0; $index < strlen($data); $index++) {$decoded .= $data[$index] ^ $masks[$index % 4];}return $decoded;}/*** 发送 WebSocket 消息给客户端* * @param resource $newClient 新连接的客户端套接字* @param string $msg 要发送的消息* @return int 返回写入的字节数*/public function send($newClient, $msg){// 封装消息为 WebSocket 数据帧$msg = $this->frame($msg);// 发送数据帧socket_write($newClient, $msg, strlen($msg));}/*** 将消息封装为 WebSocket 数据帧* * @param string $s 要封装的消息* @return string 封装后的 WebSocket 数据帧*/public function frame($s){$a = str_split($s, 125);if (count($a) == 1) {return "\x81" . chr(strlen($a[0])) . $a[0];}$ns = "";foreach ($a as $o) {$ns .= "\x81" . chr(strlen($o)) . $o;}return $ns;}/*** 关闭 WebSocket 连接* * @return bool 返回是否成功关闭*/public function close(){return socket_close($this->_sockets);}
}// 创建并运行 WebSocket 服务
$sock = new SocketService();
$sock->run();
三、构建websocket客户端
接下来写个前端页面,测试服务端是否正常,代码如下:
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"><title>WebSocket</title></head><body><input id="text" value=""><input type="submit" value="发送" onclick="start()"><input type="submit" value="关闭" onclick="close()"><div id="msg"></div><script>/*** WebSocket的连接状态代码:* 0: 未连接* 1: 已连接,可以通讯* 2: 正在关闭* 3: 已关闭或无法打开*/// 创建WebSocket实例var webSocket = new WebSocket("ws://127.0.0.1:8083");// 监听错误事件webSocket.onerror = function (event) {onError(event);};// 监听连接成功事件webSocket.onopen = function (event) {onOpen(event);};// 监听消息事件webSocket.onmessage = function (event) {onMessage(event);};// 监听关闭事件webSocket.onclose = function (event) {onClose(event);};// 错误处理函数function onError(event) {document.getElementById("msg").innerHTML = "<p>连接错误</p>";console.log("错误: " + event.data);}// 连接成功后的回调函数function onOpen(event) {console.log("连接成功: " + sockState());document.getElementById("msg").innerHTML = "<p>已连接到服务</p>";}// 处理接收到的消息function onMessage(event) {console.log("接收到消息");document.getElementById("msg").innerHTML += "<p>响应: " + event.data + "</p>";}// 连接关闭后的回调函数function onClose(event) {document.getElementById("msg").innerHTML = "<p>连接已关闭</p>";console.log("关闭连接: " + sockState());webSocket.close();}// 获取WebSocket连接状态function sockState() {var status = ['未连接', '已连接,可以通讯', '正在关闭', '已关闭或无法打开'];return status[webSocket.readyState];}// 发送消息函数function start(event) {console.log(webSocket);var msg = document.getElementById('text').value;document.getElementById('text').value = ''; // 清空输入框console.log("发送消息: " + sockState());console.log("消息内容: " + msg);webSocket.send("msg=" + msg); // 发送消息document.getElementById("msg").innerHTML += "<p>请求: " + msg + "</p>";}// 关闭连接function close(event) {webSocket.close();}</script></body>
</html>
四、测试结果
出现已连接到服务,代表成功连接。
相关文章:
php:使用socket函数创建WebSocket服务
一、前言 闲来无事,最近捣鼓了下websocket,但是不希望安装第三方类库,所以打算用socket基础函数创建个服务。 二、构建websocket服务端 <?phpclass SocketService {// 默认的监听地址和端口private $address 0.0.0.0;private $port 8…...
ubuntu20.04中编译安装gcc 9.2.0
ubuntu20.04中编译安装gcc 9.2.0,步骤如下: #install compile dependence libraries 1:$ sudo apt install libgmp-dev libisl-dev libmpc-dev libmpfr-dev # install gcc 9.2.0 # download source code 2:$ wget http://ftp.gnu.org/gn…...
ssm158企业人事管理系统的设计与实现+jsp(论文+源码)_kaic
设计题目:企业人事管理系统的设计与实现 摘 要 进入信息时代以来,很多数据都需要配套软件协助处理,这样可以解决传统方式带来的管理困扰。比如耗时长,成本高,维护数据困难,数据易丢失等缺点。本次使用数据…...
OceanBase数据库产品与工具介绍
OceanBase:蚂蚁集团自主研发的分布式关系数据库 1、什么是 OceanBase? OceanBase 是由蚂蚁集团完全自主研发的企业级分布式关系数据库,始创于 2010 年。它具有以下核心特点: 数据强一致性:在分布式架构下确保数据强…...
PakePlus将任何网页免费打包为mac/windows跨平台软件程序教程,只需要一个Github token就可以了
PakePlus是一个开源免费的软件,支持 Mac、Windows 和 Linux,很快也将支持 Android 和 iOS。无需在本地安装复杂的依赖环境,只需要一个Github Token就可以了。查看 README 以获取热门包和自定义开发信息。欢迎在讨论区分享你的建议。开源地址&…...
Element UI 组件库详解【Vue】
文章目录 一、引言二、安装并使用1. 安装2. 使用 三、常见组件说明1. 基础组件2. 布局组件3. 布局容器4. 选择框组件5. 输入框组件6. 下拉框组件7. 日期选择器8. 上传组件9. 表单组件10. 警告组件11. 提示组件12. 表格组件 一、引言 官方网站,element.eleme.cn El…...
Android kotlin之配置kapt编译器插件
配置项目目录下的gradle/libs.versions.toml文件,添加kapt配置项: 在模块目录下build.gradle.kt中增加 plugins {alias(libs.plugins.android.application)alias(libs.plugins.jetbrains.kotlin.android)// 增加该行alias(libs.plugins.jetbrains.kotl…...
基于 MUSA 的大语言模型推理和服务框架vLLM
1. 引言 vLLM是一个高性能且内存高效的大语言模型推理和服务框架,也是当前业界使用范围最广的大模型推理框架,截至目前github star数28.4k。该框架性能优秀,而且部署容易,使用CUDA/ROCm提供GPU加速能力。但vLLM目前不支持使用摩…...
k8s 集群安装
安装rockylinux https://www.jianshu.com/p/a5fe20318b8e https://www.cnblogs.com/haoee/p/18290506 配置VirtualBox双网卡 https://www.cnblogs.com/ShineLeBlog/p/17580311.html https://zhuanlan.zhihu.com/p/341328334 https://blog.csdn.net/qq_36544785/article/deta…...
Linux-服务器辨别实体机OR虚拟机
快速命令 ① lscpu(查看CPU信息) lscpu Hypervisor vendor: KVM (超管理器厂商:KVM。虚拟机:KVM / VMware) Virtualization: VT-x(虚拟化:VT-x。实体机:VT-x / AMD-V) ② systemd-detect-virt(检测当前系…...
堤防安全监测系统方案
一、背景情况 堤防是开发利用水资源和防治水灾害的重要工程措施之一,对防洪、供水、生态、发电、航运等至关重要。我国现有堤防9.8万多座,其中大中型堤防4700多座、小型堤防9.4万座,80%以上修建于上世纪50至70年代。由于堤防管护力量薄弱&am…...
Leetcode 求根节点到叶节点数字之和
使用深度优先搜索 DFS 来做 我提供的代码使用的是 深度优先搜索(DFS,Depth-First Search) 算法。以下是具体的算法思想和实现步骤的解释: 算法思想 树的路径代表数字: 树中每条从根节点到叶子节点的路径可以看作一个整…...
Git分布式版本控制系统
1. 版本控制系统 版本控制系统主要分为两类:集中式、分布式。 集中式:SVM(工作流程:所有的文件都保存在中央服务器上,每个电脑上只保存了一个副本,当需要修改时,先下载中央服务器上的最新版本文件…...
数据库审计工具--Yearning 3.1.9普民的使用指南
1 页面登录 登录地址:18000 (不要勾选LDAP) 2 修改用户密码 3 DML/DDL工单申请及审批 工单申请 根据需要选择【DML/DDL/查询】中的一种进行工单申请 填写工单信息提交SQL检测报错修改sql语句重新进行SQL检测,如检测失败可以进行SQL美化后…...
VMware Workstation 17.6.1
概述 目前 VMware Workstation Pro 发布了最新版 v17.6.1: 本月11号官宣:针对所有人免费提供,包括商业、教育和个人用户。 使用说明 软件安装 获取安装包后,双击默认安装即可: 一路单击下一步按钮: 等待…...
unity 中 RectTransform 的常用几个属性
RectTransform rectTransform this.GetComponent<RectTransform>(); rectTransform this.transform as RectTransform; Vector3 vector1 rectTransform.position; //自身轴心点相对于锚点的位置(编译器显示的pos) …...
23种设计模式-模板方法(Template Method)设计模式
文章目录 一.什么是模板方法模式?二.模板方法模式的特点三.模板方法模式的结构四.模板方法模式的应用场景五.模板方法模式的优缺点六.模板方法模式的C实现七.模板方法模式的JAVA实现八.代码解析九.总结 类图: 模板方法设计模式类图 一.什么是模板方法模…...
网络安全之国际主流网络安全架构模型
目前,国际主流的网络安全架构模型主要有: ● 信息技术咨询公司Gartner的ASA(Adaptive Security Architecture自适应安全架构) ● 美国政府资助的非营利研究机构MITRE的ATT&CK(Adversarial Tactics Techniques &…...
SpringCloud多机部署,负载均衡-LoadBalance
一.负载均衡 1.1问题描述 //根据应用名称获取服务列表 List<ServiceInstance> instancesdiscoveryClient.getInstances("product-service"); //一个微服务可能有多个实例,获取第一个 EurekaServiceInstance instance(EurekaServiceInstance)insta…...
前端开发调试之 PC 端调试学习笔记
一、引言 在前端开发过程中,调试是至关重要的一个环节。它能帮助我们快速定位代码中的问题,无论是页面布局错乱、交互效果异常还是性能不佳等情况,通过有效的调试手段都可以找到根源并进行修复。而在 PC 端进行调试有着其特定的方法和技巧&am…...
视频流媒体播放器EasyPlayer.js无插件直播流媒体音视频播放器Android端webview全屏调用无效问题
流媒体播放器的核心技术与发展趋势正在不断推动着行业的变革。未来,随着技术的不断进步和应用场景的不断拓展,流媒体播放器将为用户带来更加便捷、高效、个性化的观看体验。同时,流媒体播放器也会成为数字娱乐产业的重要组成部分,…...
使用 cnpm 安装 Electron,才是正确快速的方法
当然,下面是总结的几种安装 Electron 的方法,包括使用 npm 和 cnpm,以及一些常见的问题解决技巧。 ### 1. 使用 npm 安装 Electron #### 步骤 1: 初始化项目 在你的项目目录中初始化一个新的 Node.js 项目: bash npm init -y …...
pytest日志总结
pytest日志分为两类: 一、终端(控制台)打印的日志 1、指定-s,脚本中print打印出的信息会显示在终端; 2、pytest打印的summary信息,这部分是pytest 的默认输出(例如测试结果PASSED, FAILED, S…...
【Ubuntu24.04】使用服务器
目录 0 背景1 将文件传送到服务器1.1 主机上的虚拟机1.2 另一台独立的计算机(包括上面的虚拟机)1.3 远程文件传输1.3.1 scp2 操作服务器2.1 ssh2.2 Termius2.2.1 下载2.2.2 安装2.2.3 使用2.2.4 小结3 总结0 背景 你是一个开发者,有自己的测试环境:一个Linux服务器(假设是…...
Spark 之 Aggregate
Aggregate 参考链接: https://github.com/PZXWHU/SparkSQL-Kernel-Profiling 完整的聚合查询的关键字包括 group by、 cube、 grouping sets 和 rollup 4 种 。 分组语句 group by 后面可以是一个或多个分组表达式( groupingExpressions )…...
ubuntu没有了有线网络如何修复
今天打开ubuntu之后发现有线网络连接没有了,如下图,此时是修复好之后的,“有线”部分存在,出现问题时是不存在的 此时只需要修改NetworkManager.conf配置文件,将managedfalse更改为managedtrue,保存退出就可以了 sudo…...
2411rust,异步函数
原文 Rust异步工作组很高兴地宣布,在实现在特征中使用异步 fn的目标方面取得了重大进度.将在下周发布稳定的Rust1.75版,会包括特征中支持impl Trait注解和async fn. 稳定化 自从RFC#1522在Rust1.26中稳定下来以来,Rust就允许用户按函数的返回类型(一般叫"RPIT")编…...
解决IDEA报包不存在,但实际存在的问题
前言 最近在把一个亿老项目交割给同事,同事在导入项目运行时遇到IDEA报包不存在,但实际存在的问题,最终通过以下方式解决 现象 在IDEA里启动运行项目,报某个类有问题,引入的包不存在。 点击这个引入的包,可…...
解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题
解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题 Chapter1 解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题Chapter2 windows主机和ubuntu互传文件的4种方法 博文链接:Chapter3 安装好VMware tools后无法复制粘贴文本、拖拽文件的解决办法 Chapter1 解…...
SpringBoot与MongoDB深度整合及应用案例
SpringBoot与MongoDB深度整合及应用案例 在当今快速发展的软件开发领域,NoSQL数据库因其灵活性和可扩展性而变得越来越流行。MongoDB,作为一款领先的NoSQL数据库,以其文档导向的存储模型和强大的查询能力脱颖而出。本文将为您提供一个全方位…...
计算机网络(12)介质访问控制
ok通过前面的学习我们已经知道数据链路层提供的服务有帧封装,物理地址,流量控制,错误控制,访问控制。今天就来记录最后的访问控制。 介质访问控制 (MAC)详解 介质访问控制(Media Access Control,简称 MAC&…...
Axios 响应拦截器与未登录状态的统一处理
目录 前言1. 响应拦截器的作用与应用场景1.1 什么是响应拦截器?1.2 响应拦截器的应用场景 2. 代码解读:响应拦截器中的未登录处理2.1 原始代码分析 3. 完善未登录处理逻辑3.1 未登录状态的用户体验优化3.2 改进后的代码实现 4. 实践中的场景4.1 登录态的…...
【MySQL系列】深入理解MySQL中的存储、排序字符集
前言 在创建数据库时,我们经常会需要填写数据库的所用字符集、排序规则,字符集和排序规则是两个非常重要的概念,它们决定了数据库如何存储和比较字符串数据。在 MySQL 中,常用的存储字符集有 utf8、utf8mb4,而排序字符…...
【ARM Coresight OpenOCD 系列 5.1 -- OpenOCD 无法识别CPUID 问题: xxx is unrecognized】
请阅读【嵌入式开发学习必备专栏】 文章目录 OpenOCD 无法识别CPUID 问题ARM CPUIDCPUID 特性CPUID 寄存器字段OpenOCD 无法识别CPUID 问题 在使用OpenOCD 进行CPU debug的过程中有时会报出 无法识别CPUID的问题,本文将会介绍如何解决这个问题。首先我们来学习下什么是CPUID,…...
YOLOv11融合针对小目标FFCA-YOPLO中的FEM模块及相关改进思路
YOLOv11v10v8使用教程: YOLOv11入门到入土使用教程 YOLOv11改进汇总贴:YOLOv11及自研模型更新汇总 《FFCA-YOLO for Small Object Detection in Remote Sensing Images》 一、 模块介绍 论文链接:https://ieeexplore.ieee.org/document/10…...
【Docker容器】一、一文了解docker
1、什么是docker? 1.1 docker概念 Docker是一种容器化平台,通过使用容器技术,Docker允许开发人员将应用程序和其依赖项打包到一个独立的、可移植的容器中。每个容器具有自己的文件系统、环境变量和资源隔离,从而使应用程序可以在…...
【团购核销】抖音生活服务商家应用快速接入①——基础工作
文章目录 一、前言二、抖音开放平台(服务商平台)三、认证服务能力四、第三方生活服务商家应用五、APPID和AppSecret六、申请接口权限七、开发配置八、参考 一、前言 目的:将抖音团购核销的功能集成到我们自己开发的App和小程序中 【团购核销】…...
MyBatis实践:提高持久化层数据处理效率
一、MyBatis简介: 1.简介:https://mybatis.org/mybatis-3/zh/index.html?spmwolai.workspace.0.0.66162306mX2SuC MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下ÿ…...
如何理解Lua 使用虚拟堆栈
虚拟堆栈的基本概念 Lua使用虚拟堆栈来实现Lua和C(或其他宿主语言)之间的交互。这个虚拟堆栈是一个数据结构,用于存储Lua的值,如数字、字符串、表、函数等。它在Lua状态机(lua_State)内部维护,为…...
UE5 腿部IK 解决方案 footplacement
UE5系列文章目录 文章目录 UE5系列文章目录前言一、FootPlacement 是什么?二、具体实现 前言 在Unreal Engine 5 (UE5) 中,腿部IK(Inverse Kinematics,逆向运动学)是一个重要的动画技术,用于实现角色脚部准…...
SMMU软件指南之概述
安全之安全(security)博客目录导读 目录 1. 概述 1.1 开始之前 2. SMMU 的功能 1. 概述 本博客描述了 ARM 系统内存管理单元(SMMUv3)的基本操作及其使用案例,包括: • SMMU 架构概念、术语和操作 • 与 SMMU 功能相关的系统级考虑因素 • 典型 SMMU 使用案例的知识 1…...
Vue_Router权限控制:不同角色显示不同路由
写在前面 在Vue中,Router是一个官方提供的用于处理应用程序路由的插件。它允许我们创建单页应用程序(SPA),其中不同的页面和组件可以通过URL进行导航和展示。使我们可以轻松地创SPA,并实现可复用和可组合的组件…...
机器学习4
九、线性回归 1、概念 假设存在多个点,需要使用一条线来保障尽量拟合这些点,寻找这条线的过程就叫回归。 机器学习中一种有监督学习的算法,回归问题主要关注的是因变量(需要预测的值)和一个或多个数值型的自变量(预测变量)之间的关系。 2、损失…...
Linux中系统的延迟任务及定时任务
一、延时任务 at 命令,即用即消 如 at 11:30 rm -rf /mnt/* ctrld运行 (过一秒即可执行) -v 使用较明显的时间格式,列出at调度中的任务列表 -l 可列出目前系统上面的所有该用户的at调度 -c 可以列出后面接…...
从Stream的 toList() 和 collect(Collectors.toList()) 方法看Java的不可变流
环境 JDK 21Windows 11 专业版IntelliJ IDEA 2024.1.6 背景 在使用Java的Stream的时候,常常会把流收集为List。 假设有List list1 如下: var list1 List.of("aaa", "bbbbbb", "cccc", "d", "eeeee&qu…...
centos7.9单机版安装K8s
1.安装docker [rootlocalhost ~]# hostnamectl set-hostname master [rootlocalhost ~]# bash [rootmaster ~]# mv /etc/yum.repos.d/* /home [rootmaster ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo [rootmaster ~]# cu…...
Notepad++--在开头快速添加行号
原文网址:Notepad--在开头快速添加行号_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Notepad怎样在开头快速添加行号。 需求 原文件 想要的效果 方法 1.添加点号 Alt鼠标左键,从首行选中首列下拉,选中需要添加序号的所有行的首列ÿ…...
如何在 PyCharm 中配置 HTTP 代理以确保网络连接的顺畅性
如何在 PyCharm 中配置 HTTP 代理以确保网络连接的顺畅性 在配置 PyCharm 的 HTTP 代理以确保网络连接的顺畅性时,需按照一定的步骤进行设置,这不仅有助于确保 PyCharm 能够顺利访问互联网资源,还能保证插件和工具的正常更新与同步。以下是详…...
查找萤石云IOS Sdk中的编解码接口
2021/1/20 以前的时候,碰到的问题,想把萤石云视频介入到TRTC,但是... 萤石云的IOS接口中没有相应的解码播放库,也就是找不到PlayerSDK对应部分,怎么做呢? 一个是坐等萤石云开放这部分接口,可能…...
webpack配置
4-3vue-loader测试_哔哩哔哩_bilibili 一.新建文件夹vue_todo,vscode打开 二.ctrl打开终端,输入npm init -y,快速生成一个默认的package.json文件 之后左边出现项目初始化文件package.json 三.接下来需要webpack完成打包,所以安装…...