Search code examples
swiftcocoansstream

NSInputStream / NSOutputStream not opening


I'm trying to set up a connection to a TCP-server, with a NSInputStream and a NSOutputStream. The HasBytesAvailable or HasSpaceAvailable-event is never received. So I don't know if the socket actually got set up?

In this particular case I have access to the server and can actually see that it makes a connection, so I tried to write data before the HasSpaceAvailable-event was received and it worked!

But since the HasBytesAvailable-event never triggers I have no idea when to read from the stream.

I have read though most of the similar posts on SO and tried many of them, but with no luck.

If anyone could see something obvious that I missed, it would help me greatly! :)

Below is the relevant code from my class:

class TCPConnection : NSObject, NSStreamDelegate {


    private var host: String
    private var port: Int
    private var inputStream: NSInputStream?
    private var outputStream: NSOutputStream?
    private var readyToWrite = true

    init(host: String, port: Int) {
        self.host = host
        self.port = port

        super.init()

        NSStream.getStreamsToHostWithName(self.host, port: self.port, inputStream: &inputStream, outputStream: &outputStream)

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

        inputStream!.scheduleInRunLoop(.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        outputStream!.scheduleInRunLoop(.currentRunLoop(), forMode: NSDefaultRunLoopMode)

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

    func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
        if (aStream == inputStream) {
            print("event received: inputStream")
            switch eventCode {
            case NSStreamEvent.ErrorOccurred:
                print("ErrorOccurred: \(aStream.streamError?.description)")
                break

            case NSStreamEvent.EndEncountered:
                print("EndEncountered: \(aStream.streamError?.description)")
                break

            case NSStreamEvent.HasBytesAvailable:
                print("HasBytesAvailable")
                break

            case NSStreamEvent.None:
                print("None")
                break

            case NSStreamEvent.OpenCompleted:
                print("opened!")
                break

            default:
                print("default")
            }
        } else if aStream == outputStream {
            print("event received: outputStream")
            switch eventCode {
            case NSStreamEvent.ErrorOccurred:
                print("ErrorOccurred: \(aStream.streamError?.description)")
                break

            case NSStreamEvent.EndEncountered:
                print("EndEncountered: \(aStream.streamError?.description)")
                break

            case NSStreamEvent.HasSpaceAvailable:
                print("HasSpaceAvailable")
                break

            case NSStreamEvent.None:
                print("None")
                break

            case NSStreamEvent.OpenCompleted:
                print("opened!")
                break

            default:
                print("default")
            }
        } else {
            print("another stream?")
        }

    }        

}

Solution

  • Just add the autoreleasepool inside the getStreamsToHostWithName method call since NSStream deallocated when the block execution completed

    autoreleasepool {
            NSStream.getStreamsToHostWithName(self.host, port: self.port, inputStream: &inputStream, outputStream: &outputStream)
    }
    

    I have tested the above code in xcode7 beta 4 and SocketTest tool(http://sourceforge.net/projects/sockettest/?source=typ_redirect) enter image description here

    Alternatively you can also use the CFStreamCreatePairWithSocketToHost API

    class TCPConnection: NSObject, NSStreamDelegate {
        var host:String?
        var port:UInt32?
        var inputStream: NSInputStream?
        var outputStream: NSOutputStream?
        var status = false;
        var output = "message"
        var bufferSize = 1024;
    init(host: String, port:UInt32){
        self.host = host
        self.port = port
        self.status = false
        output = ""
        super.init()
    }
    
    func stream(aStream: NSStream, handleEvent aStreamEvent: NSStreamEvent) {
        switch aStreamEvent {
    
        case NSStreamEvent.OpenCompleted:
            print("OpenCompleted")
            break
    
        case NSStreamEvent.HasBytesAvailable:
            print("HasBytesAvailable")
            break
    
        case NSStreamEvent.HasSpaceAvailable:
            print("HasSpaceAvailable")
            break
    
        case NSStreamEvent.EndEncountered:
            print("EndEncountered")
    
            //            aStream.close()
            aStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
            break
    
        case NSStreamEvent.None:
    
            break
    
        case NSStreamEvent.ErrorOccurred:
    
            break
    
        default:
            print("# something weird happend")
            break
        }
    }
    
    func connect() {
        print("# connecting to \(host):\(port)")
        var cfReadStream : Unmanaged<CFReadStream>?
        var cfWriteStream : Unmanaged<CFWriteStream>?
    
        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port!, &cfReadStream, &cfWriteStream)
        inputStream = cfReadStream!.takeRetainedValue()
        outputStream = cfWriteStream!.takeRetainedValue()
    
        inputStream!.delegate = self
        outputStream!.delegate = self
    
        inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
    
        inputStream!.open()
        outputStream!.open()
    }
    
    
    func read(){
        var buffer = [UInt8](count: bufferSize, repeatedValue: 0)
        output = ""
        while (self.inputStream!.hasBytesAvailable){
            var bytesRead: Int = inputStream!.read(&buffer, maxLength: buffer.count)
            if bytesRead >= 0 {
                output += NSString(bytes: UnsafePointer(buffer), length: bytesRead, encoding: NSASCIIStringEncoding)! as String
            } else {
                print("# error")
            }
            print("> \(output)")
        }
    }
    
    func send(message:String){
        let data:NSData = message.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
        let bytesWritten = self.outputStream!.write(UnsafePointer(data.bytes), maxLength: data.length)
        print("< send to \(host)")
    
    }
    }