javascript - Python websockets(许多对等连接)
问题描述
文件:websocket_server.py
import asyncio
import websockets
import json
import ssl
peers = ()
async def on_open(websocket,path):
async for message in websocket:
message = json.loads(message)
if(message["type"]=="register"):
await register(websocket,message["username"])
elif(message["type"]=="offer"):
await send_offer(websocket,message)
elif(message["type"]=="answer"):
await send_answer(websocket,message)
elif(message["type"]=="candidate"):
await send_candidate(websocket,message)
await unregister(websocket)
async def register(websocket,username):
global peers
print(username+" logged in.")
peers = peers + ((websocket,username),)
for peer in peers:
if peer[0] is not websocket:
await websocket.send(json.dumps({"type": "create_peer","username":peer[1]}))
async def send_offer(websocket,message):
global peers
offer_creator = message["from"]
offer_receiver = message["to"]
offer = message["offer"]
print(offer_creator+" creates and sends offer to "+offer_receiver)
for peer in peers:
if(peer[1]==offer_receiver):
await peer[0].send(json.dumps({"type": "offer","username":offer_creator,"offer":offer}))
async def send_answer(websocket,message):
global peers
answer_creator = message["from"]
answer_receiver = message["to"]
answer = message["answer"]
print(answer_creator+" creates and sends answer to "+answer_receiver)
for peer in peers:
if(peer[1]==answer_receiver):
await peer[0].send(json.dumps({"type": "answer","username":answer_creator,"answer":answer}))
async def send_candidate(websocket,message):
global peers
candidate_creator = message["from"]
candidate_receiver = message["to"]
candidate = message["candidate"]
print(candidate_creator+" send candidate packet to "+candidate_receiver)
for peer in peers:
if(peer[1]==candidate_receiver):
await peer[0].send(json.dumps({"type": "candidate","username":candidate_creator,"candidate":candidate}))
async def unregister(websocket):
global peers
for peer_1 in peers:
if(peer_1[0]==websocket):
username = peer_1[1]
print(username+" logged out.")
for peer_2 in peers:
if(peer_2[0] is not websocket):
await peer_2[0].send(json.dumps({"type": "unregister","username":username}))
peers_list = list(peers)
peers_list.remove((websocket,username))
peers = tuple(peers_list)
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context = None
start_server = websockets.serve(on_open, "127.0.0.1", 8080)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
文件 index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Audio Calls</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript" src="main.js"></script>
<style type="text/css">
.video{
border:2px solid black;
position:relative;
}
video{
}
.username{
border-top:2px solid black;
text-align:center;
font-weight:bold;
}
</style>
</head>
<body>
</body>
</html>
文件 main.js
var ws_url = "ws://127.0.0.1:8080"
var ws = new WebSocket(ws_url);
var pcs = [];
var peer_connections = 0;
var constraints = {audio: true,video: true};
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
var local_username;
$(document).ready(function(){
local_username = makeid(5);
ws.onopen = () => ws.send(JSON.stringify({"type":"register","username":local_username}));
})
var signal_in_progress;
ws.onmessage = async function (event) {
var signal = JSON.parse(event.data);
if(signal.type=="create_peer"){
username = signal.username;
create_peer(username);
//console.log("Creating peer for: "+username);
}else if(signal.type=="offer"){
receive_offer(signal);
}else if(signal.type=="answer"){
receive_answer(signal);
//console.log("Receiving answer from peer: "+signal.username)
}else if(signal.type=="candidate"){
receive_candidate(signal);
}else if(signal.type=="unregister"){
unregister(signal);
}
}
async function create_peer(username){
pc = new RTCPeerConnection();
stream = await navigator.mediaDevices.getUserMedia(constraints);
pc.addStream(stream);
pc.onaddstream = function(event) {
make_video_element(event.stream,username)
};
pc.createOffer(function(offer) {
pc.setLocalDescription(offer, function() {
data = {"type":"offer","from":local_username,"to":username,"offer":offer}
ws.send(JSON.stringify(data));
}, fail);
}, fail);
pc.onicecandidate = function(event) {
if (event.candidate) {
data = {"type":"candidate","from":local_username,"to":username,"candidate":event.candidate}
ws.send(JSON.stringify(data));
}
};
pcs[peer_connections] = [username,pc];
peer_connections = peer_connections+1;
}
async function receive_offer(signal){
username = signal.username;
offer = signal.offer;
pc = new RTCPeerConnection();
stream = await navigator.mediaDevices.getUserMedia(constraints);
pc.addStream(stream);
pc.onaddstream = function(event) {
make_video_element(event.stream,username)
};
pc.setRemoteDescription(new RTCSessionDescription(offer), function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer, function() {
data = {"type":"answer","from":local_username,"to":username,"answer":answer}
ws.send(JSON.stringify(data));
}, fail);
}, fail);
}, fail);
pc.onicecandidate = function(event) {
if (event.candidate) {
data = {"type":"candidate","from":local_username,"to":username,"candidate":event.candidate}
ws.send(JSON.stringify(data));
}
};
pcs[peer_connections] = [username,pc];
peer_connections = peer_connections+1;
}
async function receive_answer(signal){
username = signal.username;
answer = signal.answer;
for(var i=0;i<peer_connections;i++){
if(pcs[i][0]==username){
pcs[i][1].setRemoteDescription(new RTCSessionDescription(answer));
}
}
}
async function receive_candidate(signal){
username = signal.username;
candidate = signal.candidate;
for(var i=0;i<peer_connections;i++){
if(pcs[i][0]==username){
pcs[i][1].addIceCandidate(new RTCIceCandidate(candidate));
}
}
}
async function unregister(signal){
username = signal.username;
index = 0
for(var i=0;i<peer_connections;i++){
if(pcs[i][0]==username){
pcs[i][1].close();
document.getElementById(username).style.display = "none";
document.getElementById(username).getElementsByTagName("video")[0].pause();
index = i;
break;
}
}
pcs.splice(index, 1);
peer_connections = peer_connections-1;
}
function make_video_element(stream,username){
var video_container = document.createElement("div");
video_container.id = username;
video_container.classList.add("video");
var video = document.createElement("video");
video.srcObject = stream;
video_container.appendChild(video);
var username_element = document.createElement("div");
username_element.classList.add("username");
username_element.innerHTML = username;
video_container.appendChild(username_element);
document.body.appendChild(video_container);
video.play();
}
function fail(error){
console.log(error);
}
上面的代码适用于一个连接(发送-接收)。但是,如果我打开三个标签,就会出现问题。我不知道问题集中在哪里。你能帮我吗?
解决方案
我发现了错误:
main.js
var ws_url = "ws://127.0.0.1:8080"
var ws = new WebSocket(ws_url);
var pcs = [];
var peer_connections = 0;
var constraints = {audio: true,video: true};
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
var local_username;
$(document).ready(function(){
local_username = makeid(5);
ws.onopen = () => ws.send(JSON.stringify({"type":"register","username":local_username}));
})
var signal_in_progress;
ws.onmessage = async function (event) {
var signal = JSON.parse(event.data);
if(signal.type=="create_peer"){
username = signal.username;
create_peer(username);
}else if(signal.type=="offer"){
receive_offer(signal);
}else if(signal.type=="answer"){
receive_answer(signal);
}else if(signal.type=="candidate"){
receive_candidate(signal);
}else if(signal.type=="unregister"){
unregister(signal);
}
}
async function create_peer(username){
pc = new RTCPeerConnection();
var pc_index = peer_connections;
pcs[pc_index] = [username,pc];
peer_connections = peer_connections+1;
stream = await navigator.mediaDevices.getUserMedia(constraints);
pcs[pc_index][1].addStream(stream);
pcs[pc_index][1].onaddstream = function(event) {
make_video_element(event.stream,username)
};
console.log(pc_index);
pcs[pc_index][1].createOffer(function(offer) {
pcs[pc_index][1].setLocalDescription(offer, function() {
data = {"type":"offer","from":local_username,"to":username,"offer":offer}
console.log("Offer to: "+username);
ws.send(JSON.stringify(data));
}, fail);
}, fail);
pcs[pc_index][1].onicecandidate = function(event) {
if (event.candidate) {
data = {"type":"candidate","from":local_username,"to":username,"candidate":event.candidate}
ws.send(JSON.stringify(data));
}
};
}
async function receive_offer(signal){
username = signal.username;
offer = signal.offer;
pc = new RTCPeerConnection();
pc_index = peer_connections;
pcs[pc_index] = [username,pc];
peer_connections = peer_connections+1;
stream = await navigator.mediaDevices.getUserMedia(constraints);
pcs[pc_index][1].addStream(stream);
pcs[pc_index][1].onaddstream = function(event) {
make_video_element(event.stream,username)
};
pcs[pc_index][1].setRemoteDescription(new RTCSessionDescription(offer), function() {
pcs[pc_index][1].createAnswer(function(answer) {
pcs[pc_index][1].setLocalDescription(answer, function() {
data = {"type":"answer","from":local_username,"to":username,"answer":answer}
ws.send(JSON.stringify(data));
}, fail);
}, fail);
}, fail);
pcs[pc_index][1].onicecandidate = function(event) {
if (event.candidate) {
data = {"type":"candidate","from":local_username,"to":username,"candidate":event.candidate}
ws.send(JSON.stringify(data));
}
};
}
async function receive_answer(signal){
username = signal.username;
answer = signal.answer;
for(var i=0;i<peer_connections;i++){
if(pcs[i][0]==username){
console.log("Accept answer");
console.log(username);
console.log(i)
pcs[i][1].setRemoteDescription(new RTCSessionDescription(answer));
}
}
}
async function receive_candidate(signal){
username = signal.username;
candidate = signal.candidate;
for(var i=0;i<peer_connections;i++){
if(pcs[i][0]==username){
pcs[i][1].addIceCandidate(new RTCIceCandidate(candidate));
}
}
}
async function unregister(signal){
username = signal.username;
index = 0
for(var i=0;i<peer_connections;i++){
if(pcs[i][0]==username){
pcs[i][1].close();
document.getElementById(username).style.display = "none";
document.getElementById(username).getElementsByTagName("video")[0].pause();
index = i;
break;
}
}
pcs.splice(index, 1);
peer_connections = peer_connections-1;
}
function make_video_element(stream,username){
var video_container = document.createElement("div");
video_container.id = username;
video_container.classList.add("video");
var video = document.createElement("video");
video.srcObject = stream;
video_container.appendChild(video);
var username_element = document.createElement("div");
username_element.classList.add("username");
username_element.innerHTML = username;
video_container.appendChild(username_element);
document.body.appendChild(video_container);
video.play();
}
function fail(error){
console.log(error);
}
可能由于以下原因导致错误:peer_connections 变量在创建报价之前已更改,因为正在尝试创建另一个报价。
编辑:对于想要在本地网络中实现上述代码的人,还请注意,必须在每个对等方中关闭 Windows Defender 防火墙。
推荐阅读
- java - 执行 JDBC 事务时出现重复键异常
- javascript - 使用在 Ajax 调用中创建的表单时提交取消错误
- python - 将文本文件拆分为较小的文件
- html - css - 使用 grid-auto-column 创建动态宽度
- xcode - 缺少权利 - 'UIBackgroundModes' 值 'workout-processing' 没有所需的权利 'com.apple.developer.healthkit'
- android - Xamarin axml,xml Intellisense 不工作
- android - Imageview 中的动画
- ffmpeg - 在 macOS 上捕获相机 + 麦克风并编码为 h264/aac
- css - 非常通用的 CSS 结构
- c++ - 如何检查字符串数组是否已排序