Swift 3 stream delegate even handler error

2.8k views Asked by At

I am writing a very, very simple TCP socket app that sends and receives data from a server; my app uses delegates; most of the code I found from online but none of the event handlers for the streams are working; any ideas on why the following method is never being called?

UPDATE: this question has been answered here:

Non-responsive stream delegate in Swift

However, the problem that I am facing is how do I flush the output stream after the buffer is full? I would like send data in real time to my server, but I am getting a "broken pipe" from stream output error

/**
 NSStream Delegate Method where we handle errors, read and write data from input and output streams

 :param: stream NStream that called delegate method
 :param: eventCode      Event Code
 */
final func stream(stream: Stream, handleEvent eventCode: Stream.Event) {
    switch eventCode {
    case Stream.Event.endEncountered:
        endEncountered(stream: stream)

    case Stream.Event.errorOccurred:
        print("[SCKT]: ErrorOccurred: \(stream.streamError?.localizedDescription)")

    case Stream.Event.openCompleted:
        print("open completed")
        openCompleted(stream: stream)

    case Stream.Event.hasBytesAvailable:
        handleIncommingStream(stream: stream)

    case Stream.Event.hasSpaceAvailable:
        print("space available")
        writeToStream()

    default:
        print("default!")
    }
}

UIViewController

import Darwin
import Foundation
import UIKit
import Dispatch

class ViewController: UIViewController {

    @IBOutlet private weak var joystickMove: Joystick!
    @IBOutlet private weak var joystickRotate: Joystick!

    private var joystick = Joystick()
    private var contour = Contours()
    private var contour_index: Int = 0

    private var ip_address = "000.000.00.0" as CFString
    private var port: Int = 0000

    private var control_socket: Socket

    //    private var control_socket: Socket = Socket()

    private var toast_label: UILabel = UILabel()

    //    let dataProcessingQueue = DispatchQueue.main

    //    convenience init() {
    //        self.control_socket = Socket()
    //    }

    //    init(coder aDecoder: NSCoder!) {
    //        super.init(coder: aDecoder)
    //    }

    //    init(imageURL: NSURL?) {
    //        self.control_socket = Socket()
    //        self.control_socket.open(host: self.ip_address as String!, port: self.port)
    //        super.init(nibName: nil, bundle: nil)
    //    }

//    required init?(coder aDecoder: NSCoder) {
//        fatalError("init(coder:) has not been implemented")
//    }

    required init(coder aDecoder: NSCoder) {
        print("here!")
        self.control_socket = Socket()
//        self.control_socket.open(host: self.ip_address as String!, port: self.port)
        print("here(2)!")
        super.init(coder: aDecoder)!
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.control_socket.open(host: self.ip_address as String!, port: self.port)

        createJoystick()
        createContours()
        createViewsButton()
        recvLidarData()
    }

    private func requestIsComplete() -> Bool {
        // This function should find out if all expected data was received and return 'true' if it did.
        return true
    }

    private func processData(data: Data) {
        // This function should do something with the received data
    }

    private func recvLidarData() {
        let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
        concurrentQueue.async {
            // recieve the data here!
        }
    }

    private func createToast() {
        self.toast_label = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 150, y: self.view.frame.size.height-100, width: 300, height: 35))
        self.toast_label.backgroundColor = UIColor.black
        self.toast_label.textColor = UIColor.white
        self.toast_label.textAlignment = NSTextAlignment.center;
        self.view.addSubview(self.toast_label)
        self.toast_label.text = "Could not connect to server"
        self.toast_label.alpha = 1.0
        self.toast_label.layer.cornerRadius = 10;
        self.toast_label.clipsToBounds  =  true
        UIView.animate(withDuration: 4.0, delay: 0.1, options: UIViewAnimationOptions.curveEaseOut, animations: {
            self.toast_label.alpha = 0.0
        })
    }

    private func createJoystick() {

        let n: CGFloat = 100.0
        let x: CGFloat = (UIScreen.main.bounds.width/2) - (n/2.0)
        let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0)

        self.joystick.frame = CGRect(x: x, y: y, width: n, height: n)
        self.joystick.backgroundColor =  UIColor.clear

        self.joystick.substrateColor = UIColor.lightGray
        self.joystick.substrateBorderColor = UIColor.gray
        self.joystick.substrateBorderWidth = 1.0
        self.joystick.stickSize = CGSize(width: 50.0, height: 50.0)
        self.joystick.stickColor = UIColor.darkGray
        self.joystick.stickBorderColor = UIColor.black
        self.joystick.stickBorderWidth = 2.0
        self.joystick.fade = 0.5

        self.joystick.ip_address = self.ip_address
        self.joystick.port = self.port

        var packet = ""

        DispatchQueue.global(qos: .userInitiated).async { // do some task

            //            self.control_socket = Socket()
            //            self.control_socket.open(host: self.ip_address as String!, port: self.port)

            self.joystick.trackingHandler = { (data) -> () in

                var power = sqrt(pow(Double(data.velocity.x), 2.0) + pow(Double(data.velocity.y), 2.0))
                let theta = atan2(Double(-data.velocity.y), Double(data.velocity.x))
                let degrees = theta * (180.0 / M_PI)

                power = power/1.2

                if degrees >= 55 && degrees <= 125 { // move forward
                    packet = "\(1) \(1) \(power) \(power)"
                } else if degrees >= -125 && degrees <= -55 { // move backwards
                    packet = "\(-1) \(-1) \(power) \(power)"
                } else if degrees >= -55 && degrees <= 55 { // turn right
                    packet = "\(1) \(-1) \(power) \(power)"
                } else { // turn left
                    packet = "\(-1) \(1) \(power) \(power)"
                }

            }

            print("packet: \(packet)")

            self.control_socket.send(message: packet)

            print("sent")
        }

        view.addSubview(joystick)
    }

    private func createContours() {
        let n: CGFloat = 350.0
        let x: CGFloat = (UIScreen.main.bounds.width/2.0) - (n/2.0)
        let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0) - n - 100.0
        self.contour.frame = CGRect(x: x, y: y, width: n, height: n)
        self.contour.backgroundColor = UIColor.clear
        view.addSubview(self.contour)
    }

    private func createViewsButton() {
        let width: CGFloat = 150.0
        let height: CGFloat = 75.0
        let x: CGFloat = (UIScreen.main.bounds.width/2.0) - (width/2.0)
        let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0) - width

        let button: UIButton = UIButton(frame: CGRect(x: x, y: y, width: width, height: height))
        button.backgroundColor = UIColor.blue
        button.setTitle("Contour Views", for: .normal)
        button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)
        button.tag = 1
        view.addSubview(button)
    }

    @objc private func buttonAction(sender: UIButton!) {
        var btnsendtag: UIButton = sender
        if btnsendtag.tag == 1 {
            self.contour_index = (self.contour_index + 1) % 2
            switch self.contour_index {
            case 0:
                for index in 0...356 {
                    if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
                        self.contour.circles[index].alpha = 0
                    }
                }
            case 1:
                for index in 0...356 {
                    if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
                        self.contour.circles[index].alpha = 1
                    }
                }
            default:
                for index in 0...356 {
                    if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
                        self.contour.circles[index].alpha = 1
                        UIColor.cyan.setFill()
                        self.contour.lines[index].fill()
                        self.contour.lines[index].stroke()
                    }
                }
            }

        }
    }

    override func viewDidAppear(_ animated: Bool) {
    }

    public func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
        DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
            completion()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func viewWillDisappear(_ animated: Bool) {
    }
}

Socket class

import UIKit
import Foundation

@objc protocol SocketStreamDelegate{
    func socketDidConnect(stream:Stream)
    @objc optional func socketDidDisconnet(stream:Stream, message:String)
    @objc optional func socketDidReceiveMessage(stream:Stream, message:String)
    @objc optional func socketDidEndConnection()
}

class Socket: NSObject, StreamDelegate {

    var delegate:SocketStreamDelegate?

    private let bufferSize = 1024
    private var _host:String?
    private var _port:Int?
    private var _messagesQueue:Array<String> = [String]()
    private var _streamHasSpace:Bool = false
    private var inputStream: InputStream?
    private var outputStream: OutputStream?

    var isClosed = false
    var isOpen = false

    var host:String?{
        get{
            return self._host
        }
    }

    var port:Int?{
        get{
            return self._port
        }
    }

    deinit{
        if let inputStr = self.inputStream{
                        inputStr.close()
                        inputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
        }
        if let outputStr = self.outputStream{
                        outputStr.close()
                        outputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
        }
    }

    /**
     Opens streaming for both reading and writing, error will be thrown if you try to send a message and streaming hasn't been opened

     :param: host String with host portion
     :param: port Port
     */
    final func open(host:String!, port:Int!){
        self._host = host
        self._port = port

        var inStreamUnmanaged:Unmanaged<CFReadStream>?
        var outStreamUnmanaged:Unmanaged<CFWriteStream>?

        CFStreamCreatePairWithSocketToHost(nil, host as CFString!, UInt32(port), &inStreamUnmanaged, &outStreamUnmanaged)

        inputStream = inStreamUnmanaged?.takeRetainedValue()
        outputStream = outStreamUnmanaged?.takeRetainedValue()

        if inputStream != nil && outputStream != nil {

            inputStream!.delegate = self
            outputStream!.delegate = self

            //            var myloop = RunLoop.current

            //            inputStream!.schedule(in: myloop, forMode: RunLoopMode.defaultRunLoopMode)
            //            outputStream!.schedule(in: myloop, forMode: RunLoopMode.defaultRunLoopMode)

            inputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
            outputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)

            print("[SCKT]: Open Stream")

            self._messagesQueue = Array()

            inputStream!.open()
            outputStream!.open()

            //            myloop.run()

            print("Run")

        } else {
            print("[SCKT]: Failed Getting Streams")
        }
    }

    final func close(){
        if let inputStr = self.inputStream {
            inputStr.delegate = nil
            inputStr.close()
            inputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
        }
        if let outputStr = self.outputStream {
            outputStr.delegate = nil
            outputStr.close()
            outputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
        }
        isClosed = true
    }

    /**
     NSStream Delegate Method where we handle errors, read and write data from input and output streams

     :param: stream NStream that called delegate method
     :param: eventCode      Event Code
     */
    final func stream(stream: Stream, handleEvent eventCode: Stream.Event) {
        switch eventCode {
        case Stream.Event.endEncountered:
            endEncountered(stream: stream)

        case Stream.Event.errorOccurred:
            print("[SCKT]: ErrorOccurred: \(stream.streamError?.localizedDescription)")

        case Stream.Event.openCompleted:
            print("open completed")
            openCompleted(stream: stream)

        case Stream.Event.hasBytesAvailable:
            handleIncommingStream(stream: stream)

        case Stream.Event.hasSpaceAvailable:
            print("space available")
            writeToStream()

        default:
            print("default!")
        }
    }

    final func endEncountered(stream: Stream) {

    }

    final func openCompleted(stream: Stream){
        if(self.inputStream!.streamStatus == .open && self.outputStream!.streamStatus == .open) {
            let justAOneTimeThing: () = {
                self.isOpen = true
                self.delegate!.socketDidConnect(stream: stream)
            }()
        }
    }

    /**
     Reads bytes asynchronously from incomming stream and calls delegate method socketDidReceiveMessage
     :param: stream An NSInputStream
     */
    final func handleIncommingStream(stream: Stream) {

        if stream is InputStream {

            var buffer = [UInt8](repeating: 0, count: bufferSize)

            DispatchQueue.global(qos: .userInitiated).async {

                let len = self.inputStream?.read(&buffer, maxLength: buffer.count)

                if len! >= 0 {
                    if let output = NSString(bytes: &buffer, length: len!, encoding: String.Encoding.utf8.rawValue) {
                        self.delegate?.socketDidReceiveMessage!(stream: stream, message: output as String)
                    }
                } else {
                    // Handle error
                }
            }
        } else {
            print("[SCKT]: \(#function) : Incorrect stream received")
        }
    }

    /**
     If messages exist in _messagesQueue it will remove and it and send it, if there is an error
     it will return the message to the queue
     */
    final func writeToStream() {
        if _messagesQueue.count > 0 && self.outputStream!.hasSpaceAvailable  {
            DispatchQueue.global(qos: .userInitiated).async {
                let message = self._messagesQueue.removeLast()
                let buff = [UInt8](message.utf8)
                if self.outputStream!.write(buff, maxLength: buff.count) == -1 {
                    self._messagesQueue.append(message)
                }
            }
        }
    }

    final func send(message:String){
        _messagesQueue.insert(message, at: 0)
        writeToStream()
    }
}
1

There are 1 answers

0
Nguyễn Quốc Minh On

Maybe your stream function prototype is incorrect. Your prototype is

final func stream(stream: Stream, handleEvent eventCode: Stream.Event)

Try this:

func stream(_ aStream: Stream, handle eventCode: Stream.Event)

in your Socket class