I am developing a WebSocket swift package that is written on top of NIOTS. I am running into a problem where the client connects to the server, but then immediately disconnects in the defer block. I am not sure what is going on, but basically the application is as follows.
```
public func connect() throws {
var group: EventLoopGroup? = nil
#if canImport(Network)
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) {
group = NIOTSEventLoopGroup()
} else {
print("Sorry, your OS is too old for Network.framework.")
exit(0)
}
#else
group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
print("Sorry, no Network.framework on your OS.")
#endif
defer {
try? group?.syncShutdownGracefully()
}
//Map the Event Loop Group to our Provider
let provider: EventLoopGroupManager.Provider = group.map { .shared($0)} ?? .createNew
guard let upgradePromise = group?.next().makePromise(of: Void.self) else {
throw MyErrors.optionalELGError("Our Event Loop Group was nil")
}
guard let url = URL(string: self.url) else { return }
//Initialize our NIOTSHandler
let niotsHandler = NIOTSHandler(url: url, tls: tls, group: group, groupProvider: provider, upgradePromise: upgradePromise)
//We get shutdown here as soon as we are connected
defer {
niotsHandler.disconnect()
}
_ = try niotsHandler.connect()
}
```
Inside of niotsHandler we grab our clientBootstrap that contains a WebSocketHandler that handles our web socket with the HTTPInitialRequestHandler, NIOWebSocketClientUpgrader, and NIOHTTPClientUpgradeConfiguration and start the connection
I recommend zooming in on the end of your code block, which you wrote as:
//Initialize our NIOTSHandler
let niotsHandler = NIOTSHandler(url: url, tls: tls, group: group, groupProvider: provider, upgradePromise: upgradePromise)
//We get shutdown here as soon as we are connected
defer {
niotsHandler.disconnect()
}
_ = try niotsHandler.connect()
Consider the flow here. At step 1, we initialise our NIOTSHandler. Step 2 we call connect, which returns a future that you throw away. Immediately after that returns, you call niotsHandler.disconnect() in your defer block.
You need to wait for your connection attempt to succeed and then let the connection actually run, rather than just calling disconnect. NIO is asynchronous, so the call to connect does not block.
Thank you Luksa, So if I call wait() on _ = try niotsHandler.connect() and it still disconnects after waiting then would that indicate that the future failed in my connect() throws -> EvenLoopFuture<Void> {} ?
I have written code similar to this before, but I am not sure if introducing promises has thrown me off somewhere.
No, if you wait and the future failed then your wait will throw. defer is going to execute immediately after the function ends. What you need is something you can wait on until you want to disconnect.
@lukasa Ok that makes sense. So I am connecting to the Handler however I still get disconnected right away however now the upgrade is not failing after the disconnect which is progress, but still I need to keep the connection open and then upgrade, If I understand what needs to happen correctly.
I wait like this
let connect = try niotsHandler.connect()
try connect.wait()
So is it correct to assume that it is because the connection completes and finishes before the WebSocket Handler's promise actually succeeds or fails?
If the connect.wait call completes it means the TCP connection is up. I don't know what's going on in your web socket handler so I can't easily diagnose from your logs what's happening.
Okay thank you. So my basic flow is initialize NIOTSHandler. NIOTSHandler contains this method. On the NIOHTTPClientUpgradeConfiguration we succeed the promise and then remove the HTTPHandler, at this point the client disconnects from the server and then the WebSocket is added.
So basically the completion block looks like it is finishing before the WebSocket is initialized. Do I need to wait for the WebSocket to finish initializing some how?
However why would the Client disconnect from the server without me telling it to?
When I run my unit test It says
Restarting after unexpected exit, crash, or test timeout in MyNIOWSTests.testMYNIOWSConnection(); summary will include totals from previous launches.
``
Alright thank you Cory. Much appreciated. I have 2 Questions,
My problem was that I needed to call try self.channel?.closeFuture.wait() after I connect, so why do we need to call this on WebSockets and not on pure TCP connections?
and
In my app that I am using the WebSocket Client in I needed to run it on the background thread i.e. DispatchQueue.global(qos: .background).async {}, Is there a recommended nio way to let the WebSocket Library do that instead of the library consumer?
You don't need to call this for web sockets only, if you want to wait for a pure TCP connection to exit you have to do the same thing.
You can dispatch yourself to a background thread, NIO doesn't have specific recommendations. Note that NIO owns its own threads and never runs them on the main thread.