diff --git a/README.md b/README.md index 6fd7958..eaa6051 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,17 @@ P2P CDN +/data/path/filename.xx + +缓存指定资源后, 假设资源按 16kb 分片 + a1 要求返回第 1 片, + a2 要求返回第 2 片, 如果 a2 提前完成, 继续返回第 3 片 + 全部分片接收完毕后合并进行 size 和 hash 验证 + +资源表汇报给 + + + 持有资源变动时, 通知节点列表 获取一个资源时, 先查找拥有此资源的节点列表 @@ -34,3 +45,18 @@ O1双向查询 建立连接后交换资源列表及过期时间 +### 最短响应时间 +1. 请求某个资源时, 先向节点列表伺服器请求拥有此资源的节点列表, 再通过节点列表取一个建立连接信道, 随后获取此资源 +2. 由于节点是持久在线, 资源伺服器对节点空间位置进行节点网络分布预测, 返回给距离请求节点最近的几个节点列表(减少地域距离耗时) +3. 同时向多个节点建立连接, 获取某个资源时从不同节点请求不同分片, 这需要预先了解资源大小分片数量(通过并发减少串行下载时间, 且能从更快的节点下载更多的分片, 慢的节点减少分片下载) +4. 建立连接需要多次三方通信握手, 且保持连接也需要续握手, 如何减少握手时间? +5. 每个资源都从服务器请求节点列表效率极差, 如何复用, +6. 建立连接耗时较大, 如何复用连接? + +在线与资源分布计算, +假设 10-100 同时在线, 每节点储量为1000资源(1GB) +100 设备储存 100G资源, 去除重复 75% 则 25G 资源被减耗 +高峰访问不均, 利用率上升 50% 则实 %50 流量被减耗 +由于峰值浮动, 在低谷期减耗效率仅为 10% +在线人数越多则减耗比率越大 + diff --git a/index.html b/index.html index 666ee97..cd3e5ae 100644 --- a/index.html +++ b/index.html @@ -2,3 +2,4 @@ PCDN + diff --git a/main.js b/main.js index f75ff3a..d2b86a9 100644 --- a/main.js +++ b/main.js @@ -1,33 +1,33 @@ -//export class Node { -// id: '' -// name: '' -// assets: new Map() -// get(name) {} -// set(name) {} -// del(name) {} -//} -// -//export const nodes = new Map() // 在线的节点 -//export const assets = new Map() // 在线的资源 -// -//// 示例: 查询持有某个资源的节点列表 -//const list = query('xxx.jpg').map(item => 'node id') -//const 测速排序 = () => {} -// -//// 查询某个 -// -//// 示例: 通过PCDN节点网络获取某个资源 -//const pcdn = new PCDN({ server: '/pcdn' }) -//pcdn.get('xxx.jpg').then(file => {}) +// 连接信令服务器 +const protocol = window.location.protocol.replace('http', 'ws') +const ws = new WebSocket(`${protocol}//${window.location.host}/api`) +ws.onopen = () => { + console.log('WebSocket connection opened') + ws.send(JSON.stringify({ type: 'init', data: 'Hello Server' })) +} +ws.onmessage = (event) => { + console.log('Message from server:', event.data) +} +ws.onerror = (error) => { + console.error('WebSocket error:', error) +} +ws.onclose = () => { + console.log('WebSocket connection closed') +} +// Service Worker if ('serviceWorker' in navigator) { navigator.serviceWorker.addEventListener('message', event => { console.log('收到消息 Service Worker: ', event.data) // 接收消息 event.source.postMessage('Hello from main thread') // 回应 Service Worker }) - navigator.serviceWorker.register('/sw.js').then((registration) => { - console.log('Service Worker 注册成功: ') - }).catch((error) => { - console.log('Service Worker 注册失败: ') - }) + navigator.serviceWorker.register('/sw.js') + //.then((registration) => { + // console.log('Service Worker 注册成功') + // registration.showNotification('Hello World') + //}) } + +//navigator.storage.estimate().then((estimate) => { +// console.log('Storage usage: ', estimate.usage / 1000 / 1000 / 1000, estimate.quota / 1000 / 1000 / 1000) +//}) diff --git a/package.json b/package.json index c30cf7c..110a080 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,19 @@ "name": "pcdn", "version": "1.0.0", "description": "P2P CDN", - "main": "index.js", + "type": "module", + "main": "main.js", "scripts": { - "dev": "vite --open" + "dev": "concurrently 'vite --open' 'node server.js'", + "start": "node server.js" }, "author": "satori.love", "license": "GPL-3.0-only", "devDependencies": { + "concurrently": "^8.2.2", "vite": "^5.2.12" + }, + "dependencies": { + "ws": "^8.18.1" } } diff --git a/public/test.jpg b/public/test.jpg new file mode 100644 index 0000000..c07dfe6 Binary files /dev/null and b/public/test.jpg differ diff --git a/server.js b/server.js new file mode 100644 index 0000000..dc58052 --- /dev/null +++ b/server.js @@ -0,0 +1,29 @@ +import http from 'http' +import { WebSocketServer } from 'ws' + +// 创建一个 HTTP 服务器 +const server = http.createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.end('WebSocket server is running') +}) + +// 创建 WebSocket 服务器,并将其绑定到 HTTP 服务器 +const wss = new WebSocketServer({ server }) + +// 处理 WebSocket 连接 +wss.on('connection', ws => { + console.log('A new client connected!') + + // 监听客户端发送的消息 + ws.on('message', message => { + console.log('收到数据:', message) + }) + + // 发送欢迎消息给客户端 + ws.send('Hello from WebSocket server!') +}) + +// 启动 HTTP 服务器,监听端口 +server.listen(8080, () => { + console.log('Server is listening on http://localhost:8080') +}) diff --git a/sw.js b/sw.js index 9e7a71b..ea5997c 100644 --- a/sw.js +++ b/sw.js @@ -1,20 +1,85 @@ +const CACHE_NAME = 'file-cache-v1' +const FILE_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml'] + self.addEventListener('install', (event) => { console.log('Service Worker 安装') }) - + self.addEventListener('activate', (event) => { - console.log('Service Worker 激活') + console.log('Service Worker 激活, 立即接管页面') + event.waitUntil(clients.claim()) }) self.addEventListener('fetch', (event) => { - console.log('Fetch intercepted for:', event.request.url) - self.clients.matchAll().then(clients => { - clients.forEach(client => { - client.postMessage('Hello from Service Worker') + //console.log('Service Worker 获取资源:', event.request.url) + + if (!event.request.url.match(/\.(jpeg|jpg|gif|png|webp|svg)$/i)) { + return event.respondWith(fetch(event.request)) + } + + event.respondWith( + caches.match(event.request).then(async (cachedResponse) => { + // 如果文件已缓存,则返回缓存的响应 + if (cachedResponse) { + console.log('使用了缓存:', event.request.url) + + for (const key of await caches.keys()) { + const cache = await caches.open(key) + + const requests = await cache.keys() + requests.forEach((request) => { + console.log(request.url) + }) + } + + return cachedResponse + } + + // 如果缓存中没有,则发起网络请求并缓存新图像(检查网络响应是否为文件) + return fetch(event.request).then(async (networkResponse) => { + if (networkResponse && networkResponse.ok && FILE_TYPES.includes(networkResponse.headers.get('Content-Type'))) { + const cache = await caches.open(CACHE_NAME) + await cache.put(event.request, networkResponse.clone()) + } + return networkResponse + }) }) - }) + ) + + //// 先尝试从 webRTC 获取资源, 如果失败则从网络获取资源 + //event.respondWith(async function () { + // const response = await fetch(event.request) + // return response + //}()) + + //event.respondWith(fetch(event.request)) + //self.clients.matchAll().then(clients => { + // clients.forEach(client => { + // client.postMessage('Hello from Service Worker') + // }) + //}) }) self.addEventListener('message', event => { - console.log('收到主线程消息: ', event.data) + console.log("%c收到主线程消息: " + event.data, 'color: red;') }) + +//// 检查通知权限 +//if (Notification.permission === 'granted') { +// // 如果已经授权,则可以显示通知 +// registration.showNotification('Hello World') +//} else if (Notification.permission === 'default') { +// // 如果权限尚未授予,则请求权限 +// Notification.requestPermission().then(permission => { +// if (permission === 'granted') { +// // 权限授予成功后显示通知 +// showNotification(); +// } else { +// console.log('Notification permission denied'); +// } +// }).catch(err => { +// console.log('Failed to request notification permission', err); +// }); +//} else { +// console.log('Notification permission denied'); +//} \ No newline at end of file