天天看點

node.js實作p2p網絡

理論部分

首先,我們要明白p2p要如何實作

舉個例子,

node.js實作p2p網絡
看這張圖,就比如我有3快機器

1機器開放6001端口

2機器的34394端口連結着6001端口,并且2機器開着6002端口,

3機器的36460端口連結着6002端口,并且3機器開着6003端口

那麼現在是如何記錄的呢?

先看1機器,當有端口和1機器的端口相連時,node.js是可以擷取到連結過來的機器的端口号的,而向1機器連結的這個34394端口,他是随機配置設定的.

是以我們可以知道:

1機器裡存有2機器連結的端口号:

2機器存有1機器接受的端口号和3機器連結的端口号:

3機器存有2機器接受的端口号

那麼,我們如何實作傳輸資料,我們可以從3機器通過存儲的2機器接受的端口号發送給2機器,然後為了防止2機器發回來的資料又被接受,我們可以拒絕2機器傳回的資訊

然後同理,2機器通過接受到3機器發送的資料,周遊2機器自身存儲的端口号,把消息傳遞給1機器,同時拒絕1機器的傳回資訊

這樣我們就可以實作一個簡單的p2p通信

話不多說直接代碼

代碼部分

直接發我的github上的内容,其中test3就是我自己使用的node.js實作p2p網絡.

https://github.com/feige011/p2pnetwork

使用例子

開3個視窗先進入其test3.js的目錄然後分别依次輸入

HTTP_PORT=3001 FFF_PORT=6001 node test3.js
 HTTP_PORT=3002 FFF_PORT=6002 node test3.js
  HTTP_PORT=3003 FFF_PORT=6003 node test3.js
           

由于我的設計就是自動連結port小1的端口(為了操作友善),是以第一個輸入他會告訴你連結失敗,因為你沒開6000端口,不用理他.後兩個就可以成功連結,

然後在第3視窗打字,就會發現傳輸到2,1中

如果你在1視窗打字,發現2有内容出現是正常的,因為這個是我自己設計說拒絕他傳回資訊

node.js實作p2p網絡
node.js實作p2p網絡

然後就是我們的代碼講解

var express = require("express");
var WebSocket = require("ws");
var bodyParser = require('body-parser');
           

建立express路由和websocket的架構

至于第三個用來解析json資料的,我這裡沒用上,不過是為了你如果要通過post請求來進行配對連結的話,這個就有用了

var server_me;
var http_port = process.env.HTTP_PORT || 3002;
var p2p_port = process.env.FFF_PORT || 6002;
var sockets = [];
           

這個server_me是用來看要阻擋前一個端口發送資料的,他用來記錄前一個端口是什麼

http_post為了之後網絡連結通過post請求等進行連接配接資料

p2p_port為了連接配接各個端口

這兩個 process.env是從鍵盤輸入,後面名字随便起的(沒有FFF_PORT)

socket為了記錄每一個視窗中存儲的端口号,友善後面發送資料

var initHttpServer = () => {
    var app = express();//建立express()執行個體
    app.use(bodyParser.json());
    process.stdin.setEncoding('utf-8');
    process.stdin.on('readable',function () {
        var chunk = process.stdin.read();
        if(chunk!==null){
            broadcast(chunk)
        }
    })

    // app.get("/peers", (req, res) => {
    //     console.log("feifei map:"+sockets.length)
    //     res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));
    // });
    // app.post("/addPeer", (req, res) => {
    //     connectToPeers([req.body.peer])
    //     res.send([req.body.peer])
    // });
    // app.get('/getNum', (req, res) => {
    //     res.send(num);
    // })
    // app.post('/numAdd', (req, res) => {
    //     num++;
    //     broadcast(responseLatestMsg());//廣播
    //     console.log('block added:' + num);
    //     res.send();
    // })

    app.listen(http_port, () => console.log('listening http on port: ' + http_port));
}
           

process就是從鍵盤輸入,就是輸入内容,然後通過broadcast進行廣播

為了操作友善,我直接就不用通過指令行輸入什麼連結端口了,要是需要通過指令大緻就是我登出的那種寫法

var initP2PServer = () => {
    var server = new WebSocket.Server({port: p2p_port});
    console.log(p2p_port-1);
    server.on('connection', ws => {
        console.log("feige!!!!!!!!!!is good");
        initConnection(ws)
    });
    var s="ws://localhost:"+(p2p_port-1);
    console.log(s);

    var socket = new WebSocket(s);
    server_me=socket
    socket.on('open', () =>{
        console.log("feige011 is back")
        initConnection(socket)
    } );
    socket.on('error', () => {
        console.log('connection failed');
    })
    console.log('listening websocket p2p port on:' + p2p_port);

}
           

這裡主要先給自己端口開了

然後連結自己小1的端口

連接配接上的話就initConnection

然後還有給小1的端口記錄到server_me中,好接下來阻擋他

var initConnection = (ws) => {//初始化連結
    sockets.push(ws);
    console.log("feifeifei:size="+sockets.length)
    for(var i=0;i<sockets.length;i++){
        console.log("feifeihaha:"+sockets[i]._socket.remoteAddress+":"+sockets[i]._socket.remotePort)
    }
    initMessageHandler(ws);
    initErrorHandler(ws);
}
           

初始化連結,主要給連接配接的websocket存入自己的sockets清單中,

然後定理他的自己接受時要進行什麼操作

var initMessageHandler=(ws)=>{
    ws.on('message',(data)=>{
        console.log("Received message"+data);
        if(ws!==server_me){
            console.log("fa song le shu ju")
            broadcast(data);
            // handleNum(data);
        }else{
            console.log("feifei**************"+sockets.length);
            for(var i=0;i<sockets.length;i++){
                console.log("feifei"+sockets[i]._socket.remoteAddress+":"+sockets[i]._socket.remotePort)
            }
            console.log("zu zhi le:"+ws._socket.remoteAddress+":"+ws._socket.remotePort)
            console.log("feifei***********************");
        }
    })
}
           

當接受到資料時,如果不是阻擋的資料,就進行brocast廣播,反之列印一下自己想看的東西

var initErrorHandler =(ws)=>{
    var closeConnection =(ws)=>{
        console.log('connection failed to peer: '+ws.url+" "+ws._socket.remoteAddress+":"+ws._socket.remotePort);
        sockets.splice(sockets.indexOf(ws),1);
    }
    ws.on('close',()=>closeConnection(ws));
    ws.on('error',()=>closeConnection(ws))
}
           

當連接配接錯誤時,關掉它

var write=(ws,message)=>{
    console.log("fa song gei le:"+ws._socket.remoteAddress+":"+ws._socket.remotePort)
    ws.send(message)
}
var broadcast=(message)=>{
    sockets.forEach(socket =>write(socket,message));
}
initHttpServer();
initP2PServer();

           

最後就是brocast如何廣播的,也就是調用一下socket.send()

内個forEach寫個普通for循環一樣的

如果有說的不對的地方或者是建議歡迎指正…

繼續閱讀