diff --git a/public/cloud.html b/public/cloud.html
deleted file mode 100644
index 3019b51..0000000
--- a/public/cloud.html
+++ /dev/null
@@ -1,119 +0,0 @@
-
-
-
- Cloud Upload Icon
-
-
-
-
-
-
-
diff --git a/public/cs.js b/public/cs.js
deleted file mode 100644
index f2128b3..0000000
--- a/public/cs.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// 动态分片流
-// 1. 默认提供分片流
-// 2. 允许请求者指定数据段, 返回相应分片, 发送队列可以并行, 接收队列在检查没有新数据时, 补充要求失败的分片
-// 3. 可靠模式下分片是完整的, 但响应不是立即的, 当请求超时时放弃接受和发送队列中的分片
-// 4. 同一个资源可以向不同的提供者请求, 将不同区段的分片合并然后存储
-// 5. 不需要验证hash, 但是需要验证顺序, 发送者应当标记这个分片的开始和结束位置
-
-// 资源id, 开始位置, 结束位置, 数据
-// 资源的其他信息, 如hash, 大小, 类型等, 由其他方式获取
-// 并不信任发送者, 所以必须验证发送者的分片HASH, 所以资源信息里应当包含每个分片的HASH
-// 但如果每个HASH都要验证, 那么验证的成本太高, 所以应当有一个HASH验证的阈值, 例如每100个分片验证一次, 如果验证失败, 则放弃这个分片, 重新请求
-
-// 数据块, 每个数据块验证一次HASH, 如果验证失败, 则放弃这个数据块, 重新请求
-// 数据块的大小应当是2的幂, 例如2, 4, 8, 16, 32, 64, 128, 256, 512, 1024等
-// 数据块的大小应当是分片的倍数, 例如分片大小是1024, 那么数据块大小应当是1024的倍数, 例如1024, 2048, 4096等
-
-/**
- * {
- * id: 'resource id',
- * type: 'resource type',
- * size: 'resource size',
- * hash: 'resource hash',
- * blocks: [
- * 'md5 hash of block 1',
- * 'md5 hash of block 2',
- * 'md5 hash of block 3',
- * ]
- * }
- */
-
-// 分片交叉验证
-// 1. 每个分片都没有hash
-// 2. 文件有一个完整的hash
-// 3. 文件取4个交叉验证维度
-// 4. 16个HASH验证1GB文件
-// 5. 且只在完整hash验证失败时才验证分片
diff --git a/public/entanglement.js b/public/entanglement.js
deleted file mode 100644
index a004176..0000000
--- a/public/entanglement.js
+++ /dev/null
@@ -1,192 +0,0 @@
-export default class Entanglement {
- constructor({ options }) {
- this.event = {}
- this.options = options
- this.store = {}
- this.users = []
- this.channels = [{ name: 'json' }]
- this.ws = this.__create_websocket()
- }
-
- async __create_webrtc(user) {
- const pc = new RTCPeerConnection(this.options)
- // 当有新的媒体流加入时触发
- pc.onicecandidate = (event) => {
- if (event.candidate) {
- console.debug(user.name, '发出 candidate 候选通道')
- this.ws.send(JSON.stringify({ type: 'candidate', user, candidate: event.candidate }))
- }
- }
- // 当连接状态发生改变时触发
- pc.oniceconnectionstatechange = (event) => {
- if (pc.iceConnectionState === 'disconnected' || pc.iceConnectionState === 'failed') {
- console.error(user.name, '需要添加新的 candidate')
- } else if (pc.iceConnectionState === 'connected' || pc.iceConnectionState === 'completed') {
- console.debug(user.name, 'WebRTC 连接已经建立成功')
- }
- }
- // 协商新的会话, 建立初始连接或在网络条件发生变化后重新协商连接
- pc.onnegotiationneeded = async (event) => {
- console.log('onnegotiationneeded', event)
- //const offer = await pc.createOffer()
- //await pc.setLocalDescription(offer)
- //this.ws.send(JSON.stringify({ type: 'offer', user, offer }))
- }
- // 当有新的媒体流加入时触发
- pc.ontrack = (event) => {
- console.log('ontrack', event)
- }
- // 当有新的数据通道加入时触发
- pc.ondatachannel = event => {
- console.log(user.name, '建立', event.channel.label, '接收通道')
- event.channel.onmessage = event => {
- console.log(user.name, '发来', event.target.label, '通道消息', event.data)
- }
- event.channel.onopen = () => {
- console.log(user.name, '打开', event.channel.label, '接收通道')
- //event.channel.send(JSON.stringify({ name: 'sato', hello: 'world' }))
- }
- event.channel.onclose = () => {
- console.log(user.name, '关闭', event.channel.label, '接收通道')
- }
- event.channel.onerror = () => {
- console.log(user.name, '通道', event.channel.label, '发生错误')
- }
- }
- // 创建数据通道
- user.channels = this.channels.map(item => {
- const channel = pc.createDataChannel(item.name, { reliable: true })
- channel.onopen = () => {
- console.log('打开数据发送通道')
- channel.send(JSON.stringify({ name: 'sato', hello: 'world' }))
- }
- channel.onmessage = event => {
- console.log('收到数据发送通道消息', event.data)
- }
- channel.onclose = () => {
- console.log('关闭数据发送通道')
- }
- channel.onerror = () => {
- console.log('发送通道发生错误')
- }
- return { channel, ...item }
- })
- return pc
- }
-
- async __create_websocket() {
- const host = window.location.host
- const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
- const ws = new WebSocket(`${protocol}://${host}/entanglement?name=sato&channel=chat`)
- ws.onopen = async () => {
- console.log('websocket 连接成功')
- if (this.ws instanceof Promise) {
- this.ws = await this.ws
- }
- }
- ws.onclose = async () => {
- console.log('websocket 连接关闭, 3s后尝试重新连接...')
- await new Promise(resolve => setTimeout(resolve, 3000))
- this.ws = await this.__create_websocket()
- }
- ws.onerror = async () => {
- console.log('websocket 连接错误, 3s后尝试重新连接...')
- await new Promise(resolve => setTimeout(resolve, 3000))
- this.ws = await this.__create_websocket()
- }
- ws.onmessage = async event => {
- const data = JSON.parse(event.data)
- if (data.type === 'list') {
- console.debug('收到在线列表', data.list)
- await Promise.all(data.list.map(async user => {
- console.debug('发送给', user.name, 'offer')
- const pc = await this.__create_webrtc(user)
- const offer = await pc.createOffer()
- await pc.setLocalDescription(offer)
- this.users.push({ ...user, webrtc: pc }) // 必须在send之前存入
- this.ws.send(JSON.stringify({ type: 'offer', user, offer }))
- }))
- return
- }
- if (data.type === 'push') {
- console.debug(data.user.name, '上线', data)
- return
- }
- if (data.type === 'pull') {
- console.debug(data.user.name, '下线', data)
- return
- }
- if (data.type === 'offer') {
- console.debug(data.user.name, '发来 offer')
- const pc = await this.__create_webrtc(data.user)
- await pc.setRemoteDescription(data.offer)
- const answer = await pc.createAnswer()
- await pc.setLocalDescription(answer)
- this.ws.send(JSON.stringify({ type: 'answer', user: data.user, answer }))
- this.users.push({ ...data.user, webrtc: pc })
- return
- }
- if (data.type === 'answer') {
- console.debug(data.user.name, '发来 answer')
- const pc = this.users.find(user => user.id === data.user.id).webrtc
- await pc.setRemoteDescription(data.answer)
- return
- }
- if (data.type === 'candidate') {
- console.debug(data.user.name, '发来 candidate 候选通道', JSON.stringify(this.users))
- const pc = this.users.find(user => user.id === data.user.id).webrtc
- await pc.addIceCandidate(data.candidate)
- return
- }
- console.error('收到未知数据:', data)
- }
- return ws
- }
-
- // 向所有在线的用户广播消息(webrtc)
- send_all(channel_name, data) {
- console.log('向', channel_name, '频道广播消息:', data)
- console.log('在线用户:', this.users)
- this.users.forEach(async user => {
- console.log('向', user.name, '发送', channel_name, '频道消息:', data)
- const ch = user.channels.find(item => item.name === channel_name).channel
- // 等待 datachannel 打开(临时解决方案)
- while (ch.readyState !== 'open') {
- await new Promise(resolve => setTimeout(resolve, 100))
- }
- console.log('完成发送', channel_name, '频道消息:', data)
- ch.send(JSON.stringify(data))
- })
- }
-
- // 数据被修改时触发
- set(name, data) {
- // 递归创建代理对象
- const useProxy = (obj, path = []) => {
- const proxy = new Proxy(obj, {
- set: (target, key, value) => {
- if (!Array.isArray(target) || key !== 'length') {
- console.log('对象被修改', [...path, key], value)
- this.send_all(name, { key: [...path, key], value }) // 向所有在线的用户广播消息
- }
- return Reflect.set(target, key, value)
- },
- get: (target, key) => {
- return target[key]
- }
- })
- Object.keys(obj).forEach(key => {
- if (typeof obj[key] === 'object') {
- obj[key] = useProxy(obj[key], [...path, key])
- }
- })
- return proxy
- }
- return Reflect.set(this.store, name, useProxy(data))
- }
-
- // 读取一个通道
- get(name) {
- return this.store[name]
- }
-}
diff --git a/public/test.html b/public/test.html
deleted file mode 100644
index 756584a..0000000
--- a/public/test.html
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
-
-
-
- webRTC
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/style.css b/style.css
deleted file mode 100644
index abf9d15..0000000
--- a/style.css
+++ /dev/null
@@ -1,97 +0,0 @@
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- -webkit-text-size-adjust: 100%;
-}
-
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
-}
-
-body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
-}
-
-#app {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
-}
-
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
-}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.vanilla:hover {
- filter: drop-shadow(0 0 2em #f7df1eaa);
-}
-
-.card {
- padding: 2em;
-}
-
-.read-the-docs {
- color: #888;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
-}