如何绕过连接到已经连接的一方
How to get around connecting to a party that has already been connected
根据我的程序架构,我很难确定如何取消来自两方的连接同步。这是一个P2P聊天。程序启动时,每一方都会自动相互连接。连接过程是异步的。
当有传入连接时,从侦听器调用信号,信号处理程序负责连接。所以 "accept" 也是异步的。
这将最终连接两个用户两次。会有两个连接。
双方将是服务器和客户端。这是不需要的。
现在我尝试了很多方法来解决这个问题,但 none 确实有效。
例如,我在侦听器信号处理程序中检查了该 IP 上的套接字是否已经存在,如果存在则断开连接并 return。但是连接的设计方式使得另一方会不断尝试连接。
设计它的正确方法是什么?备选方案是什么?
让我们看一下有四个同行的情况:
如果每个对等点都连接到每个其他对等点,则每对对等点之间将有两个连接。 (如果有 N 个对等点,则有 N(N-1)/2 个唯一对。)
在上图中,节点按名称排序(A、B、C、D)。与字母表中名字靠后的对等点的连接是蓝色的,与字母表中靠前的对等点的连接是红色的。
如果你能以某种方式对节点进行排序,只连接顺序中自己之前的节点(红色),并接受来自自己之后的节点的连接(再次,红色),将确保你只有一个唯一连接对同龄人。
假设您有 N
个同伴,并且您以某种方式从 0
到 N-1
对他们进行排序,包括在内。然后,对等点 i
需要 accept()
个来自 N-1-i
个对等点的连接,connect()
到 i
个对等点的连接。
当然,在实践中没有那么简单。问题是节点可能会在不同时间出现,而不是同时出现;如果另一端还没有对应地址和端口的监听套接字,则连接将失败。所以,最重要的是先做listening socket,保证backlog足够大,再做connections。
如果我们查看 GIO GSockets(因为这似乎是 OP 正在使用的),解决方案相当简单,假设所有同行都同意他们的顺序。
所有对等方使用 g_socket_new()
、g_socket_bind()
、g_set_backlog()
和 g_socket_listen()
创建一个侦听套接字。 backlog至少应该是同行的数量(少一个)。
如果新节点可以在 运行 时成为 "invited in",请使用更大的积压。如果无法邀请新的对等点,则顺序中的最后一个对等点不需要侦听套接字,因为它只会建立连接,而不接受传入的连接。
i
th peer(第一个 peer 按顺序是 0
)使用 [=18 创建 i
套接字连接到它之前的 peers =],可选 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
,将其视为事件源。)
此时,传入连接应积压在 OS 网络堆栈中(或将积压),因此对等点 i
应使用 g_socket_accept()
来接受来自排序后 N-1-i
对等点的传入连接。
就我个人而言,我会使用一种不同的 architecture/approach,它可以动态管理对等列表。
假设每个点都有一个唯一的标识符。 (这可能是 IPv6 映射地址和端口,或者用户昵称和他们的 public 密钥。)
建立连接后,连接方发送初始握手,其中包含自己的标识符以及它尝试连接的一方的标识符。当接受新连接时,将收到初始握手(在某个超时间隔内)。如果连接方是新的,则将其添加到已连接的对等方列表中。如果它已经连接,则新的或现有的连接将被丢弃(在发送一个说明丢弃原因的数据包之后)。
这也将允许 "invites",并跨网格共享对等连接信息。
根据我的程序架构,我很难确定如何取消来自两方的连接同步。这是一个P2P聊天。程序启动时,每一方都会自动相互连接。连接过程是异步的。 当有传入连接时,从侦听器调用信号,信号处理程序负责连接。所以 "accept" 也是异步的。
这将最终连接两个用户两次。会有两个连接。 双方将是服务器和客户端。这是不需要的。 现在我尝试了很多方法来解决这个问题,但 none 确实有效。 例如,我在侦听器信号处理程序中检查了该 IP 上的套接字是否已经存在,如果存在则断开连接并 return。但是连接的设计方式使得另一方会不断尝试连接。
设计它的正确方法是什么?备选方案是什么?
让我们看一下有四个同行的情况:
如果每个对等点都连接到每个其他对等点,则每对对等点之间将有两个连接。 (如果有 N 个对等点,则有 N(N-1)/2 个唯一对。)
在上图中,节点按名称排序(A、B、C、D)。与字母表中名字靠后的对等点的连接是蓝色的,与字母表中靠前的对等点的连接是红色的。
如果你能以某种方式对节点进行排序,只连接顺序中自己之前的节点(红色),并接受来自自己之后的节点的连接(再次,红色),将确保你只有一个唯一连接对同龄人。
假设您有 N
个同伴,并且您以某种方式从 0
到 N-1
对他们进行排序,包括在内。然后,对等点 i
需要 accept()
个来自 N-1-i
个对等点的连接,connect()
到 i
个对等点的连接。
当然,在实践中没有那么简单。问题是节点可能会在不同时间出现,而不是同时出现;如果另一端还没有对应地址和端口的监听套接字,则连接将失败。所以,最重要的是先做listening socket,保证backlog足够大,再做connections。
如果我们查看 GIO GSockets(因为这似乎是 OP 正在使用的),解决方案相当简单,假设所有同行都同意他们的顺序。
所有对等方使用
g_socket_new()
、g_socket_bind()
、g_set_backlog()
和g_socket_listen()
创建一个侦听套接字。 backlog至少应该是同行的数量(少一个)。如果新节点可以在 运行 时成为 "invited in",请使用更大的积压。如果无法邀请新的对等点,则顺序中的最后一个对等点不需要侦听套接字,因为它只会建立连接,而不接受传入的连接。
i
th peer(第一个 peer 按顺序是0
)使用 [=18 创建i
套接字连接到它之前的 peers =],可选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
,将其视为事件源。)此时,传入连接应积压在 OS 网络堆栈中(或将积压),因此对等点
i
应使用g_socket_accept()
来接受来自排序后N-1-i
对等点的传入连接。
就我个人而言,我会使用一种不同的 architecture/approach,它可以动态管理对等列表。
假设每个点都有一个唯一的标识符。 (这可能是 IPv6 映射地址和端口,或者用户昵称和他们的 public 密钥。)
建立连接后,连接方发送初始握手,其中包含自己的标识符以及它尝试连接的一方的标识符。当接受新连接时,将收到初始握手(在某个超时间隔内)。如果连接方是新的,则将其添加到已连接的对等方列表中。如果它已经连接,则新的或现有的连接将被丢弃(在发送一个说明丢弃原因的数据包之后)。
这也将允许 "invites",并跨网格共享对等连接信息。