Commit ab06eee4 by fengfan

1

parents
File added
node_modules
node_modules1
OutAPP
\ No newline at end of file
var fs = require('fs');
var options = {
key:fs.readFileSync('./keys/server.key'),
cert:fs.readFileSync('./keys/server.crt')
}
// options
const express = require('express');
const app = express();
var https = require('https');
var httpsServer = https.createServer(options,app);
const wsInstance = require('express-ws')(app,httpsServer);
const { v4: uuidv4 } = require('uuid');
var roomId = null
let createWindowId = []
let connectId = []
app.use(express.static('www'))
app.get('/createRoomId',function(req,res){
res.send({id: uuidv4()})
})
app.get("/createWindow", async function (req, res) {
roomId = req.query.id;
res.send({msg:`createWindow ${req.query.id} OK`});
createWindowId.indexOf(req.query.id) == -1 ? createWindowId.push(req.query.id) : ''
});
// 监听页面关闭,停电,退出连接,连接断开等操作逻辑
try{
app.get("/connect", async function (req, res) {
res.send({msg:`connect ${req.query.queryId} OK`});
connectId.indexOf(req.query.queryId) == -1 ? connectId.push(req.query.queryId) : ''
});
} catch(err) {
console.log(err);
}
// 每隔十秒做一次连接状态的判断
let timer = null
const connect = ()=>{
timer = setInterval(()=>{
let closeRoomID = []
createWindowId.forEach((o,i)=>{
if (connectId.indexOf(o) == -1) {
closeRoomID = closeRoomID.concat(createWindowId.splice(i,1))
}
})
connectId = []
if (closeRoomID.length > 0) {
wsInstance.getWss().clients.forEach(server => {
// 给electron主进程发消息
server.send(JSON.stringify({closeRoomID}));
});
}
}, 1000 * 10)
}
try {
connect();
}catch{
clearInterval(timer)
connect();
};
try {
app.ws('/', ws => {
ws.on('message', data => {
// 未做业务处理,收到消息后直接广播
wsInstance.getWss().clients.forEach(server => {
// 给electron主进程发消息
if (roomId) {
server.send(data);
}
});
});
});
} catch(err) {
console.log(err);
}
try {
app.ws('/control', ws => {
ws.on('message', data => {
// 未做业务处理,收到消息后直接广播
let receiveData = JSON.parse(data)
wsInstance.getWss().clients.forEach(server => {
// 给本地node客户端发消息
server.send(data);
});
});
});
} catch (err) {
console.log(err);
}
httpsServer.on('close', () => {
console.log('HTTPS服务器关闭,连接断开');
});
httpsServer.listen(8002, ()=>{
console.log('server 8002 ok');
});
-----BEGIN CERTIFICATE-----
MIICDDCCAXUCFAh5ExqPLNUFfamGnrwhUR9x0XNHMA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwODA5MDUwNTA1WhcNMjMwOTA4MDUw
NTA1WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQC+Wfgm+ECXPCcYn5BqASa/rdh3EcTWMmqsL73mHN3R0iMqGR8KduNn
O1GAiq+peBDgMqf5ZPJO1SRCnRmoGiqNJChZaj1uIskyrfJNvJh7s3cJDK8QdWfQ
E5aF/4Bs7ICRKRhfD9WLEIjhKfkVy1Y85bpkHUDS8b3nnmfWx8As3wIDAQABMA0G
CSqGSIb3DQEBCwUAA4GBAFwVJLv3egTZkTvTHPjU1gF46yF3rLo6YbGdCvciJL6L
0t/lLYg88CTCMb+m3GZtBn/5LX9Z6GG7gBHTzpd1JYxfssORDsNQnqd5uKymzXLu
e0H7nJCn/ODHZXipIhvhZ3oTYvkxP2gqvmCH8bAn517/lgsavQraxLKBWAkN1aXP
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE REQUEST-----
MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQC+Wfgm+ECXPCcYn5BqASa/rdh3EcTWMmqsL73mHN3R0iMq
GR8KduNnO1GAiq+peBDgMqf5ZPJO1SRCnRmoGiqNJChZaj1uIskyrfJNvJh7s3cJ
DK8QdWfQE5aF/4Bs7ICRKRhfD9WLEIjhKfkVy1Y85bpkHUDS8b3nnmfWx8As3wID
AQABoAAwDQYJKoZIhvcNAQELBQADgYEAncpdxUGY+WOqnqhtkkhVQrdNv+YZnMAS
DFGqazJJwoTWZoj9f1PHgnHQbGi//1t8RdgwQWUH/1tIFwkDEkc+4QyV2/JymDCn
aMdTg/LCDNrNs8CDX3jl3OWa9Bcukkmi5wn2zqFPyRaZDoebhRYHFxWgJTo6idey
OqTNp1+w+y4=
-----END CERTIFICATE REQUEST-----
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC+Wfgm+ECXPCcYn5BqASa/rdh3EcTWMmqsL73mHN3R0iMqGR8K
duNnO1GAiq+peBDgMqf5ZPJO1SRCnRmoGiqNJChZaj1uIskyrfJNvJh7s3cJDK8Q
dWfQE5aF/4Bs7ICRKRhfD9WLEIjhKfkVy1Y85bpkHUDS8b3nnmfWx8As3wIDAQAB
AoGAJ14QFm8tXJnSNCN8CJBoZSgdJrMEFJCkwyu0iIzby3M8Vo6PfuiLq2P3PTBx
mAj+5H9VghzAiFFIiIZZMg0HU4If53tu0wkzwWLgT8G5L1vTrN4a5JlPo3qDG135
LOIDtRo5Rk6Lmt+bvEpNvteKmk06o93gTioJNGtpDw256yECQQDfd37MSqs52lOO
SKvjxhzXQPkG7rSHiaOHVhi+c3OxaNGrPtLuzDpyYo1m2xKjqZLt9pSFKIyLIiJ7
ybEQOUU3AkEA2hBH1ibEuPtdKpXThSumWU1H7vCfPXR3EyJvk2O55dLmtAMDnT3e
5INBU8tNNQT8BJctmx0SVzVQ7aEpGhApmQJBAJrrgrIqSc5Pt4F6i//ahoAMARh3
QBx+pSnb3EecTmZ9nm2znhQC7boC7LiTw7ZGhyAFxC0UUWrbfUCIMQJoUb0CQQDT
8q0P/gCJV3AXmybLMkDuXYoEOQZdwhyWkYrnNDAUrKel4aQaqm9Bpj3RxzGUzfJy
N8qREHGqJ90wCsubVIhJAkBninfFGu6h0upKbPjLhYX5utTFp4EyrUWAFECShMjW
QJXhGct1H6bQF7LAM2U0ivfAGcM4WqjAaDMIygq5NTg8
-----END RSA PRIVATE KEY-----
298529D2B067A5411E29374EA4E4150F83C26AA7
-----BEGIN CERTIFICATE-----
MIICDDCCAXUCFCmFKdKwZ6VBHik3TqTkFQ+DwmqnMA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwODA5MDUwNzAzWhcNMjMwOTA4MDUw
NzAzWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDW8XA9Woje8S9pimlv/01CQyS7MdgWsOWdxgy7D9yzMBa6AxYE/8Ev
X84XV27x51OdfMc/ufZgltwrdFR0FtEOblXyT2EZkTt3sFDogvHN6AqmCcozMYkP
o7IaccY+BDmkuUOWDaOMLJoQezdsgXhVjvUqWYYxFXhi9NGwpeeYDQIDAQABMA0G
CSqGSIb3DQEBCwUAA4GBAGCf8CKmfMvSlILwATKok0bfqNVF5WYRDmLZWlO2tTsz
STmVUuKci+CMS8Kr3iQbXntm8uS2AIuEupNBgiCz1gFQSiCaNdf96qqunPToQ/uV
+g4aAkFpRh5gjNWp7sCiDzC3zFzJKFWYcFN6uhP7CN1jnhfQs7GbT+2V9cTkDYsS
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE REQUEST-----
MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDW8XA9Woje8S9pimlv/01CQyS7MdgWsOWdxgy7D9yzMBa6
AxYE/8EvX84XV27x51OdfMc/ufZgltwrdFR0FtEOblXyT2EZkTt3sFDogvHN6Aqm
CcozMYkPo7IaccY+BDmkuUOWDaOMLJoQezdsgXhVjvUqWYYxFXhi9NGwpeeYDQID
AQABoAAwDQYJKoZIhvcNAQELBQADgYEAUudrvydJOXOG8IfGnSZT5umH3DBzhMEv
QKrtBoKY46VewWm/goGob+w2Z4S1gaN5AbhdCswofF/YWbssdCi01v8LtrdGPYmi
Yds0g/zYluenDCsM9/sIpAd8MNeZWTaxy7yYClEG/Hl1Xx1g9W+04QbRAlLLZLEa
Jhnurd2pSl8=
-----END CERTIFICATE REQUEST-----
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDW8XA9Woje8S9pimlv/01CQyS7MdgWsOWdxgy7D9yzMBa6AxYE
/8EvX84XV27x51OdfMc/ufZgltwrdFR0FtEOblXyT2EZkTt3sFDogvHN6AqmCcoz
MYkPo7IaccY+BDmkuUOWDaOMLJoQezdsgXhVjvUqWYYxFXhi9NGwpeeYDQIDAQAB
AoGAPWPL6NoYyYQdRImRv7ktOcWvkf+Udjaj/kH4lNzc7hjIBOyot1jMtlkopaYF
O/YT/aELz3mvkrrDu22ISXfS0URPUk+zHwQ+rbkvDzWnXhR4dpBVNhvjF7eK2BM3
1DciYf6btPcvDbra24SDPKDTgzIMa4LaFSAn44kyCKtNvkECQQDvq3mZshRq70nr
7QBDhiHuwGoyBo9akL1B1/sYpW0Bezxq3tH0ISdE0d5NKuh/1vpfTNVmpwki1qmn
rArDvgLZAkEA5ZaowW0Pt3dJwGLOxaK12b5/qxJpXt9Thbth7ORtkXAiUqMxMzGi
c6G0avRY14gU1t+zdulw81s9DDBRUOIWVQJBAIqQdxNXE9rnUbOnKFnhuMiSUlNt
23+e4uyodDHBoo4Ss9GN1ZDxggSV2ZJ/1j/jCIoEn/RjajLMLPYVz3tv5bECQQDJ
IdG5kpgAPIrp9iX7Um5J0e/6qOiS6sc7E0vYws2SalBHYRxLIyKUdoeeY24pc9f9
3oBUnnILYFT9Bp9WYpr5AkEAj9wDCtYDayWTFsAUajdL6J0CtDXC4aLth338QCHi
1Rsckhe8Jxlz7CgMIuzJDl4DlW/Zfq8wQqX7x7d1SmEJ/Q==
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDW8XA9Woje8S9pimlv/01CQyS7
MdgWsOWdxgy7D9yzMBa6AxYE/8EvX84XV27x51OdfMc/ufZgltwrdFR0FtEOblXy
T2EZkTt3sFDogvHN6AqmCcozMYkPo7IaccY+BDmkuUOWDaOMLJoQezdsgXhVjvUq
WYYxFXhi9NGwpeeYDQIDAQAB
-----END PUBLIC KEY-----
{
"name": "nodeserver",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"express-ws": "^5.0.2",
"net": "^1.0.2",
"nodejs-websocket": "^1.7.2",
"uuid": "^9.0.0",
"websocket": "^1.0.34"
}
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
<title>Receiver</title>
</head>
<body style="margin:0px;box-sizing: border-box;">
<video autoplay muted id="remote" style="cursor: none;"></video>
<div class="timeMs"></div>
</body>
<style>
.timeMs{
position: fixed;
top: 10px;
left: 10px;
color: palegreen;
font-size: 24px;
}
</style>
<script>
main();
let roomId;
let timer = null;
let lastTime = null;
let currentTime = new Date().getTime();
async function main() {
roomId = await createRoomId();
let res = await openRoomByid(roomId.id);
window.roomId = roomId.id
initWs(roomId.id);
initCtrWs(roomId.id);
timer = setInterval(()=>{
connects(roomId.id);
}, 1000)
}
function createRoomId() {
return fetch("https://" + location.host + "/createRoomId").then((res) =>
res.json()
);
}
function openRoomByid(id) {
return fetch("https://" + location.host + "/createWindow?id=" + id).then((res) =>
res.json()
);
}
function connects(id) {
return fetch("https://" + location.host + "/connect?queryId=" + id).catch(()=>{
clearInterval(timer)
});
}
function initCtrWs(id) {
const protocol = location.protocol == "https:" ? "wss://" : "ws://";
window.socketControl = new WebSocket(protocol + location.host + '/control');
// console.log(protocol + location.host + '/control');
socketControl.onopen = function () {
// 先到先得发送控制指令
socketControl.send(JSON.stringify({ activeControlRoomId: id }));
};
}
function initWs(id) {
const protocol = location.protocol == "https:" ? "wss://" : "ws://";
window.socket = new WebSocket(protocol + location.host + '/');
socket.onopen = function () {
console.log('socket ok')
socket.send(JSON.stringify({roomId: roomId.id}));
var cfg = {
iceTransportPolicy: "all", // set to "relay" to force TURN.
iceServers: [
// { urls: "stun:stun.l.google.com:19302" },
{ urls: "turn:" + location.hostname, username:"username", credential:"password" }
]
};
let buddy = new RTCPeerConnection(cfg);
let timeMs = null
buddy.ondatachannel = (ev) => {
const dataChannel = ev.channel;
dataChannel.onopen = () => {
// 发送消息
dataChannel.send('发送消息');
timeMs = new Date().getTime();
};
// 设置接收数据的事件处理程序
dataChannel.onmessage = () => {
document.querySelector('.timeMs').innerHTML = (new Date().getTime() - timeMs) / 2 + 'ms'
setTimeout(()=>{
dataChannel.send('接收到消息');
timeMs = new Date().getTime();
},2000)
};
dataChannel.onclose = () => {
console.log('Data Channel closed');
// 在这里处理数据通道关闭的逻辑
};
}
// 如果接收到对方的视频
socket.onmessage = function (e) {
// console.log(JSON.parse(e.data));
const { type, sdp, iceCandidate, roomId } = JSON.parse(e.data);
if (roomId == window.roomId) {
switch (type) {
case "offer":
buddy.setRemoteDescription(
new RTCSessionDescription({ type, sdp })
);
buddy.createAnswer().then((answer) => {
answer.roomId = window.roomId
buddy.setLocalDescription(answer);
let _answer = JSON.parse(JSON.stringify(answer))
_answer.roomId = window.roomId
socket.send(JSON.stringify(_answer));
});
break;
case "offer_ice":
buddy.addIceCandidate(iceCandidate);
break;
default:
break;
}
}
};
buddy.ontrack = function (e) {
remote.srcObject = e.streams[0];
};
buddy.onicecandidate = function (e) {
if (e.candidate) {
socket.send(
JSON.stringify({
roomId: window.roomId,
type: "answer_ice",
iceCandidate: e.candidate,
})
);
}
};
};
socket.onclose = function () {
closeRobot();
// socket停止时一起关闭createWindow
// socket.send(JSON.stringify({windowClose: true, roomId: roomId.id}));
socketControl.send(JSON.stringify({ roomId: roomId.id, activeControlRoomId: roomId.id, controlClose: true }));
console.log("Socket Close");
};
}
const remoteVideo = document.querySelector('#remote')
// 鼠标移动事件
const mousemoveListener = (e) => {
e.preventDefault()
let data = {
type: 'mouse',
move: 'true',
offsetX: e.offsetX,
offsetY: e.offsetY,
video: {
// width: remoteVideo.getBoundingClientRect().width,
// height: remoteVideo.getBoundingClientRect().height
width: 1298,
height: 812
},
screen: {
// width: screen.width,
// height: screen.height
width: 1440,
height: 900
}
}
console.log(data);
socketControl.send(JSON.stringify({activeControlRoomId: roomId.id, data: data }))
}
var flagMouse = false
const mousedownListener = (e) => {
e.preventDefault()
let data = {
type: 'mouse',
click: 'true',
mouseDrag: 'down',
offsetX: e.offsetX,
offsetY: e.offsetY,
video: {
// width: remoteVideo.getBoundingClientRect().width,
// height: remoteVideo.getBoundingClientRect().height
width: 1298,
height: 812
},
screen: {
// width: screen.width,
// height: screen.height
width: 1440,
height: 900
}
}
// if(flagMouse){
// return;
// }
// flagMouse=true;
console.log(data);
socketControl.send(JSON.stringify({activeControlRoomId: roomId.id, data: data }))
}
// 防止鼠标事件一直执行
const mouseupListener = (e) => {
e.preventDefault()
flagMouse = false
let data = {
type: 'mouse',
click: 'true',
mouseDrag: 'up',
offsetX: e.offsetX,
offsetY: e.offsetY,
video: {
// width: remoteVideo.getBoundingClientRect().width,
// height: remoteVideo.getBoundingClientRect().height
width: 1298,
height: 812
},
screen: {
// width: screen.width,
// height: screen.height
width: 1440,
height: 900
}
}
socketControl.send(JSON.stringify({activeControlRoomId: roomId.id, data: data }))
}
// 鼠标滚轮事件
const mousewheelListener = (e) => {
e.stopPropagation()
let data = {
type: 'mouse',
wheel: 'true',
wheelData: e.wheelDelta
}
console.log(data);
socketControl.send(JSON.stringify({activeControlRoomId: roomId.id, data: data}))
}
// 防止键盘事件一直执行
var flagKey = false
const keydownListener = (e) => {
e.preventDefault()
let data = {
type: 'key',
code: e.code,
keyCode: e.keyCode,
shift: e.shiftKey,
meta: e.metaKey,
alt: e.altKey
}
if(flagKey){
return;
}
flagKey=true;
socketControl.send(JSON.stringify({activeControlRoomId: roomId.id, data: data}))
}
const setRobot = () => {
window.addEventListener('mousedown', mousedownListener)
window.addEventListener('mouseup', mouseupListener)
window.addEventListener('mousemove', throttle(mousemoveListener, 50))
window.addEventListener('mousewheel', mousewheelListener)
window.addEventListener('keydown', keydownListener)
window.addEventListener('keyup', ()=>flagKey = false)
}
setRobot()
const closeRobot = () => {
window.removeEventListener('mouseup', mouseupListener)
window.removeEventListener('mousedown', mousedownListener)
window.removeEventListener('mousemove', mousemoveListener)
window.removeEventListener('mousewheel', mousewheelListener)
window.removeEventListener('keydown', keydownListener)
window.removeEventListener('keyup', keydownListener)
}
window.onload = function() {
clearInterval(timer)
}
function throttle (fn, delay) {
// 利用闭包保存时间
let prev = Date.now()
return function () {
let context = this
let arg = arguments
let now = Date.now()
if (now - prev >= delay) {
fn.apply(context, arg)
prev = Date.now()
}
}
}
</script>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
<title>Send</title>
</head>
<body>
<video autoplay id="remote"></video>
<script>
const protocol = location.protocol == 'https:' ? 'wss://' : 'ws://'
let wsid = location.href.split('?id=')[1]
window.roomId = wsid
// console.log(protocol + location.host + '/' + wsid);
const socket = new WebSocket(protocol + location.host + '/');
var cfg = {
iceTransportPolicy: "all", // set to "relay" to force TURN.
iceServers: [
// { urls: "stun:stun.l.google.com:19302" },
{ urls: "turn:" + location.hostname, username:"username", credential:"password" }
]
};
let peer = new RTCPeerConnection(cfg)
// 创建数据通道
const dataChannel = peer.createDataChannel('myDataChannel');
dataChannel.onmessage = (event) => {
// console.log(event.data);
dataChannel.send('接收到消息立即发送');
}
socket.onopen = function () {
console.log("Socket Success ")
const stream = navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'screen',
maxWidth: 3840,
maxHeight: 1080,
maxFrameRate: 25
}
}
})
stream.then((res) => {
console.log(res);
res.getTracks().forEach(track => {
peer.addTrack(track, res);
})
peer.createOffer().then(offer => {
peer.setLocalDescription(offer);
let _offer = JSON.parse(JSON.stringify(offer))
_offer.roomId = window.roomId
socket.send(JSON.stringify(_offer));
})
});
peer.ontrack = function (e) {
remote.srcObject = e.streams[0]
}
peer.onicecandidate = function (e) {
if (e.candidate) {
socket.send(JSON.stringify({
roomId:window.roomId,
type: "offer_ice",
iceCandidate: e.candidate
}))
}
}
// 如果接收到对方的视频
socket.onmessage = function (e) {
// console.log(JSON.parse(e.data));
// console.log(window.roomId);
const { type, sdp, iceCandidate, roomId } = JSON.parse(e.data)
if (roomId == window.roomId) {
switch (type) {
case "answer":
peer.setRemoteDescription(
new RTCSessionDescription({ type, sdp })
)
break;
case "answer_ice":
peer.addIceCandidate(iceCandidate)
break;
case 'roomId':
case "mouse":
case "key":
default:
break;
}
}
}
}
</script>
</body>
</html>
\ No newline at end of file
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment