首页 > 解决方案 > 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);
}

上面的代码适用于一个连接(发送-接收)。但是,如果我打开三个标签,就会出现问题。我不知道问题集中在哪里。你能帮我吗?

标签: javascriptpythonhtmlwebsocketp2p

解决方案


我发现了错误:

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 防火墙。


推荐阅读