Supponiamo di avere due nodi peer: il primo nodo può inviare una richiesta di connessione al secondo, ma anche il secondo può inviare una richiesta di connessione al primo. Come evitare una doppia connessione tra i due nodi? Per risolvere questo problema, sarebbe sufficiente rendere sequenziali le operazioni eseguite per creare connessioni TCP in entrata o in uscita.
Ciò significa che ogni nodo dovrebbe elaborare in sequenza ogni nuova operazione di creazione della connessione, sia per le connessioni in entrata che per quelle in uscita. In questo modo, mantenendo un elenco di nodi connessi, prima di accettare una nuova connessione in entrata da un nodo o prima di inviare una richiesta di connessione a un nodo, sarà sufficiente verificare se questo nodo è già presente nell'elenco.
Per rendere sequenziali le operazioni di creazione delle connessioni, è sufficiente eseguire un blocco sull'elenco dei nodi connessi: infatti, per ogni nuova connessione, l'identificatore del nuovo nodo connesso viene aggiunto a questo elenco. Tuttavia, mi chiedo se questo approccio può causare deadlock distribuito :
- il primo nodo potrebbe inviare una richiesta di connessione al secondo;
- il secondo nodo potrebbe inviare una richiesta di connessione al primo;
- supponendo che le due richieste di connessione non siano asincrone, entrambi i nodi bloccano eventuali richieste di connessione in entrata.
Come potrei risolvere questo problema?
AGGIORNAMENTO: Tuttavia, devo ancora bloccare l'elenco ogni volta che viene creata una nuova connessione (in entrata o in uscita), poiché altri thread possono accedere a questo elenco, quindi il problema del deadlock rimarrebbe comunque.
AGGIORNAMENTO 2: Sulla base dei tuoi consigli ho scritto un algoritmo per impedire l'accettazione reciproca di una richiesta di accesso. Poiché ogni nodo è un peer, potrebbe avere una routine client per inviare nuove richieste di connessione e una routine server per accettare le connessioni in entrata.
ClientSideLoginRoutine() {
for each (address in cache) {
lock (neighbors_table) {
if (neighbors_table.contains(address)) {
// there is already a neighbor with the same address
continue;
}
neighbors_table.add(address, status: CONNECTING);
} // end lock
// ...
// The node tries to establish a TCP connection with the remote address
// and perform the login procedure by sending its listening address (IP and port).
boolean login_result = // ...
// ...
if (login_result)
lock (neighbors_table)
neighbors_table.add(address, status: CONNECTED);
} // end for
}
ServerSideLoginRoutine(remoteListeningAddress) {
// ...
// initialization of data structures needed for communication (queues, etc)
// ...
lock(neighbors_table) {
if(neighbors_table.contains(remoteAddress) && its status is CONNECTING) {
// In this case, the client-side on the same node has already
// initiated the procedure of logging in to the remote node.
if (myListeningAddress < remoteListeningAddress) {
refusesLogin();
return;
}
}
neighbors_table.add(remoteListeningAddress, status: CONNECTED);
} // end lock
}
Esempio: l'IP: la porta del nodo A è A: 7001 - L'IP: la porta del nodo B è B: 8001.
Supponiamo che il nodo A abbia inviato una richiesta di accesso al nodo B: 8001. In questo caso, il nodo A chiama la routine di accesso inviando inviando il proprio indirizzo di ascolto (A: 7001). Di conseguenza, neighbors_table del nodo A contiene l'indirizzo del nodo remoto (B: 8001): questo indirizzo è associato allo stato CONNECTING. Il nodo A è in attesa che il nodo B accetti o rifiuti la richiesta di accesso.
Nel frattempo, anche il nodo B potrebbe aver inviato una richiesta di connessione all'indirizzo del nodo A (A: 7001), quindi il nodo A potrebbe elaborare la richiesta del nodo B. Quindi, la tabella_gione_di vicini del nodo B contiene l'indirizzo del telecomando nodo (A: 7001): questo indirizzo è associato allo stato CONNECTING. Il nodo B è in attesa che il nodo A accetti o rifiuti la richiesta di accesso.
Se il lato server del nodo A rifiuta la richiesta da B: 8001, allora devo essere sicuro che il lato server del nodo B accetterà la richiesta da A: 7001. Allo stesso modo, se il lato server del nodo B rifiuta la richiesta da A: 7001, allora devo essere sicuro che il lato server del nodo A accetterà la richiesta da B: 8001.
Secondo la regola del "piccolo indirizzo" , in questo caso il nodo A rifiuterà la richiesta di accesso dal nodo B, mentre il nodo B accetterà la richiesta dal nodo A.
Cosa ne pensi di questo?