Ubuntu TCP打孔过程中的EADDR重用错误

Ubuntu TCP打孔过程中的EADDR重用错误,ubuntu,web,networking,tcp,hole-punching,Ubuntu,Web,Networking,Tcp,Hole Punching,我正在尝试实现TCP打孔。客户端使用服务器交换公共/私有ip详细信息。所有的实现都是在Ubuntu 20.04上完成的。客户端A和客户端B位于不同的NAT后面,服务器位于公共ip上 客户端A代码: var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout }); var addressOfS = 'x.x

我正在尝试实现TCP打孔。客户端使用服务器交换公共/私有ip详细信息。所有的实现都是在Ubuntu 20.04上完成的。客户端A和客户端B位于不同的NAT后面,服务器位于公共ip上

客户端A代码:

var readline = require('readline');
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

var addressOfS = 'x.x.x.x'; // replace this with the IP of the server running publicserver.js
var portOfS = 9999;

var socketToS;
var tunnelEstablished = false;

function connectToS () {
    console.log('> (A->S) connecting to S');

    socketToS = require('net').createConnection({host : addressOfS, port : portOfS}, function () {
        console.log('> (A->S) connected to S via', socketToS.localAddress, socketToS.localPort);


        // letting local address and port know to S so it can be can be sent to client B:
        socketToS.write(JSON.stringify({
            name: 'A',
            localAddress: socketToS.localAddress,
            localPort: socketToS.localPort
        }));
    });

    socketToS.on('data', function (data) {
        console.log('> (A->S) response from S:', data.toString());

        var connectionDetails = JSON.parse(data.toString());
        if(connectionDetails.name == 'A') {
            // own connection details, only used to display the connection to the server in console:
            console.log("");
            console.log('> (A)', socketToS.localAddress + ':' + socketToS.localPort, '===> (NAT of A)', connectionDetails.remoteAddress + ':' + connectionDetails.remotePort, '===> (S)', socketToS.remoteAddress + ':' + socketToS.remotePort);
            console.log("");
        }


        if(connectionDetails.name == 'B') {
            console.log('> (A) time to listen on port used to connect to S ('+socketToS.localPort+')');
            listen(socketToS.localAddress, socketToS.localPort);

            // try connecting to B directly:
            connectTo(connectionDetails.remoteAddress, connectionDetails.remotePort);
        }
    });

    socketToS.on('end', function () {
        console.log('> (A->S) connection closed.');
    });

    socketToS.on('error', function (err) {
        console.log('> (A->S) connection closed with err:', err.code);
    });
}

connectToS();


function connectTo (ip, port) {
    if(tunnelEstablished) return;

    console.log('> (A->B) connecting to B: ===> (B)', ip + ":" + port);
    var c = require('net').createConnection({host : ip, port : port}, function () {
        console.log('> (A->B) Connected to B via', ip + ":" + port);
        tunnelEstablished = true;
    });

    c.on('data', function (data) {
        console.log('> (A->B) data from B:', data.toString());
    });

    c.on('end', function () {
        console.log('> (A->B) connection closed.');
    });

    c.on('error', function (err) {
        console.log('> (A->B) connection closed with err:', err.code);
        setTimeout(function () {
            connectTo(ip, port);
        },500);
    });
}

var tunnelSocket = null;

function listen (ip, port) {
    var server = require('net').createServer(function (socket) {
        tunnelSocket = socket;

        console.log('> (A) someone connected, it\s:', socket.remoteAddress, socket.remotePort);

        socket.write("Hello there NAT traversal man, you are connected to A!");
        tunnelEstablished = true;

        readStuffFromCommandLineAndSendToB();
    });

    server.listen(port, ip, function (err) {
        if(err) return console.log(err);
        console.log('> (A) listening on ', ip + ":" + port);
    });
}

function readStuffFromCommandLineAndSendToB () {
    if(!tunnelSocket) return;

    rl.question('Say something to B:', function (stuff) {
        tunnelSocket.write(stuff);

        readStuffFromCommandLineAndSendToB();
    });
}
客户B代码:

var readline = require('readline');
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

// based on http://www.bford.info/pub/net/p2pnat/index.html

var addressOfS = 'x.x.x.x'; // replace this with the IP of the server running publicserver.js
var portOfS = 9999;

var socketToS;
var tunnelEstablished = false;

function connectToS () {
    console.log('> (B->S) connecting to S');

    socketToS = require('net').createConnection({host : addressOfS, port : portOfS}, function () {
        console.log('> (B->S) connected to S via', socketToS.localAddress, socketToS.localPort);


        // letting local address and port know to S so it can be can be sent to client A:
        socketToS.write(JSON.stringify({
            name: 'B',
            localAddress: socketToS.localAddress,
            localPort: socketToS.localPort
        }));
    });

    socketToS.on('data', function (data) {
        console.log('> (B->S) response from S:', data.toString());

        var connectionDetails = JSON.parse(data.toString());
        if(connectionDetails.name == 'B') {
            // own connection details, only used to display the connection to the server in console:
            console.log("");
            console.log('> (B)', socketToS.localAddress + ':' + socketToS.localPort, '===> (NAT of B)', connectionDetails.remoteAddress + ':' + connectionDetails.remotePort, '===> (S)', socketToS.remoteAddress + ':' + socketToS.remotePort);
            console.log("");
        }


        if(connectionDetails.name == 'A') {
            console.log('> (B) time to listen on port used to connect to S ('+socketToS.localPort+')');
            listen(socketToS.localAddress, socketToS.localPort);

            // try connecting to A directly:
            connectTo(connectionDetails.remoteAddress, connectionDetails.remotePort);
        }
    });

    socketToS.on('end', function () {
        console.log('> (B->S) connection closed.');
    });

    socketToS.on('error', function (err) {
        console.log('> (B->S) connection closed with err:', err.code);
    });
}

connectToS();

function connectTo (ip, port) {
    if(tunnelEstablished) return;

    console.log('> (B->A) connecting to A: ===> (A)', ip + ":" + port);
    var c = require('net').createConnection({host : ip, port : port}, function () {
        console.log('> (B->A) Connected to A via', ip + ":" + port);
        tunnelEstablished = true;
    });

    c.on('data', function (data) {
        console.log('> (B->A) data from A:', data.toString());
    });

    c.on('end', function () {
        console.log('> (B->A) connection closed.');
    });

    c.on('error', function (err) {
        console.log('> (B->A) connection closed with err:', err.code);
        setTimeout(function () {
            connectTo(ip, port);
        },500);
    });
}


var tunnelSocket = null;

function listen (ip, port) {
    var server = require('net').createServer(function (socket) {
        tunnelSocket = socket;

        console.log('> (B) someone connected, it\s:', socket.remoteAddress, socket.remotePort);

        socket.write("Hello there NAT traversal man, you are connected to B!");
        tunnelEstablished = true;

        readStuffFromCommandLineAndSendToA();
    });

    server.listen(port, ip, function (err) {
        if(err) return console.log(err);
        console.log('> (B) listening on ', ip + ":" + port);
    });
}

function readStuffFromCommandLineAndSendToA () {
    if(!tunnelSocket) return;

    rl.question('Say something to A:', function (stuff) {
        tunnelSocket.write(stuff);

        readStuffFromCommandLineAndSendToA();
    });
}
服务器代码

var socketA = null;
var socketB = null;

var detailsA = {
    name: 'A',
    localAddress: null,
    localPort: null,
    remoteAddress: null,
    remotePort: null
};

var detailsB = {
    name: 'B',
    localAddress: null,
    localPort: null,
    remoteAddress: null,
    remotePort: null
};


// assuming A will connect first:
var server = require('net').createServer(function (socket) {
    if(!socketA) {
        aConnects(socket);
    }else{
        bConnects(socket);
    }
});

server.listen(9999, function (err) {
    if(err) return console.log(err);

    console.log('server listening on', server.address().address + ':' + server.address().port);
});



function aConnects (socket) {
    socketA = socket;
    console.log('> (A) assuming A is connecting');
    console.log('> (A) remote address and port are:', socket.remoteAddress, socket.remotePort);
    console.log('> (A) storing this for when B connects');

    detailsA.remoteAddress = socket.remoteAddress;
    detailsA.remotePort = socket.remotePort;

    socket.on('data', function (data) {
        console.log('> (A) incoming data from A:', data.toString());

        var localDataA = JSON.parse(data.toString());
        if(!localDataA.name || localDataA.name != 'A') return console.log('> (A) this is not the local data of A');

        console.log('> (A) storing this for when B connects');
        console.log('');
        detailsA.localAddress = localDataA.localAddress;
        detailsA.localPort = localDataA.localPort;
        console.log('> (A) sending remote details back to A');
        socket.write(JSON.stringify(detailsA));

        console.log('> (A)', detailsA.localAddress + ':' + detailsA.localPort, '===> (NAT of A)', detailsA.remoteAddress + ':' + detailsA.remotePort, '===> (S)', socket.localAddress + ':' + socket.localPort);
    });

    socket.on('end', function () {
        console.log('> (A) connection closed.');
        socketA = null;
    });

    socket.on('error', function (err) {
        console.log('> (A) connection closed with err (',err,').');
        socketA = null;
    });
}

function bConnects(socket) {
    socketB = socket;

    console.log('> (B) assuming B is connecting');
    console.log('> (B) remote address and port are:', socket.remoteAddress, socket.remotePort);
    console.log('> (B) storing this');
    detailsB.remoteAddress = socket.remoteAddress;
    detailsB.remotePort = socket.remotePort;

    socket.on('data', function (data) {
        console.log('> (B) incoming data from B:', data.toString());

        var localDataB = JSON.parse(data.toString());
        if(!localDataB.name || localDataB.name != 'B') return console.log('> (B) this is not the local data of B');
        console.log('> (B) storing this');
        console.log('');
        detailsB.localAddress = localDataB.localAddress;
        detailsB.localPort = localDataB.localPort;
        console.log('> (B) sending remote details back to B');
        socket.write(JSON.stringify(detailsB));

        console.log('> (B)', detailsB.localAddress + ':' + detailsB.localPort, '===> (NAT of B)', detailsB.remoteAddress + ':' + detailsB.remotePort, '===> (S)', socket.localAddress + ':' + socket.localPort);



        console.log('> (S->A) sending B\'s details:', detailsB);
        socketA.write(JSON.stringify(detailsB));

        console.log('> (S->B) sending A\'s details:', detailsA);
        socketB.write(JSON.stringify(detailsA));


        console.log('');
    });

    socket.on('end', function () {
        console.log('> (B) connection closed.');
        socketB = null;
    });

    socket.on('error', function (err) {
        console.log('> (B) connection closed with err (',err,').');
        socketB = null;
    });
}
服务器详细信息

客户A

客户B