Use GCDAsyncSocket transfer file receiver - ios

I'm using GCDAsyncSocket. Write an application, which is used to transfer files between two devices, have a few problems when receiving the file:
When I want to send a size of about 60 KB text file to another receiving devices(iOS devices or iOS Simulator), need to add some packet information before data, like this:
2 bytes header version,
1 byte identify data or command,
4 bytes data size,
25 bytes the reserved space,
After 32 bytes is file data, text files or image files or other files.
So, before I call the socket.writeData, I write a NSMutableData,and send to receiver device.
var sendData: NSMutableData = NSMutableData()
var fileData: NSData = NSData(contentsOfFile: "file path", options: NSDataReadingOptions.DataReadingMapped, error: &error)
// TCP_HEADER_VERSION is Int16, value is 1
var headerVersion = TCP_HEADER_VERSION.bigEndian
var unPackVersionData: NSData = NSData(bytes: &headerVersion, length: 2)
sendData.appendData(unPackVersionData)
// dataType is Int, value is 0
var dataTypeTemp = dataType
sendData.appendData(NSData(bytes: &dataTypeTemp, length: 1))
// dataSize is Int64, value is fileData.size
var dataSizeTemp = dataSize.bigEndian
var packSizeData: NSData = NSData(bytes: &dataSizeTemp, length: 4)
sendData.appendData(packSizeData)
sendData.appendData(NSData(bytes: [0] as Array<Int>, length: 25))
sendData.appendData(fileData)
// send data
socket?.writeData(data, withTimeout: -1, tag: 0)
socket?.readDataWithTimeout(-1, tag: 0)
Every thing is ok at send device, the problem at receiver device
Received four packets. Every packet size is different, but I just send once at sender device, and socket header is chaotic.
// receiver device
func socket(sock: GCDAsyncSocket!, didReadData data: NSData!, withTag tag: Int) {
sock.readDataWithTimeout(-1, tag: 0)
println(data)
// PACKET_HEADER_LENGTH is 32
var packetHeader: NSData = data.subdataWithRange(NSMakeRange(0, PACKET_HEADER_LENGTH))
var packetContent: NSData = data.subdataWithRange(NSMakeRange(PACKET_HEADER_LENGTH, data.length - PACKET_HEADER_LENGTH))
var headerVersion: Int16 = 0
headerPacket.getBytes(&headerVersion, range: NSMakeRange(0, 2))
// headerVersion is wrong, not 1
headerVersion = headerVersion.bigEndian
// packetType is wrong, not 0
var packetType: Int = 0
headerPacket.getBytes(&packetType, range: NSMakeRange(2, 1))
var packetSize: Int64 = 0
headerPacket.getBytes(&packetSize, range: NSMakeRange(3, 4))
packetSize = packetSize.bigEndian
}
And I found some discussion about read data:
Having trouble programming streams
In end of the data add [GCDAsyncSocket CRLFData], at didReadData before use data add readDataToData:[GCDAsyncSocket CRLFData], but also not solve the problem.
Anyone can help me? Thanks!

Well.. I know how to solve this problem.
When I send packet to receiver, is like this:
64kb ... send
64kb ... send
64kb ... send
// ... send other datas to finish
At receiver device:
64kb ... waiting other datas
64kb ... waiting other datas
64kb ... OK call GCDAsyncSocket.didReadData delegate
// clear buffer
64kb ... waiting other datas
64kb ... OK call GCDAsyncSocket.didReadData delegate
So, When receive datas, need save a buffer file ,when send finish, handle buffer file to real file.
If anyone know how to set GCDAsyncSocket buffer size, please tell me.
Hope I can help others guys, thanks.

Related

Sending bytes on outputstream with Swift

I'm working with a custom network protocol that requires a precise bytes sequence to do a sort of handshake, as example the first call should send a body like:
0003joy that, translated in a [UInt8] should be [0x00,0x00,0x00,0x03,0x6a,0x6f,0x79]
(please note that the first 4 numbers should not be converted to char... I'm sending the numeric value, as per protocol request)
I'm trying to send this sequence to an outputstream but I'm wondering if the steps I'm following are correct, here is my code... do you see anything strange that might prevent this sequence to reach the server?
// Create Bytes sequence
let bytes:[UInt8] = [0x00,0x00,0x00,0x03,0x6a,0x6f,0x79]
// Convert Bytes array do Data
let dt = Data(bytes)
// Send Data to stream
_ = dt.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
outputStream.write(pointer, maxLength: dt.count)
}
Also, do I need to convert the bytes sequence to Data? is there a way to send bytes sequence directly into the socket without converting it into Data?
I don't see anything wrong with your code but the conversion to Data is not needed:
bytes.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
outputStream.write(pointer, maxLength: bytes.count)
}
since [UInt8] conforms to ContiguousBytes.
Actually, the following is also possible:
bytes.withUnsafeBufferPointer {
guard let baseAddress = $0.baseAddress else { return }
outputStream.write(baseAddress, maxLength: bytes.count)
}

Size of the NSInputStream buffer

I'm trying to use NSInputStream for receiving data using TCP socket connection. On the server side I send data size before sending of the data itself. on the iOS client side I need to extract first 4 bytes from the NSInputStream, because I need to check if size of data has received completely, but I have a problem with it:
...
case NSStreamEvent.HasBytesAvailable:
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var readBufferRef = UnsafeMutablePointer<UnsafeMutablePointer<UInt8>>()
var readBufferLengthRef = 0
let readBufferIsAvailable = inputstream.getBuffer(readBufferRef, length: &readBufferLengthRef)
...
}
}
break
After receiving of data readBufferLengthRef always equals to 0.
How it can be?
And how can I get size of the NSInputStream buffer?
UPD:
Code:
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](count: 1024, repeatedValue: 0)
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var readBufferRef: UnsafeMutablePointer<UInt8> = nil
var readBufferLengthRef = 0
let readBufferIsAvailable = inputstream.getBuffer(&readBufferRef, length: &readBufferLengthRef)
//debugger: readBufferLengthRef = (int)0
}
}
break
In your code, readBufferRef is defined as a "pointer to a pointer"
but never allocated, and therefore it is the NULL pointer.
What you should do is to pass the address of an
UnsafeMutablePointer<UInt8> as an inout argument to the function
(assuming Swift 2):
var readBufferRef: UnsafeMutablePointer<UInt8> = nil
var readBufferLengthRef = 0
let readBufferIsAvailable = inputStream.getBuffer(&readBufferRef, length: &readBufferLengthRef)
On return, readBufferRef is set to the read buffer of the stream (valid until the next read operation), and readBufferLengthRef contains
the number of available bytes.

How to create a NSData from hex value in swift

I'm a swift/iOS newbie and I have a problem to solve.
I'm trying to get data from Texas Instrument SensorTag 2. To activate a sensor, following the instructions, I have to write a binary string in the configuration bank of my sensor.
I have this snippet of code:
if SensorTag.validConfigCharacteristic(thisCharacteristic) {
// Enable Sensor
let enableByte = SensorTag.getEnableByteFor(thisCharacteristic)
self.sensorTagPeripheral.writeValue(enableByte, forCharacteristic: thisCharacteristic, type: CBCharacteristicWriteType.WithResponse)
}
and I write the function to get the value to write. enableByte type is NSData.
class func getEnableByteFor(thisCharacteristic: CBCharacteristic) -> NSData {
print(thisCharacteristic.UUID)
var enableValue = 0
if thisCharacteristic.UUID == MovementConfigUUID {
enableValue = ...
} else { // any other activation
enableValue = 1
}
return NSData(bytes: &enableValue, length: sizeof(UInt8))
}
For every sensor I have to write a 1 if I want to enable the sensor and 0 if I want to disable it, but with the movement sensor I have to write according to this guide 16 bits (2 byte). For my config I have to write a binary value of 0000000001111111, 0x007F. How can I initialize a NSData object with value 0x007F?
Try this:
let bytes : [CChar] = [0x0, 0x7F]
let data = NSData(bytes: bytes, length: 2)
NSData(bytes:length:) creates an NSData object from a byte stream. In Objective-C, this byte stream is of type char *. The Swift equivalent is [CChar]. The question (and another answer) use an Int to represent this byte stream. This is wrong and dangerous.
var enableValue = 0 // enableValue is a 64-bit integer
NSData(bytes: &enableValue, length: sizeof(UInt8)) // this trims it to the first 8 bits
It works because x86 uses Little Endian encoding, which puts the least significant byte first. It will fail on PowerPC, which uses Big Endian. ARM uses switchable endianness so it may or may not fail there. When the situation call for exact bit layout, you should not rely on the architecture's endianness:
class func getEnableByteFor(thisCharacteristic: CBCharacteristic) -> NSData {
print(thisCharacteristic.UUID)
let enableValue : [CChar]
if thisCharacteristic.UUID == MovementConfigUUID {
enableValue = [0x0, 0x7F]
} else { // any other activation
enableValue = [0x1]
}
return NSData(bytes: enableValue, length: enableValue.count)
}
Much shorter solution taking in account byte order:
NSData(bytes: [UInt16(0x007F).bigEndian], length: 2)
Now there is nothing wrong with using [UInt16] as byte stream because UInt16 has bigEndian property that returns the big-endian representation of the integer changing byte order if necessary.

Save binary information to a file

I receive binary information via stream in Swift. Lets say the information is a picture. I now want to save the picture. How is this possible?
I tried following:
let bufferSize = 154000
var buffer = [UInt8](count: bufferSize, repeatedValue: 0)
var bytesRead = inputStream?.read(&buffer, maxLength: bufferSize)
if bytesRead > 0 {
var bytesWrittenSoFar = 0
do {
var diffbytes = bytesRead! - bytesWrittenSoFar
fileStream?.open()
fileStream?.write(UnsafePointer(&buffer[bytesWrittenSoFar]), maxLength: diffbytes)
} while (bytesWrittenSoFar != bytesRead);
But when I try to write (fileStream?.write...) I get following error: "Could not find an overload for 'init' that accepts the supplied arguments
Thank you for your answer in advance!
The problem is with the initialization of UnsafePointer. In this case, you don't need it at all, you can just pass &buffer[bytesWrittenSoFar], as that is an acceptable value to pass to a function that needs an unsafePointer, per the discussion in the apple book "Using Swift with Cocoa and Objective-C".

Swift - converting from ConstUnsafePointer<()>

I'm on beta 3. Consider the following Objective-C line:
const uint8_t *reportData = [data bytes];
where data is a NSData object.
How would this line be re-written in Swift?
data.bytes is of type ConstUnsafePointer<()>, and while there's plenty of documentation on how to create a pointer type in Swift, there isn't much info on how to work with them.
edit:
To add some context, I'm trying to port Apple's HeartRateMonitor sample code to Swift. This code interacts with BLE heart rate monitors. This code I'm working on translates the data received by the Bluetooth system into an int for use in the UI. The data received from BT is expected to be an array of uints, element 0 is used to check for a flag and element 1 contains the value.
Here's the same Objective-C line in context:
const uint8_t *reportData = [data bytes];
uint16_t bpm = 0;
if ((reportData[0] & 0x01) == 0)
{
/* uint8 bpm */
bpm = reportData[1];
}
What you were looking for was how to convert NSData to an array of UInt8. Here's how.
import Foundation
let path = "/etc/csh.cshrc" // something existent
let data = NSData(contentsOfFile: path)
var aofb = [UInt8](count:data.length, repeatedValue:0)
data.getBytes(&aofb, length:data.length)
for c in aofb {
let s = UnicodeScalar(Int(c)).escape(asASCII:true)
println("\(c):\(s)")
}
Just built following code (Note code below works on Beta 3, ConstUnsafePointer<()> needs to be changed to COpaquePointer in order to work on Beta 2, please see edit history for more information)
var dataPath = NSBundle.mainBundle().pathForResource("TestData", ofType: "") // What I have in TestData is "GREETINGS WORLD"
var originalData = NSData(contentsOfFile: dataPath)
var dataLength = originalData.length
println("original data: \(originalData)") // Output original data
// Data to bytes
var reportBytes: ConstUnsafePointer<()> = originalData.bytes
var bytesToString = NSString(bytes: reportBytes, length: dataLength, encoding: NSUTF8StringEncoding)
println("string from bytes: \(bytesToString)")
// Bytes to data
var bytesToData = NSData(bytes: reportBytes, length: dataLength)
println("data from bytes: \(bytesToData)")
Console log
original data: <47524545 54494e47 5320574f 524c44>
string from bytes: GREETINGS WORLD
data from bytes: <47524545 54494e47 5320574f 524c44>
Also found this may help
ConstUnsafePointer<T>
/// This type stores a pointer to an object of type T. It provides no
/// automated memory management, and therefore the user must take care
/// to allocate and free memory appropriately.
Hope this shed light.
Looking at handling bluetooth heart rate monitors in Swift now I found the simplest way to get the NSData byte values to UInt8 format:
let bytes = UnsafePointer<UInt8>(data.bytes)
if bytes[0] & 0x01 == 0 {
NSLog("BPM \(bytes[1]")
}

Resources