天天看点

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循环一样的

如果有说的不对的地方或者是建议欢迎指正…