Как обойти подключение к вечеринке, которая уже была подключена - PullRequest
0 голосов
/ 15 декабря 2018

Я сталкиваюсь с трудностями при определении способа десинхронизации соединений от двух сторон в соответствии с архитектурой моей программы.Это P2P-чат.Каждая из сторон автоматически подключается друг к другу при запуске программы.Процесс подключения асинхронный.Когда есть входящее соединение, сигнал вызывается от слушателя, и обработчик сигнала заботится о соединении.Таким образом, «принять» также является асинхронным.

Это в конечном итоге соединит обоих пользователей дважды.Там будет два соединения.Обе стороны будут сервером и клиентом.И это нежелательно.Теперь я попытался решить несколько вещей, но ни одна из них не сработала.Например, я проверил в обработчике сигналов прослушивателя, существует ли сокет на этом IP, и если это так, отключите его и верните.Но соединение спроектировано таким образом, что другая сторона будет пытаться установить соединение.

Каков правильный подход к его проектированию и какие есть альтернативы?

1 Ответ

0 голосов
/ 15 декабря 2018

Давайте рассмотрим ситуацию с четырьмя одноранговыми узлами:
Four peers
Если каждый одноранговый узел соединяется с каждым другим одноранговым узлом, между каждой парой одноранговых узлов будет два соединения.(Если имеется N одноранговых узлов, существует N (N-1) / 2 уникальных пар.)

На приведенной выше диаграмме одноранговые узлы упорядочены по их имени (A, B, C, D).Соединения с одноранговыми узлами с именами позже в алфавите выделены синим цветом, соединения с одноранговыми узлами ранее в алфавите выделены красным цветом.

Если вы можете каким-либо образом упорядочить одноранговые узлы, подключайтесь только к одноранговым узлам перед собой в порядкекрасный) и прием соединений от одноранговых узлов после себя (опять же, красный) гарантирует, что у вас будет только одно соединение на уникальную пару одноранговых узлов.

Допустим, у вас есть N одноранговых узлов, и вы как-то заказываете их из0 до N-1 включительно.Затем одноранговому i необходимо accept() подключения от N-1-i одноранговых узлов и connect() к i одноранговым узлам.

Конечно, на практике все не так просто.Проблема в том, что одноранговые группы могут появляться в разное время, а не одновременно;и соединение не будет установлено, если на другом конце еще нет прослушивающего сокета, соответствующего адресу и порту.Таким образом, наиболее важно сначала создать прослушивающий сокет, убедиться, что резерв достаточно велик, а затем выполнить подключения.

Если мы посмотрим на GIO GSocket s (поскольку это, кажется,будь то, что использует OP), решение довольно простое, при условии, что все одноранговые узлы согласны с их порядком.

  1. Все одноранговые узлы создают сокет прослушивания, используя g_socket_new(), g_socket_bind(), g_set_backlog() и g_socket_listen().Задержка должна составлять не менее числа пиров (не более одного).

    Если новых пиров можно «пригласить» во время выполнения, используйте больший объем запаздывания.Если новые одноранговые узлы не могут быть приглашены, то последнему одноранговому узлу в заказе не требуется сокет прослушивания, поскольку он будет только устанавливать соединения, а не принимать входящие.

  2. iТретий узел (первый узел в порядке 0) создает i сокетов для подключения к узлам перед порядком, используя g_socket_new(), опционально g_socket_bind(), опционально g_socket_set_blocking(), чтобы сделать соединения неблокирующими, и g_socket_connect().

    Если сокет был разблокирован с использованием g_socket_set_blocking(), то одноранговый узел может зацикливаться на g_socket_check_connect_result() или g_socket_condition_check(), чтобы дождаться завершения всех соединений.

    Обратите внимание, что, посколькуреальное различие в том, когда каждый узел создает свой входящий сокет, соединения могут прерваться (потому что другой конец еще не начался).В этих случаях вы должны просто повторить попытку;т.е. снова вызовите g_socket_connect().

    (Вы также можете использовать g_socket_create_source() для создания GSource из сокета, чтобы рассматривать его как источник события.)

  3. На этом этапе входящие соединения должны быть заблокированы в сетевом стеке ОС (или будут), поэтому одноранговый i должен использовать g_socket_accept() для приема входящих соединений от одноранговых узлов N-1-i после него в порядке.


Лично я бы использовал другую архитектуру / подход, который динамически управляет списком пиров.

Допустим, у каждого пира есть уникальный идентификатор.(Это может быть, например, сопоставленный с IPv6 адрес и порт, или псевдоним пользователя и их открытый ключ.)

Когда соединение установлено, соединяющая сторона отправляет начальное рукопожатие, которое содержит свой собственный идентификатор, иидентификатор стороны, к которой он пытается подключиться.Когда новое соединение принято, получено начальное рукопожатие (в течение некоторого интервала времени ожидания).Если присоединяющаяся сторона является новой, она добавляется в список подключенных пиров.Если оно уже подключено, либо новое, либо существующее соединение будет сброшено (после отправки пакета, в котором указана причина сброса).

Это также позволит "приглашать" и обмениваться информацией о одноранговых соединениях черезсетка.

...