Swift Only -- Reading from Nsinputstream

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.

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.

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;
}

Read IntegerType from NSInputStream

The read() method requires a mutable buffer pointer:

let n = withUnsafeMutablePointer(&buffer) { (p) in
self.read(UnsafeMutablePointer(p), maxLength: sizeof(T))
}

The placeholder type <UInt8> in UnsafeMutablePointer(p) can be inferred automatically from the context.

Note that there is a general problem with your approach: If the input
stream is not connected to a real file but to some communication channel
(e.g. a TCP socket, pipe, ...) then the only guarantee is that read()
waits until at least one byte is read and the assertion n == sizeof(T)
can fail. You have to read again in a loop until the required amount
is read.

How to convert NSInputStream to NSString or how to read NSInputStream

Got it. The stream that I tried to access hadn't been opened. Even then, it was read only. So I made a copy of it and then opened it. This still wasn't right though, I was only reading a byte at a time (a single character). So here's the final solution:

NSInputStream *stream = r.HTTPBodyStream;
uint8_t byteBuffer[4096];

[stream open];
if (stream.hasBytesAvailable)
{
NSLog(@"bytes available");
NSInteger bytesRead = [stream read:byteBuffer maxLength:sizeof(byteBuffer)]; //max len must match buffer size
NSString *stringFromData = [[NSString alloc] initWithBytes:byteBuffer length:bytesRead encoding:NSUTF8StringEncoding];

NSLog(@"another pathetic attempt: %@", stringFromData);
}

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

How do you correctly work with NSStreams (no blocking, read to the end of the data, and message retrying)?

You're probably working too hard here. NSStreamDelegate was designed before GCD, when the majority of Cocoa work was done on a single thread. While there are still reasons to use it in some cases, in most cases GCD and synchronous methods will make it much easier. For example, to read you'd do something like this:

import Foundation

enum StreamError: ErrorType {
case Error(error: NSError?, partialData: [UInt8])
}

func readStream(inputStream: NSInputStream) throws -> [UInt8] {
let bufferSize = 1024
var buffer = [UInt8](count: bufferSize, repeatedValue: 0)
var data: [UInt8] = []

while true {
let count = inputStream.read(&buffer, maxLength: buffer.capacity)

guard count >= 0 else {
inputStream.close()
throw StreamError.Error(error: inputStream.streamError, partialData: data)
}

guard count != 0 else {
inputStream.close()
return data
}

data.appendContentsOf(buffer.prefix(count))
}
}

let textPath = NSBundle.mainBundle().pathForResource("text.txt", ofType: nil)!
let inputStream = NSInputStream(fileAtPath: textPath)!
inputStream.open()
do {
let data = try readStream(inputStream)
print(data)
} catch let err {
print("ERROR: \(err)")
}

This will block the current queue, sure. So don't run it on the main queue. Put the do block into a dispatch_async. If you later need the data on the main queue, dispatch_async it back, just like any other background process.

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

}
}


Related Topics



Leave a reply



Submit