I'm send a string to my server (PHP) with the code below:
NSString* str = #"teststring";
NSData* dataToSend = [str dataUsingEncoding:NSUTF8StringEncoding];
uint8_t *dataBytes = (uint8_t *)[dataToSend bytes];
uint64_t length = dataToSend.length;
[outputStream write:dataBytes maxLength:length];
The problem with this is code, is that I don't receive the string (Server recognize that a user enter and I receive a handshake in Xcode console) I test others codes and works very well (receive data), but in this time I'm trying by my own. I believe I'm not converting the message to the appropriate format, and that's why the message is not being received, am I right? If so how I could convert this message to the appropriate format?
Are you sure the output stream has space for writing that many bytes at the moment when you call the write:... method? You should only call write:... on an output stream from within your stream:handleEvent: delegate method, and only if the event code is NSStreamEventHasSpaceAvailable.
And you must check the result to see how many bytes were actually written. If it is fewer bytes than you sent, you must enqueue the remaining data to resend later, whenever your stream:handleEvent: delegate method gets called again.
For more details, see:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Streams/Articles/WritingOutputStreams.html
Related
I have an external device that communicates via Bluetooth with an iPhone Application that has support for iOS6+.
The problem is that I need to write more than 20 bytes to a characteristic and from what I've read and tried this is not possible using BLE.
Here is a basic structure of my method:
NSData * payload = ... //53 bytes, last 2 bytes CRC
[peripheral writeValue:payload forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
The error I get when sending >20 bytes is CBErrorOperationCancelled (code 5) and CBErrorConnectionTimeout (code 6). I believe this is normal.
I divided the data in chunks of 20 bytes (example here: https://github.com/RedBearLab/iOS/issues/8) and the data is not written well on the device:
NSData * chunk1 = ... //first 20 bytes
NSData * chunk2 = ... //next 20 bytes
...
NSData * chunkN = ... //remaining bytes + CRC from whole data
[peripheral writeValue:chunk1 forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
[peripheral writeValue:chunk2 forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
...
[peripheral writeValue:chunkN forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
I think the external device treats each byte array, not the whole data.
Also, I tried to split the payload data in chunks of 18 bytes and append the CRC to each chunk of bytes I send. This got me the same result as in appending CRC to the last chunk of data.
In the Android version of the application the whole data is sent, so I know that the device can read >20 bytes on a single command.
My questions are:
Is it possible to send the whole data in chunks to the external
device without modifying the peripherals hardware / software?
Is there a flag / signal byte in the first chunk of data that notifies
the device the length of the whole message?
I've read that in iOS 7 is possible to send a larger byte array. If
I make support for iOS 7+ will it solve my problem?
What alternatives do I have to CB?
Any help will be appreciated. Many thanks!
EDIT
When I try sending data in chunks like in the BTLE sample I get the following response:
// NSLog - data that is written on the peripheral
// Chunk 1 : <2047d001 0f002000 315b0011 b4425543 41524553>
// Chunk 2 : <54202020 20202020 20202020 20202020 20202020>
// Chunk 3 : <20202020 2009059b 56210988 b9b50408 02c7123d>
// Write to peripheral
[self.twCurrentPeripheral writeValue:chunk1 forCharacteristic:self.twCharacteristic type:CBCharacteristicWriteWithoutResponse];
[self.twCurrentPeripheral writeValue:chunk2 forCharacteristic:self.twCharacteristic type:CBCharacteristicWriteWithoutResponse];
[self.twCurrentPeripheral writeValue:chunk3 forCharacteristic:self.twCharacteristic type:CBCharacteristicWriteWithResponse];
// NSLog - Response from peripheral
// Data update char : <20470000 04d00101 00ab42>
// Data update char : <204700>
// Data update char : <0004d001 0100ab42>
// Data update char : <204700>
// Data update char : <0504d001 032c6bcf>
The correct response should be (like in the Android version):
// Response <20470000 04d00101 00ab42>
// Response <20470000 04d00105 006786>
You need to send the data in 20 bytes chanks. Each packet should be at the size of 20 bytes. The other side (the BLE central) should accept the packets and save them until he gets all the packets he was waiting for. For this to happen you will have to create some kind of protocol:
Sending the type of message then the length of the message in bytes and the centeral should read this header and then save the "length" bytes he is waiting for in this message and then parse the entire data.
Note that IOS might fail sending some of the packets when there are many packets to send very fast, so when calling
success = [self.peripheral updateValue:chunk forCharacteristic:myChar onSubscribedCentrals:nil];
success will tell you if the send was successful, if it was not you need to resend this packet.
in that case you should implement the IOS BLE protocol CBPeripheralManagerDelegate
and at the method peripheralManagerIsReadyToUpdateSubscribers you should retry sending the same packet again.
You need to chunk the data in 20 byte length pieces, you got that part right. If you use the "write with response" method, then before sending the next piece of data, you should wait for the peripheral:didWriteValueForCharacteristic:error: callback to happen on your peripheral delegate before sending the next piece. In case of "write without response", you should wait before sending out the next chunk. The stuff you add in the in the writeValue call will be buffered. If the buffer is full, then your call will be dropped.
I am just getting started with socket programming on iOS and I am struggling to determine the use of the NSStreamEventHasSpaceAvailable event for NSOutputStreams.
On the one hand, Apple's official documentation (Listing 2) shows that in the -stream:handleEvent: delegate method, data should be written to the output buffer with -write:maxLength: message, passing data continually from a buffer, whenever the NSStreamEventHasSpaceAvailable event is received.
On the other hand, this tutorial from Ray Wenderlich and this iOS TCP socket example on GitHub ignore the NSStreamEventHasSpaceAvailable event altogether, and just go ahead and -write:maxLength: to the buffer whenever they need to (even ignoring -hasSpaceAvailable).
Thirdly, there is this example code which appears to do both...
My question is therefore, what is the correct way(s) to handle writing data to an NSOutputStream that is attached to a socket? And of what use is the NSStreamEventHasSpaceAvailable event code if it can (apparently) be ignored? It seems to me that there is either very fortunate UB happening (in examples 2 and 3), or there are several ways of sending data through a socket-based NSOutputStream...
You can write to a stream at any time, but for network streams, -write:maxLength: returns only until at least one byte has been written to the socket write buffer. Therefore, if the socket write buffer is full (e.g. because the other end of the connection does not read the data fast enough),
this will block the current thread. If you write from the main thread, this will block
the user interface.
The NSStreamEventHasSpaceAvailable event is signalled when you can write to the stream
without blocking. Writing only in response to that event avoids that the current thread
and possibly the user interface is blocked.
Alternatively, you can write to a network stream from a separate "writer thread".
After seeing #MartinR's answer, I re-read the Apple Docs and did some reading up on NSRunLoop events. The solution was not as trivial as I first thought and requires some extra buffering.
Conclusions
While the Ray Wenderlich example works, it is not optimal - as noted by #MartinR, if there is no room in the outgoing TCP window, the call to write:maxLength will block. The reason Ray Wenderlich's example does work is because the messages sent are small and infrequent, and given an error-free and large-bandwidth internet connection, it will 'probably' work. When you start dealing with (much) larger amounts of data being sent (much) more frequently however, the write:maxLength: calls could start to block and the App will start to stall...
For the NSStreamEventHasSpaceAvailable event, Apple's documentation has the following advice:
If the delegate receives an NSStreamEventHasSpaceAvailable event and does not write anything to the stream, it does not receive further space-available events from the run loop until the NSOutputStream object receives more bytes. ... ... You can have the delegate set a flag when it doesn’t write to the stream upon receiving an NSStreamEventHasSpaceAvailable event. Later, when your program has more bytes to write, it can check this flag and, if set, write to the output-stream instance directly.
It is therefore only 'guaranteed to be safe' to call write:maxLength: in two scenarios:
Inside the callback (on receipt of the NSStreamEventHasSpaceAvailable event).
Outside the callback if and only if we have already received the NSStreamEventHasSpaceAvailable but elected not to call write:maxLength: inside the callback itself (e.g. we had no data to actually write).
For scenario (2), we will not receive the callback again until write:maxLength is actually called directly - Apple suggest setting a flag inside the delegate callback (see above) to indicate when we are allowed to do this.
My solution was to use an additional level of buffering - adding an NSMutableArray as a data queue. My code for writing data to a socket looks like this (comments and error checking omitted for brevity, the currentDataOffset variable indicates how much of the 'current' NSData object we have sent):
// Public interface for sending data.
- (void)sendData:(NSData *)data {
[_dataWriteQueue insertObject:data atIndex:0];
if (flag_canSendDirectly) [self _sendData];
}
// NSStreamDelegate message
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
// ...
case NSStreamEventHasSpaceAvailable: {
[self _sendData];
break;
}
}
// Private
- (void)_sendData {
flag_canSendDirectly = NO;
NSData *data = [_dataWriteQueue lastObject];
if (data == nil) {
flag_canSendDirectly = YES;
return;
}
uint8_t *readBytes = (uint8_t *)[data bytes];
readBytes += currentDataOffset;
NSUInteger dataLength = [data length];
NSUInteger lengthOfDataToWrite = (dataLength - currentDataOffset >= 1024) ? 1024 : (dataLength - currentDataOffset);
NSInteger bytesWritten = [_outputStream write:readBytes maxLength:lengthOfDataToWrite];
currentDataOffset += bytesWritten;
if (bytesWritten > 0) {
self.currentDataOffset += bytesWritten;
if (self.currentDataOffset == dataLength) {
[self.dataWriteQueue removeLastObject];
self.currentDataOffset = 0;
}
}
}
I am developing one application in which I need to update multiple values like Engine RPM,Speed etc. parameters at a time using OBD connector. I need to achieve asynchronous command/response . For this I am sending commands using [gcdAsyncSocket writeData:data withTimeout:-1 tag:uniqueTag]; asynchronously with unique tag.
But when gcdAsync's delegate method "socketDidReadDatawithTag" is called, it returns the data but it is not proper.e.g. If I have sent one command "010C\r" (Read RPM), and "010D\r" (Speed),with Tag 263 and 264 respectively ,and if I parse the response with tag 264 in socketDidReadDatawithTag , sometimes it returns me the data of RPM. (My response gets Mixed up or OBD Device is unable to handle asynchronous response)
NSLog(#"Command Sent for Async : %#",commandString);
NSData *data = [commandString dataUsingEncoding:NSASCIIStringEncoding];
long obdObjectTag = [obdObject getPIDTag];//Unique Tag
[gcdAsyncSocket writeData:data withTimeout:-1 tag:obdObjectTag];
NSData *readData = [#">" dataUsingEncoding:NSASCIIStringEncoding];
[gcdAsyncSocket readDataToData:readData withTimeout:-1 tag:obdObjectTag];
And in socketdidReadDatawithTag data and tag are mismatched.
The OBD-II connector (I assume it's an ELM-327) cannot really handle asynchronous calls as far as I know.
It cannot handle multiple requests at once. You send 1 command, and the OBD-II device will gather that info from the OBD-bus and return with an answer. Then it will process your next command. Ofcourse, the commands you send end up in a buffer, that will be processed one by one.
I'm thinking this might be a problem for you to make it, but I'm not sure.
I'm not familiar at all with the ios programming and what happens with those tags. You set those tags to identify what paramters are the data for?
In the reply data, you can also see for what parameter it is meant, so in the answer itself you can see that data represents RPM or Speed etc.
I hope the OBD-II part has shed some light on it. Will check this question more for some discussion perhaps.
I have an RFID scanner attached to a RedPark serial cable connected to an iPad app. When people scan their RFID cards, I get a callback with -readBytesAvailable:. However, sometimes it doesn't give me the entire RFID in one call. Sometimes it send it in two calls.
How can I determine if I've received everything? When my code takes the first callback's data and tries to use it, I get an error, because let's say the RFID was "123456789" sometimes I'll get one call with #"12" and a second call with #"3456789". So I try to process #"12" and get a user not found error, then I try to process #"3456789" and get a user not found error.
How can I tell if I'm done reading data? The lengths of the RFIDs can vary from vendor to vendor, so I can't just assume I need to read a certain number of digits.
This is the method I use to receive the data from the scanner through the RedPark:
- (void) readBytesAvailable:(UInt32)length {
NSLog(#"readBytesAvailable: %lu", length);
UInt8 rxLoopBuff[LOOPBACK_TEST_LEN];
[self.rfidManager read:rxLoopBuff Length:length];
NSString *rfid = [[NSString alloc] initWithBytes:rxLoopBuff length:length encoding:NSUTF8StringEncoding];
NSLog(#"rfid=%#", rfid);
[self receivedScanOfRFID:rfid];
}
Serial port gives you no control over packetization. Data is just a stream of bytes with no way to predict which bytes appear in each read call. You have to parse the data stream itself to interpret the contents and understand start/end of your messages. You either need to look for a reliable terminating character or potentially use a timeout approach where you do multiple reads until you get no more data for some period of time. I don't recommend the timeout approach.
I am sending data to the server twice. First, I send "Hello world" and then I send "Server".
But the server received the data at 1 read. But the server have to read the data in a two-read operation.
Also, I write the data. Then read data from server and then I write the data.
In this case, the server can read the first data. But server can not read the second data.
The server uses read, write, read.
So how to overcome this issue? How do I write data to socket in BlackBerry?
What you describe is how TCP is supposed to work by default. What you are seeing is the Nagle algorithm (RFC 896) at work, reducing the number of outbound packets being sent so they are processed as efficiently as possible. You may be sending 2 packets in your code, but they are being transmitted together as 1 packet. Since TCP is a byte stream, the receiver should not be making any assumptions about how many packets it gets. You have to delimit your packet data in a higher-level protocol, and the receiver has to process data according to that protocol. It has to handle cases where multiple packets arrive in a single read, a single pakcet arriving in multiple reads, and everything in between, only processing packet data when they have been received in full, caching whatever is left over for subsequent reads to process when needed.
Hard to say without a little more detail, but it sounds like you're using 1-directional communication in the first case - i.e. the client writes, then writes again. There are any number of reasons that the server would receive the 2 writes as 1 read. Buffering on the client, somewhere in the wireless stack (or in the BES), buffering on the server side. All of those are legal with TCP/IP.
Without knowing anything more about your solution, have you thought about defining a small protocol - i.e. the client writes a known byte or bytes (like a 0 byte?) before sending the second write? Then the server can read, then recognize the delimiting byte, and say 'aha, this is now a different write from the client'?
As previously said this is an expected TCP behavior to save bandwidth. Note that to deliver your package TCP adds lot of data (e.g. destination port,sequence number, checksums...).
Instead of flushing the data I´ll recommend you to put more work in your protocol. For example you can define a header that contains the number of bytes to read and then the payload (the actual data).
The following code is a protocol encoded in an string with the structure [length];[data]
StringBuffer headerStr = new StringBuffer();
StringBuffer data = new StringBuffer();
//read header
char headerByte = dataInputStream.readChar();
while (headerByte != ';') {
headerStr.append(headerByte);
headerByte = dataInputStream.readChar();
}
//header has the number of character to read
int header= Integer.parseInt(headerStr.toString());
int bytesReaded = 1;
char dataByte = dataInputStream.readChar();
//we should read the number of characters indicated in the header
while (bytesReaded < header) {
data.append(dataByte);
dataByte = dataInputStream.readChar();
bytesReaded++;
}
For the first query, I guess you are using TCP. If you use UDP, then the server will read the packets in the order you want.
Can you be more clear/elaborative on the second query ?
I would try explicitly telling Connector.open to open up the stream as read_write. Then I would ensure that I flush my connections after each time I talked to the server.
SocketConnection connection = (SocketConnection) Connector.open(url, Connector.READ_WRITE);
OutputStream out = connection.openOutputStream();
// ... write to server
out.flush()
I got a solution to overcome to extract both the string
On sender device
Create a header which contains details of that data eg the data
length, datatype etc.
Add this header to the actual data and send it
On recipient device
read the header
retrieve the actual data length from the header
read the next data upto the data length as specified by the header