CoreBluetooth: number of bytes sent != number of bytes received - ios

I have an app that is acting as a peripheral and another app that is acting as a central.
The central app is reading a characteristic on the peripheral:
[self.service.peripheral readValueForCharacteristic:self.packetCharacteristic]
The peripheral handles the request as such:
- (void)peripheralManager:(CBPeripheralManager *)manager didReceiveWriteRequests:(NSArray *)requests
{
for (CBATTRequest *request in requests)
{
if ([request.characteristic.UUID isEqual:self.service.packetCharacteristic.UUID])
{
NSData *value = self.packets[0]; // This value's length logs at 512 bytes, tested 500 bytes too
request.value = value;
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}
}
}
The size of NSData *value is equal to 512 bytes. Note that I have also tested this with 500 bytes.
The central then receives the the delegate call as such:
- (void)didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (characteristic == self.packetCharacteristic)
{
NSLog(#"PACKET RECEIVED: %lu bytes", (unsigned long)characteristic.value.length);
}
}
The NSLog statement states that the value received is 536 bytes regardless of if I send 500 or 512 bytes. The bytes sent and the bytes received are identical until about a quarter of the way through (by looking at the HEX value provided by Xcode), the rest of the bytes are completely different.
The questions are as follows:
1. Why am I receiving more bytes than I have sent?
2. What are these bytes? What do they represent?
3. Where can I find documentation on this? I have reviewed the CoreBluetooth docs/guides over and over and can't find anything indicating that this could happen.
4. Could this be related to endianness?
EDIT #1
Ok, so I have done a little bit more testing and found out the following...
The MTU seems to be 134 bytes (from iOS to iOS). As soon as the data being sent is equal or bigger than 134 bytes, CoreBluetooth is calling peripheralManager:didReceiveReadRequest: 4 times.
My assumption is that because the data being sent is at least equal to the MTU, CoreBluetooth doesn't know whether or not it is done sending all the data. Therefore it calls peripheralManager:didReceiveReadRequest: N number of times until N x MTU covers the maximum possible size of a characteristic's value (512 bytes). In my particular case, 4 x 134 bytes equals the magical 536 bytes.
Note that the request's offset is being updated every time, in my particular case, 0, 134, 268, 402.
Edit #2
Ok, figured it out.
I was semi-right in my assumption. CoreBluetooth calls peripheralManager:didReceiveReadRequest: N time until the data being sent is smaller than the MTU. If the data being sent is equal or larger than the MTU, CoreBluetooth will keep calling peripheralManager:didReceiveReadRequest: until it N x MTU covers the max size (512 bytes). If the data % MTU == 0 then peripheralManager:didReceiveReadRequest: will be called one last time where you have to return 0 bytes.

Answering my own question.
Look at edit #2.

Related

Why is the MTU different for With Response vs Without Response?

On iOS when I request the MTU I get different values for .withoutResponse vs .withResponse. I realize it could be different since the function takes that parameter, but why is it different?
Here's on device (BLE 4.2):
(lldb) po peripheral.maximumWriteValueLength(for: .withoutResponse)
182
(lldb) po peripheral.maximumWriteValueLength(for: .withResponse)
512
On another device (BLE 5) I get:
(lldb) po peripheral.maximumWriteValueLength(for: .withoutResponse)
509
(lldb) po peripheral.maximumWriteValueLength(for: .withResponse)
512
Write Without Response is directly related to the MTU. (MTU - 3).
When you use writeWithResponse, iOS can either perform a Write With Response (max value length is MTU - 3), or a "Write Long Characteristic Values", which uses several ATT transactions to write the value. In the latter case the only restricting factor is the maximum length of a characteristic value, which is set to 512 bytes by the standard.

Twilio queue overflow error: how large is the queue?

Twilio's Message resource has a "status" property that indicates whether a SMS message is "queued", "sending", "failed", etc. If a Message instance failed to deliver, one possible error message is "Queue overflow". In the Twilio documentation, the description for this error case is: "You tried to send too many messages too quickly and your message queue overflowed. Try sending your message again after waiting some time."
Is the queue referenced in error code 30001 an instance of this resource? https://www.twilio.com/docs/api/rest/queue
Or is the queue (in the case of a 30001 error code) something that Twilio maintains on their end? If Twilio does throttling behind the scenes (queueing SMS messages per sending phone number), what is the size of that queue? How much would we have to exceed the rate limit (per phone number) before the queue overflow referenced in error code 30001 occurs?
Emily, message queue is not related to the queue resource you linked to above and it is something maintained on our end.
Twilio can queue up to 4 hours of SMS. This means, we can send out 1 sms per second, if there are more than 14,400 messages in the queue, all the messages queued up after will fail with 30001 error queue overflow and will not be sent. This info is for Long Code numbers. The link above explains processing for other scenarios.
A few suggestions to avoid the error:
Keep messages to at most 160 characters if possible. But if not
possible, calculate how many SMS messages each message will be (if
you are not sure you can always send 1 test message and see how much
you are charged for that message).
Based on the assumption that your messages is 160 characters,
throttle the sending rate to 3600 messages per hour (1 messaage/sec *
60 sec/min * 60 min/hr).
Please let me know if you've got any other questions.
Each of the Twilio phone numbers(senders) has a separate queue in which 14400(4 hr x 60 min x 60 sec) message segments can be queued. 1 segment is sent in one second.
What is a message segment?
A message segment is not a complete message but a part of a message.
Normally SMS is sent in terms of message segments and all message
segments are combined on the user’s mobile to create the actual SMS.
Twillio message segment config:
1 character = 8 bits(1 byte)
GSM encoding = 7 bit per character
UCS-2 encoding = 16 bit per character
Data Header = 6 bytes per segment
Summary: Each character takes 8 bits, If GSM encoding is used, each
character will take 7 bits or if UCS-2 encoding is used, each char
will take 16 bits. In the case of multiple segments, 6 bytes per
segment will be used for data headers(responsible for combining all
segments of the same SMS on user mobile)
Character per Message Segment:
GSM encoding when single segment = (140 char bytes x 8 bits)/ 7 bits =
160 characters
UCS-2 encoding when single segment = (140 char bytes x 8 bits)/ 16
bits = 70 characters
GSM encoding when multiple segment = ((140 char bytes - 6 header
bytes) x 8 bits)/ 7 bits = 154 characters
UCS-2 encoding when multiple segment = ((140 char bytes - 6 header
bytes) x 8 bits)/ 16 bits = 67 characters
Based on what encoding is used(check via Twilio Admin) for your message, you can calculate how many SMS can be in the queue at a time.
References:
https://support.twilio.com/hc/en-us/articles/115002943027-Understanding-Twilio-Rate-Limits-and-Message-Queues
https://www.twilio.com/blog/2017/03/what-the-heck-is-a-segment.html

SNMP out or in octets (Network Traffic)

I am writing software in C# measuring or utilizing out or in octets (bytes) via SNMP. I need to do how many bytes pass in 1000 secs?
According to my research, its value gets timed out or reset sometimes because some results give a negative value.
.1.3.6.1.2.1.2.2.1.10 for input stream in .139
In 1024 secs it gives result of -2,1 MBytes.
How can I get accurate measurement of traffic (in or out) ?
EDIT : This code I use for calculations. It takes value in everysec and gets result.
private void timer1_Tick(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
SnmpObject objSnmpObject, objSnmpIfSpeed;
objSnmpObject = (SnmpObject)objSnmpManager.Get(".1.3.6.1.2.1.2.2.1.16.139");
objSnmpIfSpeed = (SnmpObject)objSnmpManager.Get(".1.3.6.1.2.1.2.2.1.5.139");
if (GetResult() == 0)
{
float value = Int64.Parse(objSnmpObject.Value);
float ifSpeed = Int64.Parse(objSnmpIfSpeed.Value);
float Bytes = (value * 8 * 100 / ifSpeed);
// float megaBytes = Bytes / 1024;
sum += Bytes;
tb_calc.Text = (sum.ToString() + " Bytes");
}
_gv_timeSec++;
lb_timer.Text = _gv_timeSec.ToString();
Cursor.Current = Cursors.Default;
}
1.3.6.1.2.1.2.2.1.10 is the OID for IF-MIB::ifInOctets which is described by MIB as a Counter32 which has a upper limit of 2^32-1 (4294967295 decimal).
"The total number of octets received on the interface,
including framing characters.
Discontinuities in the value of this counter can occur at
re-initialization of the management system, and at other times as
indicated by the value of ifCounterDiscontinuityTime."
Quoting this SO answer :
a Counter32 has no defined initial value, so a single reading of
Counter32 has no information content. This is why you have to take two
(or more) readings to make sense of it. An example of this would be
the number of packets received on an ethernet interface. If you take a
reading and get back 4 million packets, you haven't learned anything:
the wire could have been pulled out of the interface for the past
year, or it could be passing millions of packets per second. You have
to take multiple readings to know anything.
I'd recommend ifHCInOctets .1.3.6.1.2.1.31.1.1.1.6 and ifHCOutOctets .1.3.6.1.2.1.31.1.1.1.10 which are 64 bit versions of OIDs mentioned by #k1eran
Those counters don't rotate so quickly when dealing with higher speeds.

iOS BLE no Glucose Measurement notifications

Context:
- iOS 7.1.2 on iPhone 5c
- A glucose measuring "peripheral" (BT LE)
- Device and peripheral have been paired (introducing a code)
- Peripheral has >= 1 records on/in it.
- The behavior described below was reproduced several times. With the peripheral only having one record, and as well having made two new ones. The results (characteristic.value's) read are always the same.
I'm trying to read (receive) records from the peripheral using an iOS device the following way:
Make the glucose peripheral try to send its records to my iOS device.
Discover and connect to relevant peripheral (Glucose Service 0x1808) on iOS device.
In peripheral:didDiscoverServices:error: discover characteristics, which are Glucose Measurement (0x2A18), Glucose Measurement Context (0x2A34), Glucose Feature (0x2A51) and Record Access Control Point (0x2A52)
In peripheral:didDiscoverCharacteristicsForService:error: iterate over all characteristics and read values for those characteristics (via [_peripheral readValueForCharacteristic:characteristic])
Call [_peripheral setNotifyValue:YES forCharacteristic:characteristic] (for all but the "Glucose Feature" characteristic). This is triggered via a button click, when "everything has been loaded".
Request number of records available (triggered by user) via "Record Access Control Point" like:
char buffer[3];
// Op Code: 0x04 report number of stored records
buffer[0] = 0x04;
// Operator: 0x01 All records
buffer[1] = 0x01;
// Operand: 0x00 n/a
buffer[2] = 0x00;
NSData *data = [NSData dataWithBytes:buffer length:3];
[_peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
Callback calls peripheral:didUpdateValueForCharacteristic:error: delegate method and the updated value is 06000405. I don't fully understand the related specification (see link below), so I'm not able to interpret the response my self but either 5 or 6 in the LSO (Least Significant Octet) would mean a "success" (or at least not an error).
Trigger the request of all stored records (triggered by user) like:
char buffer[3];
// Op Code: 0x01 report stored records
buffer[0] = 0x01;
// Operator: 0x01 All records, 0x06 last one
buffer[1] = 0x01; // Tried 0x06 as well with the same result
// Operand: 0x00 n/a
buffer[2] = 0x00;
NSData *data = [NSData dataWithBytes:buffer length:3];
[_peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
Call back calls peripheral:didUpdateValueForCharacteristic:error: with an updated value of 06000105. Changing the second octet from 0x01 to 0x06 gave the same response (value). Which as well I'm not able to understand/interpret.
Nothing more happens. Neither on Glucose Measurement nor on Glucose Measurement Context.
Note: On Android it seems that one must also set the Client Characteristic Configuration descriptor to notify and/or indicate, but trying either of that results in an exception and a message that notifications should be set on the characteristic itself using setNotifyValue:forCharacteristic: on the peripheral.
My main problem is, that there are no callbacks to peripheral:didUpdateValueForCharacteristic:error: on Glucose Measurement characteristic including the records. I event tried calling [_peripheral setNotifyValue:YES forCharacteristic:characteristic] again on that characteristic, after requesting the records.
Does anyone see where my error(s) lie? Does anyone achieved (on iOS) what I'm trying to achieve?
Another thing is the BT glucose service specification. I would greatly appreciate, if someone could enlighten me as on how to interpret the responses (means the updated values of the Record Access Control Point characteristic) I get. As I'm not even sure in which order the bytes in the characteristic.value come (e.g. when they are read via getBytes:length: method of NSData).
I think I'm following the process described in the Glucose Profile specification so I'm really at a loss here.
Thank you very much in advance!
Best regards,
Gabriel
Having encountered the same issue, I was unable to work out how to interpret the 06000105 value, however I think that is an error response code of sorts.
What fixed the issue for me was to exclude the Operand if it was null, and only use the Op Code and Operator:
char buffer[2];
// Op Code: 0x01 report stored records
buffer[0] = 0x01;
// Operator: 0x01 All records, 0x06 last one
buffer[1] = 0x01;
NSData *data = [NSData dataWithBytes:buffer length:2];
...
Then all the records came flowing in as expected in didUpdateValueForCharacteristic

websocket client packet unframe/unmask

I am trying to implement latest websocket spec. However, i am unable to get through the unmasking step post successful handshake.
I receive following web socket frame:
<<129,254,1,120,37,93,40,60,25,63,71,88,92,125,80,81,73,
51,91,1,2,53,92,72,85,103,7,19,79,60,74,94,64,47,6,83,
87,58,7,76,87,50,92,83,70,50,68,19,77,41,92,76,71,52,
70,88,2,125,90,85,65,96,15,14,20,107,31,14,28,100,27,9,
17,122,8,72,74,96,15,86,68,37,68,18,76,48,15,28,93,48,
68,6,73,60,70,91,24,122,77,82,2,125,80,81,85,45,18,74,
64,47,91,85,74,51,21,27,20,115,24,27,5,37,69,80,75,46,
18,68,72,45,88,1,2,40,90,82,31,37,69,76,85,103,80,94,
74,46,64,27,5,60,75,87,24,122,25,27,5,47,71,73,81,56,
21,27,93,48,88,76,31,57,77,74,11,55,73,68,73,115,65,81,
31,104,26,14,23,122,8,75,68,52,92,1,2,110,24,27,5,53,
71,80,65,96,15,13,2,125,75,83,75,41,77,82,81,96,15,72,
64,37,92,19,93,48,68,7,5,62,64,93,87,46,77,72,24,40,92,
90,8,101,15,28,83,56,90,1,2,108,6,13,21,122,8,82,64,42,
67,89,92,96,15,93,19,56,28,8,65,101,31,94,16,105,28,10,
20,56,30,14,65,56,27,93,71,106,16,11,17,63,25,4,17,57,
73,89,17,59,29,88,29,106,24,27,5,46,65,72,64,54,77,69,
24,122,66,93,93,49,5,12,8,109,15,28,76,59,90,93,72,56,
76,1,2,41,90,73,64,122,8,89,85,50,75,84,24,122,25,15,
23,105,25,5,19,106,26,14,20,111,25,27,5,53,77,85,66,53,
92,1,2,110,26,13,2,125,95,85,65,41,64,1,2,108,27,10,19,
122,7,2>>
As per base framing protocol defined here (https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2) i have:
fin:1, rsv:0, opcode:1, mask:1, length:126
Masked application+payload data comes out to be:
<<87,58,7,76,87,50,92,83,70,50,68,19,77,41,92,76,71,52,70,88,2,125,90,85,65,96,
15,14,20,107,31,14,28,100,27,9,17,122,8,72,74,96,15,86,68,37,68,18,76,48,15,
28,93,48,68,6,73,60,70,91,24,122,77,82,2,125,80,81,85,45,18,74,64,47,91,85,
74,51,21,27,20,115,24,27,5,37,69,80,75,46,18,68,72,45,88,1,2,40,90,82,31,37,
69,76,85,103,80,94,74,46,64,27,5,60,75,87,24,122,25,27,5,47,71,73,81,56,21,
27,93,48,88,76,31,57,77,74,11,55,73,68,73,115,65,81,31,104,26,14,23,122,8,75,
68,52,92,1,2,110,24,27,5,53,71,80,65,96,15,13,2,125,75,83,75,41,77,82,81,96,
15,72,64,37,92,19,93,48,68,7,5,62,64,93,87,46,77,72,24,40,92,90,8,101,15,28,
83,56,90,1,2,108,6,13,21,122,8,82,64,42,67,89,92,96,15,93,19,56,28,8,65,101,
31,94,16,105,28,10,20,56,30,14,65,56,27,93,71,106,16,11,17,63,25,4,17,57,73,
89,17,59,29,88,29,106,24,27,5,46,65,72,64,54,77,69,24,122,66,93,93,49,5,12,8,
109,15,28,76,59,90,93,72,56,76,1,2,41,90,73,64,122,8,89,85,50,75,84,24,122,
25,15,23,105,25,5,19,106,26,14,20,111,25,27,5,53,77,85,66,53,92,1,2,110,26,
13,2,125,95,85,65,41,64,1,2,108,27,10,19,122,7,2>>
While the 32-bit masking key is:
<<37,93,40,60,25,63,71,88,92,125,80,81,73,51,91,1,2,53,92,72,85,103,7,19,79,60,
74,94,64,47,6,83>>
As per https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 :
j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j
however, i doesn't seem to get my original octet sent from client side, which is basically a xml packet. Any direction, correction, suggestions are greatly appreciated.
I think you've mis-read the data framing section of the protocol spec.
Your interpretation of the first byte (129) is correct - fin + opcode 1 - final (and first) fragment of a text message.
The next byte (254) implies that the body of the message is masked and that the following 2 bytes provide its length (lengths of 126 or 127 imply longer messages whose length's can't be represented in 7 bits. 126 means that the following 2 bytes hold the length; 127 mean that its the following 4 bytes).
The following 2 bytes - 1, 120 - imply a message length of 376 bytes.
The following 4 bytes - 37,93,40,60 - are your mask.
The remaining data is your message which should be transformed as you write, giving the message
&ltbody xmlns='http://jabber.org/protocol/httpbind' rid='2167299354' to='jaxl.im' xml:lang='en' xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh' ack='1' route='xmpp:dev.jaxl.im:5222' wait='30' hold='1' content='text/xml; charset=utf-8' ver='1.1
0' newkey='a6e44d87b54461e62de3ab7874b184dae4f5d870' sitekey='jaxl-0-0' iframed='true' epoch='1324196722121' height='321' width='1366'/>

Resources