Receiving Data from Nsinputstream in Swift

Receiving Data from NSInputStream in Swift

You're missing the event hasSpaceAvailable, which I expect is occurring when it says "unknown". It's telling you that it is ready to receive more data.

Generally, I avoid using default in switch statements for enums, since the compiler will tell you when you've missed something.

SWIFT ONLY -- Reading from NSInputStream

I have figured it out myself.

Look at this simple code:

let data: NSData = "Jonathan Yaniv.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
let stream: NSInputStream = NSInputStream(data: data)

var buffer = [UInt8](count: 8, repeatedValue: 0)

stream.open()

if stream.hasBytesAvailable {
let result :Int = stream.read(&buffer, maxLength: buffer.count)
}

// result = 8 -- because of the size of the buffer.
// buffer contains the first 8 bytes repreenting the word "Jonathan"

Explanation:
The read method signature:
stream.read(<#buffer: UnsafeMutablePointer#>, maxLength: <#Int#>)

It gets a UnsafeMutablePointer as a first parameter, which means the method expects to get a POINTER to an array of type UInt8 - NOT the array itself

Therefore, we add the & notation before the name of the buffer variable.
&buffer == the pointer to the UInt8 array object named buffer.

Get input from STDIN using NSInputStream in Swift

The problem is using UnsafeMutablePointer<UnsafeMutablePointer<UInt8>>() for readBufferRef and UnsafeMutablePointer<Int>() for readBufferLengthRef.

The modified code

#!/usr/bin/env xcrun swift

import Foundation

let inputStream = NSInputStream(fileAtPath: "/dev/stdin")

inputStream!.open()

println(inputStream!.hasBytesAvailable)

if(inputStream!.hasBytesAvailable) {
// var readBufferRef = UnsafeMutablePointer<UnsafeMutablePointer<UInt8>>()
var readBufferRef = UnsafeMutablePointer<UInt8>()
// var readBufferLengthRef = UnsafeMutablePointer<Int>()
var readBufferLengthRef = 0
// let readBufferIsAvailable = inputStream!.getBuffer(readBufferRef, length: readBufferLengthRef)
let readBufferIsAvailable = inputStream!.getBuffer(&readBufferRef, length: &readBufferLengthRef)

if readBufferIsAvailable {
println("Yay")
}
}

println("Heck")

Stream to Get Data - NSInputStream

There are two ways to get data from a stream: polling and using stream events.

Polling is simpler, but will block the thread it is running in. If you use this method, you don't need to perform the setDelegate: or scheduleInRunLoop:forMode: calls. Polling is performed by repeatedly calling read:maxLength:.

NSInteger result;
uint8_t buffer[BUFFER_LEN]; // BUFFER_LEN can be any positive integer
while((result = [iStream read:buffer maxLength:BUFFER_LEN]) != 0) {
if(result > 0) {
// buffer contains result bytes of data to be handled
} else {
// The stream had an error. You can get an NSError object using [iStream streamError]
}
}
// Either the stream ran out of data or there was an error

Using stream events requires setting the delegate and adding the stream to a run loop. Instead of blocking the thread, the stream will send a stream:handleEvent: message to its delegate when certain events occur, including when it receives data. The delegate can then retrieve the data from the stream. Here is an example stream:handleEvent: method:

- (void)stream:(NSInputStream *)iStream handleEvent:(NSStreamEvent)event {
BOOL shouldClose = NO;
switch(event) {
case NSStreamEventEndEncountered:
shouldClose = YES;
// If all data hasn't been read, fall through to the "has bytes" event
if(![iStream hasBytesAvailable]) break;
case NSStreamEventHasBytesAvailable: ; // We need a semicolon here before we can declare local variables
uint8_t *buffer;
NSUInteger length;
BOOL freeBuffer = NO;
// The stream has data. Try to get its internal buffer instead of creating one
if(![iStream getBuffer:&buffer length:&length]) {
// The stream couldn't provide its internal buffer. We have to make one ourselves
buffer = malloc(BUFFER_LEN * sizeof(uint8_t));
freeBuffer = YES;
NSInteger result = [iStream read:buffer maxLength:BUFFER_LEN];
if(result < 0) {
// error copying to buffer
break;
}
length = result;
}
// length bytes of data in buffer
if(freeBuffer) free(buffer);
break;
case NSStreamEventErrorOccurred:
// some other error
shouldClose = YES;
break;
}
if(shouldClose) [iStream close];
}

Use NSOutputstream and NSInputstream to write and read every interval

Looks like NSInputStream and NSOutputStream are not supposed to be re-used after a stream finishes. I fix the problem by changing the code to below:

func start(ipAddress ipAddress: String) {
self.conn = Connection(serverAddress: ipAddress)
self.sendTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "getTimeAndUpdateUI", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.sendTimer!, forMode: NSDefaultRunLoopMode)
}

func getTimeAndUpdateUI() {
self.conn!.connect()
let time = self.conn!.receive()
self.timeLabel.text = "Current time is \(time)"
self.conn!.disconnect()
}

Every time iOS poll the server for information, it recreates an instance of NSInputStream and NSOutputStream.

Any suggestion to improve is welcome. Thanks.

NSInputStream / NSOutputStream not opening

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)
Sample Image

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)")

}
}

NSinputstream read data is returning null value?

I think the code is absolutely fine and it should read the data, may be after you've read 4096 bytes there might be some more bytes available and loop continues, and you are initialising the output variable again, So you might be missing it.

Use the following snippet:

if (aStream == inputStream) {
uint8_t buffer[4096];
int len;
NSString* tempOutput = @"";

while ([inputStream hasBytesAvailable]) {

len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
NSLog(@"\nThe length is -- %d\n",len);
if (len > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:len];
output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
tempOutput = [tempOutput stringByAppendingString:output];
// output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];

}
}
output = tempOutput;
}


Related Topics



Leave a reply



Submit