理論部分
首先,我們要明白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有内容出現是正常的,因為這個是我自己設計說拒絕他傳回資訊
然後就是我們的代碼講解
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循環一樣的
如果有說的不對的地方或者是建議歡迎指正…