Не могу установить соединение с собеседниками WebRTC. Собеседники не могут подключиться
Возникла проблема с установлением аудиовызовов между пользователями. На Android есть приложение, в котором аудиосвязь между пользователями работает идеально. Однако при попытке позвонить из веб-браузера на Android возникает ошибка подключения. Вот мой HTML-код, который я использую:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AudioCall</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.5.0/socket.io.js"></script>
</head>
<body>
<h1>AudioCall</h1>
<label for="myId">you ID:</label>
<input type="text" id="myId" value="243"><br><br>
<label for="recipientId">ID users for:</label>
<input type="text" id="recipientId" value="3039"><br><br>
<button id="callButton">Call</button>
<button id="endCallButton">End Call</button>
<script>
const socket = io('https://srv1-node-call-jakarta.dkon.app', {
query: { token: "7d3d3b6c2d3683bf25bbb51533ec6dab" }
});
let localStream;
let peerConnection;
let DkonCalls =socket.id;
let globalSocketId;
let pizda;
const configuration = {
iceServers: [
{ urls: 'stun:stun.cloudflare.com:3478' },
{
urls: "turn:global.relay.metered.ca:80",
username: "688db195aa1c5ae4555d3fb5",
credential: "x9XGuSBKLsedMYoe",
},
{
urls: "turn:global.relay.metered.ca:443",
username: "688db195aa1c5ae4555d3fb5",
credential: "x9XGuSBKLsedMYoe",
},
]
};
socket.on('connect', () => {
const myId = document.getElementById('myId').value;
socket.emit('socket_user_connect', { connectedId: myId, connected: true, userToken: "7d3d3b6c2d3683bf25bbb51533ec6dab", socketId: socket.id });
console.log(`Подключен с сокет ID: ${socket.id}`);
});
document.getElementById('callButton').addEventListener('click', () => {
if (peerConnection) {
console.log("Уже есть активный вызов. Пожалуйста, завершите его перед началом нового.");
return;
}
const myId = document.getElementById('myId').value;
const recipientId = document.getElementById('recipientId').value;
console.log(`Попытка позвонить пользователю с ID: ${recipientId} от пользователя с ID: ${myId}`);
socket.emit('socket_call_user_ping', { recipientId: recipientId }, (data) => {
if (data.socketId) {
console.log(`Пользователь с ID ${recipientId} доступен. Socket ID: ${data.socketId}`);
globalSocketId = data.socketId;
socket.emit('make_new_call', {
to: data.socketId,
from: socket.id,
callerID: myId,
isAudioCall: true
});
} else {
console.log(`Пользователь с ID ${recipientId} не доступен`);
}
});
});
socket.on('receive_new_call', (data) => {
const { callerID, from } = data;
console.dir(data);
pizda = data.from;
pizdato = data.to;
console.log('PP',pizdato);
console.log(`Входящий звонок от пользователя с ID ${callerID}`);
const callDiv = document.createElement('div');
callDiv.innerHTML = `
<p>Входящий звонок от пользователя с ID ${callerID}</p>
<button id="acceptCall">Принять</button>
<button id="rejectCall">Отклонить</button>
`;
document.body.appendChild(callDiv);
document.getElementById('acceptCall').onclick = () => {
console.log(`Вы приняли звонок от пользователя с ID ${callerID}`);
// Отправка сигнала о принятии вызова
socket.emit('accept_new_call', { callerSocketId: from, userSocketId: socket.id });
if (from) {
socket.emit('signaling_server', { to: from, from: socket.id, type:'init', payload:'payload'});
console.dir(data);
} else {
console.error('from is undefined, cannot emit signaling_server');
}
startCall(from);
document.body.removeChild(callDiv);
};
document.getElementById('rejectCall').onclick = () => {
console.log(`Вы отклонили звонок от пользователя с ID ${callerID}`);
socket.emit('reject_new_call', { userSocketId: socket.id, callerSocketId: from, reason: "IGNORED" });
endCall(); // Завершите вызов
document.body.removeChild(callDiv);
};
});
function startCall(callerSocketId) {
console.log(`Начинаем звонок с Caller Socket ID: ${callerSocketId}`);
if (peerConnection) {
peerConnection.close();
peerConnection = null;
}
peerConnection = new RTCPeerConnection(configuration);
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
localStream = stream;
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
peerConnection.onicecandidate = event => {
if (event.candidate) {
console.log('Отправка ICE-кандидата:', event.candidate);
console.log('Отправка ICE-кандидатаssss:', event.candidate.sdpMLineIndex);
socket.emit('signaling_server', { to: globalSocketId, from: socket.id, type:"offer", payload: {type: "offer", sdpMLineIndex: 0, sdpMid: "audio", sdp:event.candidate.candidate} });
socket.emit('ice_candidate', {
candidate: {
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex
},
to: globalSocketId,
from: socket.id
});
console.dir(event);
console.log(socket.id);
}
};
peerConnection.ontrack = event => {
const remoteAudio = document.createElement('audio');
remoteAudio.srcObject = event.streams[0];
remoteAudio.autoplay = true; // Автоматическое воспроизведение
document.body.appendChild(remoteAudio);
console.log('Удаленный поток подключен.');
};
// Создание предложения
peerConnection.createOffer()
.then(offer => {
console.log('Создано предложение:', offer);
return peerConnection.setLocalDescription(offer);
})
.then(() => {
console.log('Отправка предложения вызова:', peerConnection.localDescription);
// console.dir(userSocketId);
socket.emit('call_answer', {
sdp: peerConnection.localDescription.sdp,
to: globalSocketId,
type: peerConnection.localDescription.type// peerConnection.candidate
});
socket.emit('signaling_server', { to: globalSocketId, from: socket.id, type:peerConnection.localDescription.type, payload:peerConnection.localDescription.sdp});
console.log('Оdfggrа:', globalSocketId);
console.log('candidatess:', peerConnection.localDescription.sdp);
})
.catch(error => {
console.error('Ошибка при создании предложения:', error);
});
})
.catch(error => {
console.error('Ошибка доступа к аудиоустройству:', error);
});
}
// Обработка предложения вызова
socket.on('call_offer', (data) => {
const offer = data.offer;
const callerSocketId = data.from;
console.dir(data);
console.log(`Получено предложение вызова от ${callerSocketId}:`, offer);
console.dir(data);
if (!peerConnection) {
peerConnection = new RTCPeerConnection(configuration);
}
peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
.then(() => {
console.log('Удаленное описание установлено.');
return peerConnection.createAnswer();
})
.then(answer => {
return peerConnection.setLocalDescription(answer);
})
.then(() => {
console.log('Отправка ответа на вызов:', peerConnection.localDescription);
socket.emit('call_answer', {
answer: peerConnection.localDescription,
to: callerSocketId
});
})
.catch(error => {
console.error('Ошибка при обработке предложения вызова:', error);
});
console.dir(data);
});
socket.on('ice_candidate', (data) => {
const candidate = data.candidate;
console.log('Получен ICE-кандидат:', candidate); // Логирование кандидата для отладки
// Проверка на наличие необходимых полей
if (candidate && candidate.sdpMid !== null && candidate.sdpMLineIndex !== null) {
peerConnection.addIceCandidate(new RTCIceCandidate(candidate))
.then(() => {
console.log('ICE-кандидат добавлен успешно');
})
.catch(error => {
console.error('Ошибка при добавлении ICE-кандидата:', error);
});
} else {
console.error('Получен некорректный ICE-кандидат:', candidate);
}
});
socket.on('signaling_server', (data) => {
console.log('Получено сообщение:', data);
console.dir(data);
console.error('signaling_server Pl',data);
// Проверка на наличие необходимых полей
if (data.type === 'offer') {
// Обработка предложения
const offer = data.payload;
console.log('Получено предложение:', offer);
if (offer.sdp) {
// Установка удаленного описания
peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
.then(() => {
console.log('Удаленное описание установлено.');
// Создание ответа на предложение
return peerConnection.createAnswer();
})
.then(answer => {
return peerConnection.setLocalDescription(answer);
})
.then(() => {
console.log('Ответ на предложение отправлен:', peerConnection.localDescription);
socket.emit('call_answer', {
answer: peerConnection.localDescription,
to: data.from // Отправка ответа обратно
});
})
.catch(error => {
console.error('Ошибка при обработке предложения:', error);
});
}
} else if (data.type === 'candidate') {
// Обработка ICE-кандидата
const candidate = data.payload;
console.log('Получен ICE-кандидат:', candidate);
if (candidate && candidate.sdpMid !== null && candidate.sdpMLineIndex !== null) {
peerConnection.addIceCandidate(new RTCIceCandidate({
candidate: candidate.candidate,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex
}))
.then(() => {
console.log('ICE-кандидат добавлен успешно');
})
.catch(error => {
console.error('Ошибка при добавлении ICE-кандидата:', error);
});
} else {
console.error('Получен некорректный ICE-кандидат:', candidate);
}
} else if (data.type === 'init') {
// Обработка типа "init"
console.log('Получено сообщение типа "init":', data);
// Отправка сообщения типа "init" обратно
console.dir(data);
socket.emit('signaling_server', {
type: 'init',
from: socket.id,//socket.id,// Укажите, от кого отправляется сообщение
to: data.from
});
// console.error('ssss:', data.from);
console.dir(data);
console.log('Полdfddd":', pizda);
} else {
console.error('Неизвестный тип сообщения:', data.type);
}
});
socket.on('ice_candidate', (data) => {
const candidate = data.candidate;
console.log('Получен ICE-кандидат:', candidate); // Логирование кандидата для отладки
// Проверка на наличие необходимых полей
if (candidate && candidate.sdpMid !== null && candidate.sdpMLineIndex !== null) {
peerConnection.addIceCandidate(new RTCIceCandidate(candidate))
.then(() => {
console.log('ICE-кандидат добавлен успешно');
})
.catch(error => {
console.error('Ошибка при добавлении ICE-кандидата:', error);
});
} else {
console.error('Получен некорректный ICE-кандидат:', candidate);
}
});
// Функция для завершения вызова
function endCall() {
if (peerConnection) {
socket.emit('hang_up_call', { callerSocketId: socket.id });
peerConnection.close();
peerConnection = null;
console.log('Звонок завершен.');
}
}
// Обработка завершения звонка
socket.on('hang_up_call', (data) => {
console.log(`Получено событие завершения вызова от ${data.callerSocketId}`);
if (peerConnection) {
peerConnection.close();
peerConnection = null;
console.log('Соединение закрыто.');
console.dir(data);
}
});
// Обработка отклонения звонка
socket.on('reject_new_call', (data) => {
// const { callerSocketId, rejectedBy } = data;
console.log(`Звонок отклонен пользователем с ID: ${rejectedBy} от пользователя с ID: ${callerSocketId}`);
if (peerConnection) {
peerConnection.close();
peerConnection = null;
console.log('Соединение закрыто после отклонения вызова.');
}
});
// Обработка принятия звонка
socket.on('accept_new_call', (data) => {
// const { callerID, from } = data;
const { callerID, from } = data;
console.log(`Звонок принят пользователем с ID: ${data.callerSocketId}`);
socket.emit('signaling_server', { to: data.userSocketId, from: data.callerSocketId, type:'init', payload:'payload'});
startCall(data.callerSocketId);
});
// Обработка завершения вызова по кнопке
document.getElementById('endCallButton').addEventListener('click', () => {
endCall();
});
</script>
</body>
</html>
My WebRTC server
app.js
/**
* Created by abderrahimelimame on 9/24/16.
*/
var app = require('express')();
var server = require('http').Server(app);
//var server = require('https').Server(app);
var users = require('./users.js')();
var pingInterval = 25000;
var Socket = require('socket.io');
var io = Socket(server, {'pingInterval': pingInterval, 'pingTimeout': 60000});
/**
* You can control those variables as you want
*/
var serverPort = 44333;
var app_key_secret = "7d3d3b6c2d3683bf25bbb51533ec6dab";
var debugging_mode = true;
/**
* server listener
*/
var port = process.env.PORT || serverPort;
server.listen(port, function () {
console.log('Server listening at port %d', port);
});
/**
* this for check if the user connect from the app
*/
io.use(function (socket, next) {
var token = socket.handshake.query.token;
if (token === app_key_secret) {
if (debugging_mode) {
console.log("token valid authorized", token);
}
next();
} else {
if (debugging_mode) {
console.log("not a valid token Unauthorized to access ");
}
next(new Error("not valid token"));
}
}
);
/**
* Socket.io event handling
*/
require('./socketHandler.js')(io, users, debugging_mode, pingInterval);
socketHandler.js
/**
* Created by abderrahimelimame on 4/7/17.
*/
module.exports = function(io, users, debugging_mode, pingInterval) {
io.on('connection', function(socket) {
/*****************************************************************************************************************************************
********************************************* Users Connection Methods *****************************************************************
*****************************************************************************************************************************************/
/**
* Ping/Pong methods
* */
socket.on('socket_pong', function(data) {
if (debugging_mode) {
// console.log("Pong received from client ");
}
});
setTimeout(sendHeartbeat, pingInterval);
function sendHeartbeat() {
setTimeout(sendHeartbeat, pingInterval);
io.sockets.emit('socket_ping', {
beat: 1
});
}
/**
* method to save user as connected
*/
socket.on('socket_user_connect', function(data) {
if (debugging_mode) {
console.log("the user with id " + data.connectedId + " connected " + +data.connected + " token " + data.userToken + "socket.id " + socket.id);
}
if (data.connectedId != null && data.connectedId != 0) {
var user = users.getUser(data.connectedId);
if (user != null) {
users.updateUser(data.connectedId, data.connected, socket.id);
} else {
users.addUser(data.connectedId, data.connected, socket.id);
}
io.sockets.emit('socket_user_connect', {
connectedId: data.connectedId,
connected: true,
socketId: data.socketId
});
}
});
/**
* method if a user is disconnect from sockets
* and then remove him from array of current users connected
*/
socket.on('disconnect', function() {
var usersArray = users.getUsers();
if (usersArray.length != 0) {
for (var i = 0; i < usersArray.length; i++) {
var user = usersArray[i];
if (user != null) {
if (user.socketID == socket.id) {
if (debugging_mode) {
console.log("the user with id " + user.ID + " is disconnect 1 ");
}
io.sockets.emit('socket_user_connect', {
connectedId: user.ID,
connected: false,
socketId: user.socketID
});
users.removeUser(user.ID);
if (debugging_mode) {
console.log("the users list size disconnect " + usersArray.length);
}
break;
}
} else {
if (debugging_mode) {
console.log("the user is null disconnect ");
}
}
}
}
});
socket.on('socket_user_disconnect', function(data) {
if (data.connectedId != null && data.connectedId != 0) {
if (debugging_mode) {
console.log("the user with id " + data.connectedId + " is disconnect 2");
}
var user = users.getUserBySocketID(data.socketId);
if (user != null) {
io.sockets.emit('socket_user_connect', {
connectedId: user.ID,
connected: false,
socketId: user.socketID
});
users.removeUser(user.ID);
}
}
});
/**
* method to check if recipient is Online
*/
socket.on('socket_is_online', function(data) {
io.sockets.emit('socket_is_online', {
senderId: data.senderId,
// senderId: "55536890679",
connected: data.connected
});
});
/**
* method to check status last seen
*/
/* socket.on('socket_last_seen', function (data) {
io.sockets.emit('socket_last_seen', {
lastSeen: data.lastSeen,
senderId: data.senderId,
recipientId: data.recipientId
});
});*/
/*****************************************************************************************************************************************
********************************************* Single User Messages Methods *****************************************************************
*****************************************************************************************************************************************/
socket.on('socket_update_register_id', function(data) {
var user = users.getUser(data.recipientId);
if (user != null) {
socket.to(user.socketID).emit('socket_update_register_id', data);
}
});
/*****************************************************************************************************************************************
********************************************* Users Call Methods *****************************************************************
*****************************************************************************************************************************************/
/**
* method to check if user is connected before call him (do a ping and get a callback)
*/
socket.on('socket_call_user_ping', function(data, callback) {
if (debugging_mode)
console.log("socket_call_user_ping called ");
var user = users.getUser(data.recipientId);
var pingedData;
if (user != null) {
console.log("socket id " + user.socketID);
pingedData = {
socketId: user.socketID,
recipientId: data.recipientId,
connected: true
};
callback(pingedData);
} else {
pingedData = {
socketId: null,
recipientId: data.recipientId,
connected: false
};
callback(pingedData);
}
});
/**
* method to check if user is already on users array
*/
socket.on('reset_socket_id', function(data, callback) {
if (debugging_mode)
console.log("reset_socket_id called " + data.userSocketId);
var pingedData = {
userSocketId: data.userSocketId
};
callback(pingedData);
});
/**
* method make the connection between the too peer
*/
socket.on('signaling_server', function(data) {
if (debugging_mode)
console.log("signaling_server called " + data.to);
var socketId = data.to;
/*var user = users.getUserBySocketID(data.to);
if (user != null) {*/
delete data.to;
socket.to(socketId).emit('signaling_server', data);
/*} else {
console.log("user is null signaling_server function ");
//kolo adasnt skergh bach ighiga null nrj3 request bach ndir dialog this person is not available like whatsapp
}*/
});
var makeCall = function(data) {
if (debugging_mode)
console.log("make_new_call function " + data.to);
/* var user = users.getUserBySocketID(data.to);
if (user != null) {*/
socket.to(data.to).emit('receive_new_call', data);
/*} else {
console.log("user is null make_new_call function ");
//kolo adasnt skergh bach ighiga null nrj3 request bach ndir dialog this person is not available like whatsapp
}*/
};
/**
* method to initialize the new call
*/
socket.on('make_new_call', makeCall);
/**
* method to Reject a call
*/
socket.on('reject_new_call', function(data) {
if (debugging_mode)
console.log("reject_new_call function ");
/* var user = users.getUserBySocketID(data.callerSocketId);
if (user != null) {*/
socket.to(data.callerSocketId).emit("reject_new_call", data);
/*} else {
console.log("user is null reject_new_call function ");
}*/
});
/**
* method to Accept a call
*/
socket.on('accept_new_call', function(data) {
if (debugging_mode)
console.log("accept_new_call function ");
/*var user = users.getUserBySocketID(data.callerSocketId);
if (user != null) {
*/
socket.to(data.callerSocketId).emit("accept_new_call", data);
/*} else {
console.log("user is null accept_new_call function ");
}*/
});
/**
* method to HangUp a call
*/
socket.on('hang_up_call', function(data) {
if (debugging_mode)
console.log("hang_up_call function ");
/* var user = users.getUserBySocketID(data.callerSocketId);
if (user != null) {*/
socket.to(data.callerSocketId).emit("hang_up_call", data);
/*} else {
console.log("user is null hang_up_call function ");
}*/
});
});
};
users.js
/**
* Created by abderrahimelimame on 9/24/16.
*/
module.exports = function () {
/**
* available users
* the id value is considered unique (provided by socket.io)
*/
var usersList = [];
/**
* User object
*/
var User = function (id, connected, socketID) {
this.connected = connected;
this.ID = id;
this.socketID = socketID
};
return {
addUser: function (id, connected, socketID) {
var user = new User(id, connected, socketID);
usersList.push(user);
},
removeUser: function (id) {
var index = 0;
while (index < usersList.length && usersList[index].ID != id) {
index++;
}
usersList.splice(index, 1);
},
updateUser: function (id, connected, socketID) {
var user = usersList.find(function (element, i, array) {
return element.ID == id;
});
user.connected = connected;
user.socketID = socketID;
},
getUser: function (id) {
return usersList.find(function (element, i, array) {
return element.ID == id;
});
},
getUserBySocketID: function (socketID) {
return usersList.find(function (element, i, array) {
return element.socketID == socketID;
});
},
getUsers: function () {
return usersList;
}
}
};
Logs
2025-07-12T12:36:00.151 app[e286e922c57228] waw [info] socket_call_user_ping called
2025-07-12T12:36:00.151 app[e286e922c57228] waw [info] socket id LVfqkk-RH4uryHvvAACU
2025-07-12T12:36:00.345 app[e286e922c57228] waw [info] make_new_call function LVfqkk-RH4uryHvvAACU
2025-07-12T12:36:03.383 app[e286e922c57228] waw [info] accept_new_call function
2025-07-12T12:36:03.486 app[e286e922c57228] waw [info] signaling_server called yc8BtNp93oZS-Bh7AACN
2025-07-12T12:36:03.517 app[e286e922c57228] waw [info] signaling_server called yc8BtNp93oZS-Bh7AACN
2025-07-12T12:36:03.578 app[e286e922c57228] waw [info] signaling_server called yc8BtNp93oZS-Bh7AACN
2025-07-12T12:36:03.797 app[e286e922c57228] waw [info] signaling_server called yc8BtNp93oZS-Bh7AACN
2025-07-12T12:36:03.797 app[e286e922c57228] waw [info] signaling_server called yc8BtNp93oZS-Bh7AACN
2025-07-12T12:36:11.341 app[e286e922c57228] waw [info] hang_up_call function
2025-07-12T12:36:14.476 app[e286e922c57228] waw [info] the user with id 3039 is disconnect 1
2025-07-12T12:36:14.476 app[e286e922c57228] waw [info] the users list size disconnect 2
2025-07-12T12:36:15.261 app[e286e922c57228] waw [info] token valid authorized 7d3d3b6c2d3683bf25bbb51533ec6dab
2025-07-12T12:36:15.570 app[e286e922c57228] waw [info] the user with id 3039 connected 1 token d85dc7ebbac3f646429d5e3aa313e783socket.id USZoAOTFmizo8n8lAACX
Тест приложения для Android https://dkon.app/dev/last_version/dkon.apk
Я попытался переставить переменные и изменить логику, но сам сервер я не трогал, так как серверный код нельзя изменить, только HTML-файл.