首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Swift-NIO TCP客户端自动重新连接

Swift-NIO TCP客户端自动重新连接
EN

Stack Overflow用户
提问于 2020-01-13 22:36:41
回答 1查看 621关注 0票数 0

我在Swift-NIO中编写了一个TCP客户端来连接Netty TCP Server。我希望tcp客户端可以在需要时自动重新连接。

代码语言:javascript
复制
import Foundation
import NIO

class MessageHandler: ChannelInboundHandler {
    let notificationMessage = NSNotification.Name(rawValue: "Location")
    public typealias InboundIn = ByteBuffer
    public typealias OutboundOut = ByteBuffer
    private var numBytes = 0
    private var task: RepeatedTask? = nil
    private var bootstrap: ClientBootstrap

    init(bootstrap: ClientBootstrap) {
        self.bootstrap = bootstrap
    }

    public func channelActive(context: ChannelHandlerContext) {
        print("Reconnect Successful")

        self.task?.cancel()
        context.fireChannelActive()
    }

    func channelInactive(context: ChannelHandlerContext) {
        self.task = context.channel.eventLoop.scheduleRepeatedTask(initialDelay: TimeAmount.seconds(0), delay: TimeAmount.seconds(10), { (RepeatedTask) in
            print("Reconnecting...")

            try { () -> EventLoopFuture<Channel> in
                return try self.bootstrap.connect(host: SystemUtil.getConfig(key: "IP") as! String, port: SystemUtil.getConfig(key: "TCPPort") as! Int)
                }()
        })

        context.fireChannelInactive()
    }

    public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        var buffer = unwrapInboundIn(data)
        let readableBytes = buffer.readableBytes
        if let message = buffer.readString(length: readableBytes) {
            print(message)
            let dictMessage = ["Location": message]
            NotificationCenter.default.post(name: notificationMessage , object:MessageHandler.self, userInfo: dictMessage)
        }
    }

    public func errorCaught(context: ChannelHandlerContext, error: Error) {
        print("error: ", error)

        // As we are not really interested getting notified on success or failure we just pass nil as promise to
        // reduce allocations.
        context.close(promise: nil)
    }
}

它是关于工作的,但有一些不正确的地方。我用eventLoop.scheduleRepeatedTask每隔10秒检查一次,当连接后取消RepeatedTask.But的self.task?.cancel()不工作,我看了一下取消的源代码。取消RepeatedTask的正确方法是什么?谢谢

代码语言:javascript
复制
private func cancel0(localCancellationPromise: EventLoopPromise<Void>?) {
        self.eventLoop.assertInEventLoop()
        self.scheduled?.cancel()
        self.scheduled = nil
        self.task = nil

        // Possible states at this time are:
        //  1) Task is scheduled but has not yet executed.
        //  2) Task is currently executing and invoked `cancel()` on itself.
        //  3) Task is currently executing and `cancel0()` has been reentrantly invoked.
        //  4) NOT VALID: Task is currently executing and has NOT invoked `cancel()` (`EventLoop` guarantees serial execution)
        //  5) NOT VALID: Task has completed execution in a success state (`reschedule()` ensures state #2).
        //  6) Task has completed execution in a failure state.
        //  7) Task has been fully cancelled at a previous time.
        //
        // It is desirable that the task has fully completed any execution before any cancellation promise is
        // fulfilled. States 2 and 3 occur during execution, so the requirement is implemented by deferring
        // fulfillment to the next `EventLoop` cycle. The delay is harmless to other states and distinguishing
        // them from 2 and 3 is not practical (or necessarily possible), so is used unconditionally. Check the
        // promises for nil so as not to otherwise invoke `execute()` unnecessarily.
        if self.cancellationPromise != nil || localCancellationPromise != nil {
            self.eventLoop.execute {
                self.cancellationPromise?.succeed(())
                localCancellationPromise?.succeed(())
            }
        }
    }

是的,任务是空的,所以取消不起作用。我将全局变量更改为静态变量

代码语言:javascript
复制
static var task: RepeatedTask? = nil

现在可以正常工作了。

但我仍然不确定在Swift-NIO中自动重新连接的最佳实践是什么。在我的Android应用程序中,我使用Netty作为TCP客户端,如下所示

代码语言:javascript
复制
private inner class ConnectServerThread : Thread() {
    override fun run() {
        super.run()

        val workerGroup = NioEventLoopGroup()

        try {
            val bootstrap = Bootstrap()
            bootstrap.group(workerGroup)
                .channel(NioSocketChannel::class.java)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_REUSEADDR, true)
                .handler(object : ChannelInitializer<SocketChannel>() {
                    public override fun initChannel(ch: SocketChannel) {
                        ch.pipeline().addLast(
                            ReconnectHandler(bootstrap, channel),
                            StringEncoder(StandardCharsets.UTF_8),
                            StringDecoder(StandardCharsets.UTF_8),
                            MessageHandlerAdapter()
                        )
                    }
                })
            val channelFuture = bootstrap.connect(
                InetSocketAddress(
                    ConfigUtil.config!!.ip,
                    ConfigUtil.config!!.tcpPort!!.toInt()
                )
            ).sync()
            channelFuture.addListener {
                getConnectionListener()
            }
            channel = channelFuture.channel() as SocketChannel
        } catch (e: Exception) {
            Log.d("SystemService", e.toString())
        }
    }
}

我使用ReconnectHandler进行重新连接,使用getConnectionListener进行侦听。在Swift-NIO中是否有类似的侦听器或其他解决方案?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-15 20:13:31

SwiftNIO的解决方案将要求您的处理程序将回调附加到从connect返回的未来。这些回调可以关闭重复的任务,因此可以在连接完成时取消它。例如:

代码语言:javascript
复制
import Foundation
import NIO

final class Reconnector {
    private var task: RepeatedTask? = nil
    private let bootstrap: ClientBootstrap

    init(bootstrap: ClientBootstrap) {
        self.bootstrap = bootstrap
    }

    func reconnect(on loop: EventLoop) {
        self.task = loop.scheduleRepeatedTask(initialDelay: .seconds(0), delay: .seconds(10)) { task in
            print("reconnecting")
            try self._tryReconnect()
        }
    }

    private func _tryReconnect() throws {
        try self.bootstrap.connect(host: SystemUtil.getConfig(key: "IP") as! String, port: SystemUtil.getConfig(key: "TCPPort") as! Int).whenSuccess { _ in
            print("reconnect successful!")
            self.task?.cancel()
            self.task = nil
        }
    }
}

class MessageHandler: ChannelInboundHandler {
    let notificationMessage = NSNotification.Name(rawValue: "Location")
    public typealias InboundIn = ByteBuffer
    public typealias OutboundOut = ByteBuffer
    private var numBytes = 0
    private let reconnect: Reconnector

    init(bootstrap: ClientBootstrap) {
        self.reconnector = Reconnector(bootstrap: bootstrap)
    }

    func channelInactive(context: ChannelHandlerContext) {
        self.reconnector.reconnect()
        context.fireChannelInactive()
    }

    public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        var buffer = unwrapInboundIn(data)
        let readableBytes = buffer.readableBytes
        if let message = buffer.readString(length: readableBytes) {
            print(message)
            let dictMessage = ["Location": message]
            NotificationCenter.default.post(name: notificationMessage , object:MessageHandler.self, userInfo: dictMessage)
        }
    }

    public func errorCaught(context: ChannelHandlerContext, error: Error) {
        print("error: ", error)

        // As we are not really interested getting notified on success or failure we just pass nil as promise to
        // reduce allocations.
        context.close(promise: nil)
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59718703

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档