├── package.json ├── README.md ├── server.js ├── index.html └── public └── SkyRTC-client.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SkyRTC-demo", 3 | "description": "A simple SkyRTC demo", 4 | "dependencies": { 5 | "ws": ">= 0.0.0", 6 | "node-uuid": ">= 1.4.1", 7 | "express": "3.1.0", 8 | "skyrtc": ">= 0.0.0" 9 | }, 10 | "readmeFilename": "README.md" 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #一个简单的SkyRTC示例 2 | --- 3 | ##简介 4 | 这是一个使用SkyRTC和SkyRTC-client搭建浏览器中音频、视频、文字聊天室的Demo 5 | 6 | ##安装和使用 7 | 1. 安装Node.js及npm环境 8 | 2. 下载源码到本地,并解压缩 9 | 3. 移动到解压后的目录下 10 | 4. 使用命令`npm install`安装所需要的库 11 | 5. 运行命令`node server.js`,建议配合`forever` 12 | 6. 访问`localhost:3000#roomName`查看效果,其中`roomName`为进入的房间名,不同房间的用户无法互相通信 13 | 14 | ##功能说明 15 | 支持划分房间的在线音频、视频、文字聊天,提供房间内文件共享功能 16 | 17 | ##SkyRTC项目链接 18 | [SkyRTC项目](https://github.com/LingyuCoder/SkyRTC) 19 | 20 | [SkyRTC-client项目](https://github.com/LingyuCoder/SkyRTC-client) 21 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var server = require('http').createServer(app); 4 | var SkyRTC = require('skyrtc').listen(server); 5 | var path = require("path"); 6 | 7 | var port = process.env.PORT || 3000; 8 | server.listen(port); 9 | 10 | app.use(express.static(path.join(__dirname, 'public'))); 11 | 12 | app.get('/', function(req, res) { 13 | res.sendfile(__dirname + '/index.html'); 14 | }); 15 | 16 | SkyRTC.rtc.on('new_connect', function(socket) { 17 | console.log('创建新连接'); 18 | }); 19 | 20 | SkyRTC.rtc.on('remove_peer', function(socketId) { 21 | console.log(socketId + "用户离开"); 22 | }); 23 | 24 | SkyRTC.rtc.on('new_peer', function(socket, room) { 25 | console.log("新用户" + socket.id + "加入房间" + room); 26 | }); 27 | 28 | SkyRTC.rtc.on('socket_message', function(socket, msg) { 29 | console.log("接收到来自" + socket.id + "的新消息:" + msg); 30 | }); 31 | 32 | SkyRTC.rtc.on('ice_candidate', function(socket, ice_candidate) { 33 | console.log("接收到来自" + socket.id + "的ICE Candidate"); 34 | }); 35 | 36 | SkyRTC.rtc.on('offer', function(socket, offer) { 37 | console.log("接收到来自" + socket.id + "的Offer"); 38 | }); 39 | 40 | SkyRTC.rtc.on('answer', function(socket, answer) { 41 | console.log("接收到来自" + socket.id + "的Answer"); 42 | }); 43 | 44 | SkyRTC.rtc.on('error', function(error) { 45 | console.log("发生错误:" + error.message); 46 | }); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SkyRTC聊天室Demo 6 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | 81 |
82 |
83 | 84 |
85 |
86 |
87 | 88 | 89 | 219 | 220 | -------------------------------------------------------------------------------- /public/SkyRTC-client.js: -------------------------------------------------------------------------------- 1 | var SkyRTC = function() { 2 | var PeerConnection = (window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection); 3 | var URL = (window.URL || window.webkitURL || window.msURL || window.oURL); 4 | var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); 5 | var nativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate); 6 | var nativeRTCSessionDescription = (window.mozRTCSessionDescription || window.RTCSessionDescription); // order is very important: "RTCSessionDescription" defined in Nighly but useless 7 | var moz = !!navigator.mozGetUserMedia; 8 | var iceServer = { 9 | "iceServers": [{ 10 | "url": "stun:stun.l.google.com:19302" 11 | }] 12 | }; 13 | var packetSize = 1000; 14 | 15 | /**********************************************************/ 16 | /* */ 17 | /* 事件处理器 */ 18 | /* */ 19 | /**********************************************************/ 20 | function EventEmitter() { 21 | this.events = {}; 22 | } 23 | //绑定事件函数 24 | EventEmitter.prototype.on = function(eventName, callback) { 25 | this.events[eventName] = this.events[eventName] || []; 26 | this.events[eventName].push(callback); 27 | }; 28 | //触发事件函数 29 | EventEmitter.prototype.emit = function(eventName, _) { 30 | var events = this.events[eventName], 31 | args = Array.prototype.slice.call(arguments, 1), 32 | i, m; 33 | 34 | if (!events) { 35 | return; 36 | } 37 | for (i = 0, m = events.length; i < m; i++) { 38 | events[i].apply(null, args); 39 | } 40 | }; 41 | 42 | 43 | /**********************************************************/ 44 | /* */ 45 | /* 流及信道建立部分 */ 46 | /* */ 47 | /**********************************************************/ 48 | 49 | 50 | /*******************基础部分*********************/ 51 | function skyrtc() { 52 | //本地media stream 53 | this.localMediaStream = null; 54 | //所在房间 55 | this.room = ""; 56 | //接收文件时用于暂存接收文件 57 | this.fileData = {}; 58 | //本地WebSocket连接 59 | this.socket = null; 60 | //本地socket的id,由后台服务器创建 61 | this.me = null; 62 | //保存所有与本地相连的peer connection, 键为socket id,值为PeerConnection类型 63 | this.peerConnections = {}; 64 | //保存所有与本地连接的socket的id 65 | this.connections = []; 66 | //初始时需要构建链接的数目 67 | this.numStreams = 0; 68 | //初始时已经连接的数目 69 | this.initializedStreams = 0; 70 | //保存所有的data channel,键为socket id,值通过PeerConnection实例的createChannel创建 71 | this.dataChannels = {}; 72 | //保存所有发文件的data channel及其发文件状态 73 | this.fileChannels = {}; 74 | //保存所有接受到的文件 75 | this.receiveFiles = {}; 76 | } 77 | //继承自事件处理器,提供绑定事件和触发事件的功能 78 | skyrtc.prototype = new EventEmitter(); 79 | 80 | 81 | /*************************服务器连接部分***************************/ 82 | 83 | 84 | //本地连接信道,信道为websocket 85 | skyrtc.prototype.connect = function(server, room) { 86 | var socket, 87 | that = this; 88 | room = room || ""; 89 | socket = this.socket = new WebSocket(server); 90 | socket.onopen = function() { 91 | socket.send(JSON.stringify({ 92 | "eventName": "__join", 93 | "data": { 94 | "room": room 95 | } 96 | })); 97 | that.emit("socket_opened", socket); 98 | }; 99 | 100 | socket.onmessage = function(message) { 101 | var json = JSON.parse(message.data); 102 | if (json.eventName) { 103 | that.emit(json.eventName, json.data); 104 | } else { 105 | that.emit("socket_receive_message", socket, json); 106 | } 107 | }; 108 | 109 | socket.onerror = function(error) { 110 | that.emit("socket_error", error, socket); 111 | }; 112 | 113 | socket.onclose = function(data) { 114 | that.localMediaStream.close(); 115 | var pcs = that.peerConnections; 116 | for (i = pcs.length; i--;) { 117 | that.closePeerConnection(pcs[i]); 118 | } 119 | that.peerConnections = []; 120 | that.dataChannels = {}; 121 | that.fileChannels = {}; 122 | that.connections = []; 123 | that.fileData = {}; 124 | that.emit('socket_closed', socket); 125 | }; 126 | 127 | this.on('_peers', function(data) { 128 | //获取所有服务器上的 129 | that.connections = data.connections; 130 | that.me = data.you; 131 | that.emit("get_peers", that.connections); 132 | that.emit('connected', socket); 133 | }); 134 | 135 | this.on("_ice_candidate", function(data) { 136 | var candidate = new nativeRTCIceCandidate(data); 137 | var pc = that.peerConnections[data.socketId]; 138 | pc.addIceCandidate(candidate); 139 | that.emit('get_ice_candidate', candidate); 140 | }); 141 | 142 | this.on('_new_peer', function(data) { 143 | that.connections.push(data.socketId); 144 | var pc = that.createPeerConnection(data.socketId), 145 | i, m; 146 | pc.addStream(that.localMediaStream); 147 | that.emit('new_peer', data.socketId); 148 | }); 149 | 150 | this.on('_remove_peer', function(data) { 151 | var sendId; 152 | that.closePeerConnection(that.peerConnections[data.socketId]); 153 | delete that.peerConnections[data.socketId]; 154 | delete that.dataChannels[data.socketId]; 155 | for (sendId in that.fileChannels[data.socketId]) { 156 | that.emit("send_file_error", new Error("Connection has been closed"), data.socketId, sendId, that.fileChannels[data.socketId][sendId].file); 157 | } 158 | delete that.fileChannels[data.socketId]; 159 | that.emit("remove_peer", data.socketId); 160 | }); 161 | 162 | this.on('_offer', function(data) { 163 | that.receiveOffer(data.socketId, data.sdp); 164 | that.emit("get_offer", data); 165 | }); 166 | 167 | this.on('_answer', function(data) { 168 | that.receiveAnswer(data.socketId, data.sdp); 169 | that.emit('get_answer', data); 170 | }); 171 | 172 | this.on('send_file_error', function(error, socketId, sendId, file) { 173 | that.cleanSendFile(sendId, socketId); 174 | }); 175 | 176 | this.on('receive_file_error', function(error, sendId) { 177 | that.cleanReceiveFile(sendId); 178 | }); 179 | 180 | this.on('ready', function() { 181 | that.createPeerConnections(); 182 | that.addStreams(); 183 | that.addDataChannels(); 184 | that.sendOffers(); 185 | }); 186 | }; 187 | 188 | 189 | /*************************流处理部分*******************************/ 190 | 191 | 192 | //创建本地流 193 | skyrtc.prototype.createStream = function(options) { 194 | var that = this; 195 | 196 | options.video = !!options.video; 197 | options.audio = !!options.audio; 198 | 199 | if (getUserMedia) { 200 | this.numStreams++; 201 | getUserMedia.call(navigator, options, function(stream) { 202 | that.localMediaStream = stream; 203 | that.initializedStreams++; 204 | that.emit("stream_created", stream); 205 | if (that.initializedStreams === that.numStreams) { 206 | that.emit("ready"); 207 | } 208 | }, 209 | function(error) { 210 | that.emit("stream_create_error", error); 211 | }); 212 | } else { 213 | that.emit("stream_create_error", new Error('WebRTC is not yet supported in this browser.')); 214 | } 215 | }; 216 | 217 | //将本地流添加到所有的PeerConnection实例中 218 | skyrtc.prototype.addStreams = function() { 219 | var i, m, 220 | stream, 221 | connection; 222 | for (connection in this.peerConnections) { 223 | this.peerConnections[connection].addStream(this.localMediaStream); 224 | } 225 | }; 226 | 227 | //将流绑定到video标签上用于输出 228 | skyrtc.prototype.attachStream = function(stream, domId) { 229 | var element = document.getElementById(domId); 230 | if (navigator.mozGetUserMedia) { 231 | element.mozSrcObject = stream; 232 | element.play(); 233 | } else { 234 | element.src = webkitURL.createObjectURL(stream); 235 | } 236 | element.src = webkitURL.createObjectURL(stream); 237 | }; 238 | 239 | 240 | /***********************信令交换部分*******************************/ 241 | 242 | 243 | //向所有PeerConnection发送Offer类型信令 244 | skyrtc.prototype.sendOffers = function() { 245 | var i, m, 246 | pc, 247 | that = this, 248 | pcCreateOfferCbGen = function(pc, socketId) { 249 | return function(session_desc) { 250 | pc.setLocalDescription(session_desc); 251 | that.socket.send(JSON.stringify({ 252 | "eventName": "__offer", 253 | "data": { 254 | "sdp": session_desc, 255 | "socketId": socketId 256 | } 257 | })); 258 | }; 259 | }, 260 | pcCreateOfferErrorCb = function(error) { 261 | console.log(error); 262 | }; 263 | for (i = 0, m = this.connections.length; i < m; i++) { 264 | pc = this.peerConnections[this.connections[i]]; 265 | pc.createOffer(pcCreateOfferCbGen(pc, this.connections[i]), pcCreateOfferErrorCb); 266 | } 267 | }; 268 | 269 | //接收到Offer类型信令后作为回应返回answer类型信令 270 | skyrtc.prototype.receiveOffer = function(socketId, sdp) { 271 | var pc = this.peerConnections[socketId]; 272 | this.sendAnswer(socketId, sdp); 273 | }; 274 | 275 | //发送answer类型信令 276 | skyrtc.prototype.sendAnswer = function(socketId, sdp) { 277 | var pc = this.peerConnections[socketId]; 278 | var that = this; 279 | pc.setRemoteDescription(new nativeRTCSessionDescription(sdp)); 280 | pc.createAnswer(function(session_desc) { 281 | pc.setLocalDescription(session_desc); 282 | that.socket.send(JSON.stringify({ 283 | "eventName": "__answer", 284 | "data": { 285 | "socketId": socketId, 286 | "sdp": session_desc 287 | } 288 | })); 289 | }, function(error) { 290 | console.log(error); 291 | }); 292 | }; 293 | 294 | //接收到answer类型信令后将对方的session描述写入PeerConnection中 295 | skyrtc.prototype.receiveAnswer = function(socketId, sdp) { 296 | var pc = this.peerConnections[socketId]; 297 | pc.setRemoteDescription(new nativeRTCSessionDescription(sdp)); 298 | }; 299 | 300 | 301 | /***********************点对点连接部分*****************************/ 302 | 303 | 304 | //创建与其他用户连接的PeerConnections 305 | skyrtc.prototype.createPeerConnections = function() { 306 | var i, m; 307 | for (i = 0, m = this.connections.length; i < m; i++) { 308 | this.createPeerConnection(this.connections[i]); 309 | } 310 | }; 311 | 312 | //创建单个PeerConnection 313 | skyrtc.prototype.createPeerConnection = function(socketId) { 314 | var that = this; 315 | var pc = new PeerConnection(iceServer); 316 | this.peerConnections[socketId] = pc; 317 | pc.onicecandidate = function(evt) { 318 | if (evt.candidate) 319 | that.socket.send(JSON.stringify({ 320 | "eventName": "__ice_candidate", 321 | "data": { 322 | "label": evt.candidate.sdpMLineIndex, 323 | "candidate": evt.candidate.candidate, 324 | "socketId": socketId 325 | } 326 | })); 327 | that.emit("pc_get_ice_candidate", evt.candidate, socketId, pc); 328 | }; 329 | 330 | pc.onopen = function() { 331 | that.emit("pc_opened", socketId, pc); 332 | }; 333 | 334 | pc.onaddstream = function(evt) { 335 | that.emit('pc_add_stream', evt.stream, socketId, pc); 336 | }; 337 | 338 | pc.ondatachannel = function(evt) { 339 | that.addDataChannel(socketId, evt.channel); 340 | that.emit('pc_add_data_channel', evt.channel, socketId, pc); 341 | }; 342 | return pc; 343 | }; 344 | 345 | //关闭PeerConnection连接 346 | skyrtc.prototype.closePeerConnection = function(pc) { 347 | if (!pc) return; 348 | pc.close(); 349 | }; 350 | 351 | 352 | /***********************数据通道连接部分*****************************/ 353 | 354 | 355 | //消息广播 356 | skyrtc.prototype.broadcast = function(message) { 357 | var socketId; 358 | for (socketId in this.dataChannels) { 359 | this.sendMessage(message, socketId); 360 | } 361 | }; 362 | 363 | //发送消息方法 364 | skyrtc.prototype.sendMessage = function(message, socketId) { 365 | if (this.dataChannels[socketId].readyState.toLowerCase() === 'open') { 366 | this.dataChannels[socketId].send(JSON.stringify({ 367 | type: "__msg", 368 | data: message 369 | })); 370 | } 371 | }; 372 | 373 | //对所有的PeerConnections创建Data channel 374 | skyrtc.prototype.addDataChannels = function() { 375 | var connection; 376 | for (connection in this.peerConnections) { 377 | this.createDataChannel(connection); 378 | } 379 | }; 380 | 381 | //对某一个PeerConnection创建Data channel 382 | skyrtc.prototype.createDataChannel = function(socketId, label) { 383 | var pc, key, channel; 384 | pc = this.peerConnections[socketId]; 385 | 386 | if (!socketId) { 387 | this.emit("data_channel_create_error", socketId, new Error("attempt to create data channel without socket id")); 388 | } 389 | 390 | if (!(pc instanceof PeerConnection)) { 391 | this.emit("data_channel_create_error", socketId, new Error("attempt to create data channel without peerConnection")); 392 | } 393 | try { 394 | channel = pc.createDataChannel(label); 395 | } catch (error) { 396 | this.emit("data_channel_create_error", socketId, error); 397 | } 398 | 399 | return this.addDataChannel(socketId, channel); 400 | }; 401 | 402 | //为Data channel绑定相应的事件回调函数 403 | skyrtc.prototype.addDataChannel = function(socketId, channel) { 404 | var that = this; 405 | channel.onopen = function() { 406 | that.emit('data_channel_opened', channel, socketId); 407 | }; 408 | 409 | channel.onclose = function(event) { 410 | delete that.dataChannels[socketId]; 411 | that.emit('data_channel_closed', channel, socketId); 412 | }; 413 | 414 | channel.onmessage = function(message) { 415 | var json; 416 | json = JSON.parse(message.data); 417 | if (json.type === '__file') { 418 | /*that.receiveFileChunk(json);*/ 419 | that.parseFilePacket(json, socketId); 420 | } else { 421 | that.emit('data_channel_message', channel, socketId, json.data); 422 | } 423 | }; 424 | 425 | channel.onerror = function(err) { 426 | that.emit('data_channel_error', channel, socketId, err); 427 | }; 428 | 429 | this.dataChannels[socketId] = channel; 430 | return channel; 431 | }; 432 | 433 | 434 | 435 | /**********************************************************/ 436 | /* */ 437 | /* 文件传输 */ 438 | /* */ 439 | /**********************************************************/ 440 | 441 | /************************公有部分************************/ 442 | 443 | //解析Data channel上的文件类型包,来确定信令类型 444 | skyrtc.prototype.parseFilePacket = function(json, socketId) { 445 | var signal = json.signal, 446 | that = this; 447 | if (signal === 'ask') { 448 | that.receiveFileAsk(json.sendId, json.name, json.size, socketId); 449 | } else if (signal === 'accept') { 450 | that.receiveFileAccept(json.sendId, socketId); 451 | } else if (signal === 'refuse') { 452 | that.receiveFileRefuse(json.sendId, socketId); 453 | } else if (signal === 'chunk') { 454 | that.receiveFileChunk(json.data, json.sendId, socketId, json.last, json.percent); 455 | } else if (signal === 'close') { 456 | //TODO 457 | } 458 | }; 459 | 460 | /***********************发送者部分***********************/ 461 | 462 | 463 | //通过Dtata channel向房间内所有其他用户广播文件 464 | skyrtc.prototype.shareFile = function(dom) { 465 | var socketId, 466 | that = this; 467 | for (socketId in that.dataChannels) { 468 | that.sendFile(dom, socketId); 469 | } 470 | }; 471 | 472 | //向某一单个用户发送文件 473 | skyrtc.prototype.sendFile = function(dom, socketId) { 474 | var that = this, 475 | file, 476 | reader, 477 | fileToSend, 478 | sendId; 479 | if (typeof dom === 'string') { 480 | dom = document.getElementById(dom); 481 | } 482 | if (!dom) { 483 | that.emit("send_file_error", new Error("Can not find dom while sending file"), socketId); 484 | return; 485 | } 486 | if (!dom.files || !dom.files[0]) { 487 | that.emit("send_file_error", new Error("No file need to be sended"), socketId); 488 | return; 489 | } 490 | file = dom.files[0]; 491 | that.fileChannels[socketId] = that.fileChannels[socketId] || {}; 492 | sendId = that.getRandomString(); 493 | fileToSend = { 494 | file: file, 495 | state: "ask" 496 | }; 497 | that.fileChannels[socketId][sendId] = fileToSend; 498 | that.sendAsk(socketId, sendId, fileToSend); 499 | that.emit("send_file", sendId, socketId, file); 500 | }; 501 | 502 | //发送多个文件的碎片 503 | skyrtc.prototype.sendFileChunks = function() { 504 | var socketId, 505 | sendId, 506 | that = this, 507 | nextTick = false; 508 | for (socketId in that.fileChannels) { 509 | for (sendId in that.fileChannels[socketId]) { 510 | if (that.fileChannels[socketId][sendId].state === "send") { 511 | nextTick = true; 512 | that.sendFileChunk(socketId, sendId); 513 | } 514 | } 515 | } 516 | if (nextTick) { 517 | setTimeout(function() { 518 | that.sendFileChunks(); 519 | }, 10); 520 | } 521 | }; 522 | 523 | //发送某个文件的碎片 524 | skyrtc.prototype.sendFileChunk = function(socketId, sendId) { 525 | var that = this, 526 | fileToSend = that.fileChannels[socketId][sendId], 527 | packet = { 528 | type: "__file", 529 | signal: "chunk", 530 | sendId: sendId 531 | }, 532 | channel; 533 | 534 | fileToSend.sendedPackets++; 535 | fileToSend.packetsToSend--; 536 | 537 | 538 | if (fileToSend.fileData.length > packetSize) { 539 | packet.last = false; 540 | packet.data = fileToSend.fileData.slice(0, packetSize); 541 | packet.percent = fileToSend.sendedPackets / fileToSend.allPackets * 100; 542 | that.emit("send_file_chunk", sendId, socketId, fileToSend.sendedPackets / fileToSend.allPackets * 100, fileToSend.file); 543 | } else { 544 | packet.data = fileToSend.fileData; 545 | packet.last = true; 546 | fileToSend.state = "end"; 547 | that.emit("sended_file", sendId, socketId, fileToSend.file); 548 | that.cleanSendFile(sendId, socketId); 549 | } 550 | 551 | channel = that.dataChannels[socketId]; 552 | 553 | if (!channel) { 554 | that.emit("send_file_error", new Error("Channel has been destoried"), socketId, sendId, fileToSend.file); 555 | return; 556 | } 557 | channel.send(JSON.stringify(packet)); 558 | fileToSend.fileData = fileToSend.fileData.slice(packet.data.length); 559 | }; 560 | 561 | //发送文件请求后若对方同意接受,开始传输 562 | skyrtc.prototype.receiveFileAccept = function(sendId, socketId) { 563 | var that = this, 564 | fileToSend, 565 | reader, 566 | initSending = function(event, text) { 567 | fileToSend.state = "send"; 568 | fileToSend.fileData = event.target.result; 569 | fileToSend.sendedPackets = 0; 570 | fileToSend.packetsToSend = fileToSend.allPackets = parseInt(fileToSend.fileData.length / packetSize, 10); 571 | that.sendFileChunks(); 572 | }; 573 | fileToSend = that.fileChannels[socketId][sendId]; 574 | reader = new window.FileReader(fileToSend.file); 575 | reader.readAsDataURL(fileToSend.file); 576 | reader.onload = initSending; 577 | that.emit("send_file_accepted", sendId, socketId, that.fileChannels[socketId][sendId].file); 578 | }; 579 | 580 | //发送文件请求后若对方拒绝接受,清除掉本地的文件信息 581 | skyrtc.prototype.receiveFileRefuse = function(sendId, socketId) { 582 | var that = this; 583 | that.fileChannels[socketId][sendId].state = "refused"; 584 | that.emit("send_file_refused", sendId, socketId, that.fileChannels[socketId][sendId].file); 585 | that.cleanSendFile(sendId, socketId); 586 | }; 587 | 588 | //清除发送文件缓存 589 | skyrtc.prototype.cleanSendFile = function(sendId, socketId) { 590 | var that = this; 591 | delete that.fileChannels[socketId][sendId]; 592 | }; 593 | 594 | //发送文件请求 595 | skyrtc.prototype.sendAsk = function(socketId, sendId, fileToSend) { 596 | var that = this, 597 | channel = that.dataChannels[socketId], 598 | packet; 599 | if (!channel) { 600 | that.emit("send_file_error", new Error("Channel has been closed"), socketId, sendId, fileToSend.file); 601 | } 602 | packet = { 603 | name: fileToSend.file.name, 604 | size: fileToSend.file.size, 605 | sendId: sendId, 606 | type: "__file", 607 | signal: "ask" 608 | }; 609 | channel.send(JSON.stringify(packet)); 610 | }; 611 | 612 | //获得随机字符串来生成文件发送ID 613 | skyrtc.prototype.getRandomString = function() { 614 | return (Math.random() * new Date().getTime()).toString(36).toUpperCase().replace(/\./g, '-'); 615 | }; 616 | 617 | /***********************接收者部分***********************/ 618 | 619 | 620 | //接收到文件碎片 621 | skyrtc.prototype.receiveFileChunk = function(data, sendId, socketId, last, percent) { 622 | var that = this, 623 | fileInfo = that.receiveFiles[sendId]; 624 | if (!fileInfo.data) { 625 | fileInfo.state = "receive"; 626 | fileInfo.data = ""; 627 | } 628 | fileInfo.data = fileInfo.data || ""; 629 | fileInfo.data += data; 630 | if (last) { 631 | fileInfo.state = "end"; 632 | that.getTransferedFile(sendId); 633 | } else { 634 | that.emit("receive_file_chunk", sendId, socketId, fileInfo.name, percent); 635 | } 636 | }; 637 | 638 | //接收到所有文件碎片后将其组合成一个完整的文件并自动下载 639 | skyrtc.prototype.getTransferedFile = function(sendId) { 640 | var that = this, 641 | fileInfo = that.receiveFiles[sendId], 642 | hyperlink = document.createElement("a"), 643 | mouseEvent = new MouseEvent('click', { 644 | view: window, 645 | bubbles: true, 646 | cancelable: true 647 | }); 648 | hyperlink.href = fileInfo.data; 649 | hyperlink.target = '_blank'; 650 | hyperlink.download = fileInfo.name || dataURL; 651 | 652 | hyperlink.dispatchEvent(mouseEvent); 653 | (window.URL || window.webkitURL).revokeObjectURL(hyperlink.href); 654 | that.emit("receive_file", sendId, fileInfo.socketId, fileInfo.name); 655 | that.cleanReceiveFile(sendId); 656 | }; 657 | 658 | //接收到发送文件请求后记录文件信息 659 | skyrtc.prototype.receiveFileAsk = function(sendId, fileName, fileSize, socketId) { 660 | var that = this; 661 | that.receiveFiles[sendId] = { 662 | socketId: socketId, 663 | state: "ask", 664 | name: fileName, 665 | size: fileSize 666 | }; 667 | that.emit("receive_file_ask", sendId, socketId, fileName, fileSize); 668 | }; 669 | 670 | //发送同意接收文件信令 671 | skyrtc.prototype.sendFileAccept = function(sendId) { 672 | var that = this, 673 | fileInfo = that.receiveFiles[sendId], 674 | channel = that.dataChannels[fileInfo.socketId], 675 | packet; 676 | if (!channel) { 677 | that.emit("receive_file_error", new Error("Channel has been destoried"), sendId, socketId); 678 | } 679 | packet = { 680 | type: "__file", 681 | signal: "accept", 682 | sendId: sendId 683 | }; 684 | channel.send(JSON.stringify(packet)); 685 | }; 686 | 687 | //发送拒绝接受文件信令 688 | skyrtc.prototype.sendFileRefuse = function(sendId) { 689 | var that = this, 690 | fileInfo = that.receiveFiles[sendId], 691 | channel = that.dataChannels[fileInfo.socketId], 692 | packet; 693 | if (!channel) { 694 | that.emit("receive_file_error", new Error("Channel has been destoried"), sendId, socketId); 695 | } 696 | packet = { 697 | type: "__file", 698 | signal: "refuse", 699 | sendId: sendId 700 | }; 701 | channel.send(JSON.stringify(packet)); 702 | that.cleanReceiveFile(sendId); 703 | }; 704 | 705 | //清除接受文件缓存 706 | skyrtc.prototype.cleanReceiveFile = function(sendId) { 707 | var that = this; 708 | delete that.receiveFiles[sendId]; 709 | }; 710 | 711 | return new skyrtc(); 712 | }; --------------------------------------------------------------------------------