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 - - - -
-

Entanglement

-

同步

-
- - - - \ 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; - } -}