private void OnConnectintOutTimeout(Action timeoutAction) { lock (deviceLocker) { //如果中间已经连接上了,又到了这里,就不要执行了。 if (State == DeviceState.Connected || State == DeviceState.Error) { return; } State = DeviceState.Error; ConnectingOutTimer?.Stop(); ConnectingOutSocketTimer?.Stop(); if (timeoutAction == null) { //如果用户没有指定超时的行为,调用Device的默认的超时事件。 ConnectTimeouted?.Invoke(this); } else { //如果有自定义超时,调用自定义超时。 timeoutAction(); } } }
/// <summary> /// 在限定的时间内,每隔两秒钟,不停的尝试链接,直到连接成功。如果超时,调用timeoutAction. 在给定时间内,不限次数的连接。 /// /// 注意!! 调用Connect之前,如果Device的状态已经是ConnectingOut,这个函数会直接返回。如果想要强制重连,需要先将Device状态设置成Idle. /// </summary> /// <param name="timeoutMilliSeconds">允许尝试连接的时间(包括等待对方应答的时间)。</param> /// <param name="socketTimeoutMilliSeconds">建立socket最大等待时间。默认值=timeoutMilliSeconds。如果在这个事件范围内未成功建立连接,则报错</param> public void Connect(double timeoutMilliSeconds = 10000d, Action timeoutAction = null, double?socketTimeoutMilliSeconds = null) { if (State == DeviceState.ConnectingOut) { return; //不要重复的发连接消息。一个连接失败后,状态会改变。但在尝试期间,不得重复。 } #if DEBUG //在测试情况下输入IP地址,或者通过扫描二维码,得到了这个设备,去主动连接这个设备时,底层的设备列表中并没有发现这个设备。 if (ID == null) { ID = IPAddress; } #endif Preconditions.Check(timeoutMilliSeconds > 0, "允许连接尝试时间必须大于0"); Preconditions.Check(ID != null); //如果对方也在尝试连接我。 //TODO BUG? 这个地方和安全冲突。如果对方要求安全密码在连接我,我同时点击它,就不会要求输入密码了,再另一方会认证失败。这样倒也能接受。 if (State == DeviceState.ConnectingIn) { //TODO 这样做是否合适? Accept(); return; } State = DeviceState.ConnectingOut; socketCreated = false; double _socketTimeoutMilliSeconds = socketTimeoutMilliSeconds ?? timeoutMilliSeconds; ConnectingOutSocketTimer?.Stop(); if (_socketTimeoutMilliSeconds != timeoutMilliSeconds) { ConnectingOutSocketTimer = Util.DoLater(() => { OnConnectintOutTimeout(timeoutAction); }, _socketTimeoutMilliSeconds); } //一旦启动连接后,启动一个计时器。如果中间连接成功,则清除这个计时器。 ConnectingOutTimer?.Stop(); //如果以前曾经尝试连接,忽略以前的结果 ConnectingOutTimer = Util.DoLater(() => { OnConnectintOutTimeout(timeoutAction); }, timeoutMilliSeconds); IsRequester = true; //超时时间与上面的计时器时间相同,所以不在设置超时行为。 AppModel.Instance.ChannelManager.CreateChannel( this , (channel) => { SwitchChannel(channel); //TODO 为什么要延时发送?对方执行ChannelCreated, 并为Channel设置PacketReceivedEvent好像需要执行时间 //如果在这期间发送了Connect消息,会被对方忽略。 //TODO 重构 如果这个问题真的存在,可以让对方准备好之后回一个消息,这边收到消息之后再发送connect消息 Util.RunLater( () => { //如果是主动连接别人,发送一个连接消息。不要调用Device的Post函数。 //如果这里给别人发了链接消息,但是对方一直没理你,还是会导致超时。 SendConnectMessage(); } , 100d); } , null , timeoutMilliSeconds , (int)RETRY_INTERVAL ); }