为啥 XMPPFramework 会阻塞?

Posted

技术标签:

【中文标题】为啥 XMPPFramework 会阻塞?【英文标题】:Why is XMPPFramework blocking?为什么 XMPPFramework 会阻塞? 【发布时间】:2017-06-30 15:29:32 【问题描述】:

我们正在尝试获取 XMPPFramework 运行的最小示例。

考虑这个简单的类:

import Foundation
import XMPPFramework

class Connection: NSObject, XMPPStreamDelegate 
    let stream: XMPPStream

    override init() 
        self.stream = XMPPStream()!
    

    func connectToServer(timeout: TimeInterval) 
        stream.addDelegate(self, delegateQueue: DispatchQueue.main)
        stream.myJID = XMPPJID(string: "myuser")
        stream.hostName = "myserver.tld"
        stream.hostPort = 5222

        do 
            try stream.connect(withTimeout: timeout)
        
        catch 
            print(error)
        
    

    func xmppStreamWillConnect(_ sender: XMPPStream!) 
        print("will connect")
    

    func xmppStreamDidConnect(_ sender: XMPPStream!) 
        print("did connect")
        self.stream.disconnect()
    

还有这个简单的测试类:

import Foundation
import XCTest
@testable import MyModule

class ConnectionTests: XCTestCase 
    func testConnect() 
        let connection = Connection()

        print("Try to connect")
        let expectation = self.expectation(description: "connect")
        connection.connectToServer(timeout: 3)
        self.waitForExpectations(timeout: 5)
    

我期望这个输出:

Try to connect
will connect
did connect
<5s timeout since I don't fulfill>

如果我的 XMPP 服务器不小心对我的请求做出了积极响应,我希望看到:

Try to connect
will connect
<some error message>

如果 XMPP 服务器没有(快速)回复,我希望:

Try to connect
will connect
<3s timeout reached>

但是,我没有得到这些,而是​​:

Try to connect
will connect
<5s timeout since I don't fulfill>

发生了什么事?

这是我收集的。

XCTest 等待 DispatchQueue.main。 我们可以控制代表的运行位置;使用DispatchQueue.mainDispatchQueue(label: "test", qos: .userInitiated, attributes: .concurrent) 似乎并不重要。 我可以在XMPPStream.connectWithTimeout 中跟踪直到dispatch_sync(xmppQueue, block); 的执行,其中xmppQueue 是一个带有标签xmpp 的新队列;该块似乎也已完全执行。

所以我不明白谁在这里阻止了谁,以及如何防止它发生。

【问题讨论】:

不进行身份验证连接的目的是什么?连接后需要登录,否则会关闭连接。 由于 xmppStreamDidConnect 没有被调用,我们甚至没有走到那一步。 【参考方案1】:

问题有三个方面:

    期望超时,因为它从未实现。 我们从来没有看到“确实连接”,因为它从来没有走到那一步。 我们对 XMPP(Framework) 的了解不够,无法理解在哪里插入。

我们实现了一些额外的委托方法:

func xmppStream(_ sender: XMPPStream!, socketDidConnect socket: GCDAsyncSocket!) 
    print("socket did connect")


func xmppStreamDidStartNegotiation(_ sender: XMPPStream!) 
    print("negotiation started")


...

func xmppStream(_ sender: XMPPStream!, didReceiveError error: DDXMLElement!) 
    print("error: \(error)")


func xmppStreamDidDisconnect(_ sender: XMPPStream!, withError error: Error!) 
    print("disconnected")
    self.afterConnection()

这里,self.afterConnection 是作为附加参数传递给connectToServer 的函数。

我们在测试中改变:

connection.connectToServer(timeout: 3, then:  expectation.fulfill() )

现在测试以有序的方式结束,输出如下:

Try to connect
will connect
socket did connect
negotiation started
disconnected
error: Optional(<stream:error xmlns:stream="http://etherx.jabber.org/streams"><host-unknown xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>)

毕竟这是服务器的问题。我们需要实施正确的代表来诊断问题;我们本来希望stream.connect 在这种情况下抛出错误!

【讨论】:

【参考方案2】:

按如下方式制作该类的单例:

static let sharedInstance = Connection();

现在这样称呼它:

func testConnect() 
     Connection.sharedInstance.connectToServer(timeout: 3);

【讨论】:

以上是关于为啥 XMPPFramework 会阻塞?的主要内容,如果未能解决你的问题,请参考以下文章

为啥阻塞系统调用会阻塞用户级线程的整个过程?

为啥程序会在select中被频道阻塞?

为啥 File.ReadAllLinesAsync() 会阻塞 UI 线程?

为啥从管道读取会阻塞进程?

我不确定为啥我的 QThread 模式会阻塞

为啥我的 IUnknown 释放函数会阻塞我的子线程?