I'm working with NSStream to send and receive single characters over a network connection.
I instantiate the streams both for reading and for writing using CFStreamCreatePairWithSocketToCFHost(...).
The basic working mechanism to send and receive is working fine.
Now I want to create the NSStream only when I need to send or receive something (usually one time every 2 minutes). The first time I create the streams, send the packet, close it and everything work well. Next I try to recreate the NSStream (create a new socket and open the stream on it), the opening function don't work. Since I'm using netcat to test it, I see that when I close the stream using [NSStream close] method, the connection on the server is not closing. How can I force to close the socket? Or there's a better way to do what I'm trying to do?
Use CFWriteStreamSetProperty and CFReadStreamSetProperty to set kCFStreamPropertyShouldCloseNativeSocket to kCFBooleanTrue in order that the underlying socket is closed when the stream is closed.
Related
I am developing an app that needs to send large amounts of data between an iPhone and a device (it takes approximately 10 seconds to send the data). But I want to be able to cancel the data communication anytime. I am aware I can simply drop the connection to the device at anytime with
centralManager.cancelPeripheral(peripheral)
but that is not what I am actually looking for, as I want to stop sending data but without terminating the bluetooth connection.
Is there a way to terminate the data transmission without dropping the connection to the device?
the codes of sending data is as follow:
for (var Hex: UInt8 = 0x01; Hex <= 0x14; Hex+=1){
var outbuffer = [UInt8](count: 16, repeatedValue: 0x00)
outbuffer[0] = (0x68)
outbuffer[1] = (Hex)
let data = NSData(bytes: outbuffer, length: 7)
print("data\(data)")
connectingPeripheral.writeValue(data, forCharacteristic: connectingCharacteristicPassword , type: CBCharacteristicWriteType.WithResponse)
}
I figured that I would go ahead and give my input on this. There is no way in CoreBluetooth to stop the transmission of a data packet that has already been written to the output buffer. The reason for why this is the case is simply because it is not needed and it would be a useless functionality. The only reason for why you are having this issue is because your methodology is wrong in my opinion. Do not put everything in a for-loop and push the data all at once. Instead you should implement some sort of flow control mechanism.
In Bluetooth LE there are two main ways of writing data to a peripheral: “Write Commands” and “Write Requests”. You can look at it a bit like the TCP vs UDP protocols. With write commands you are just sending data without knowing whether or not the data was received by the application on the other side of the bluetooth link. With write requests you are sending data and letting the peripheral know that you want to be notified (ack’ed) that the data was in fact received. These two types are in CoreBluetooth called CBCharacteristicWriteWithResponse and CBCharacteristicWriteWithoutResponse. When writing data using the CBCharacteristicWriteWithResponse (like you are doing in your code) you will get a peripheral:didWriteValueForCharacteristic:error: callback which verifies that the data has arrived at the other side. At this point you now have the option to go ahead and send the next packet if you want to, but if you for some reason want to stop sending data, then you can do that as well. Doing it this way you are in control of the whole flow and not just simply pushing everything though a for-loop.
But wait, why would you ever want to use write commands then? Well, since write requests requires the receiver to respond back to the sender it means that data must be sent in both directions. In this case, since the ack is sent by the application layer, you have to wait for the next connection interval before the ack can be sent. This means that when sending large amounts of data you can only send one packet per every two connection intervals which will give you a very poor overall bit rate.
With write commands, since they are not ack’ed, you can send as manny packets as possible within one connection event window. In most cases you should be able to send about 10-20 packets per connection window. But be aware that if you send too many packets then you will fill the outgoing buffer and packets will be lost. So, something that you can try is to directly send 9 packets with the WriteWithoutResponse type, followed by 1 packet of the WriteWithResponse type. After doing this you can wait for the peripheral:didWriteValueForCharacteristic:error: callback in which you can then send 10 more packets the same way. This way you will manage to send 10 packets per every 2 connection intervals while still being able to control the flow better.
You can of course experiment with the ratio a bit, but remember that the buffer is shared between multiple applications on the iOS device so you don’t want to be too close to the limit.
I have an iOS app that establish a peer-to-peer connection through Bonjour/NSNetService. (based on the WiTap sample code)
In some cases, both devices won’t receive data anymore after a few seconds while they are still able to write data out (i.e. without an error being reported).
The data written to the NSOutputStream never makes it to the NSInputStream of the other side.
The strange part is that sending and receiving works right after the connection is established. It seems to go bad after a few seconds.
If I set includesPeerToPeer = false it is not happening anymore. Does anyone having the same issue? I need to use peer to peer because is required to use bluetooth.
Thanks
My first guess would be that you're writing data to the stream before you get a stream event telling you that it is ready to accept more data.
If that isn't the problem, then either it's a bug in your code (which you haven't posted any of) or... well, it is possible that the class in question still doesn't work correctly. See:
https://developer.apple.com/library/ios/qa/qa1546/_index.html
If that's the case, you might have to drop down to Core Foundation briefly.
I'm trying to interrupt
[NSOutputstream write]
when the connection is lost.
However, it seems that it continues to block even though I close the stream using
[NSOutputstream close].
All I'm trying to achieve is reconnection when the server connection is lost.
I tried to schedule a NSTimer before the send call - What it does is close the stream if the send call did not manage to finish within the interval (Something like 5 seconds) but the close operation does not affect the stream at all. It continues to block. Sigh.
How can I implement a auto-reconnection to the server in case of disconnection?
The best thing to do with streams is to register them on the threads run loop using scheduleInRunLoop:forMode and deal with the delegate events which are raised telling you when there is capacity to write, when there is data to read and when there are errors.
This way you are efficiently interacting with the network stack and when you detect an error through the delegate events, you can reconnect and add the new streams to the run loop.
NB: Calling close will remove the stream from the run loop.
I am writing a bulletin board system (BBS) reader on ios. I use GCDAsyncSocket library to handle packets sending and receiving. The issue that I have is the server always splits the data to send into multiple packets. I can see that happens by printing out the receiving string in didReceiveData() function.
From the GCDAsyncSocket readme, I understand TCP is a stream. I also know there are some end of stream mechanisms, such as double CR LFs at the end. I have used WireShark to parse the packets, but there is no sign of some sort of pattern in the last data packet. The site is not owned by me, so I couldn't make it to send certain bytes. There must be some way to detect the last packet, otherwise how BBS clients handle displaying data?
Double CR LFs are not end of stream. That is just part of the details of HTTP protocol, for example, but has nothing to do with closing the stream. HTTP 1.1 allows me to send multiple responses on a single stream, with double CR LF after HTTP header, without end of stream.
The TCP socket stream will return 0 on a read when it is closed from the other end, blocking or non-blocking.
So assuming the server will close the socket when it is done sending to you, you can loop and perform a blocking read and if returns > 0, process the data, then read again. if < 0, process the error code (could be fatal or not), and if == 0, socket is closed from the other side, don't read again.
For non-blocking sockets, you can use select() or some other API to detect when the stream becomes ready to read. I'm not familiar with the specific lib you are using but if it is a POSIX / Berkeley sockets API, it will work that way.
In either case, you should build a buffer of input, concatenating the results of each read until you are ready to process. As you've found, you can't assume that a single read will return a complete application level packet. But as to your question, unless the server wants you to close the socket, you should wait for read to return 0.
I cant really understand this event.
I'm hoping that it is called when the sending queue (or something similar internal structure) is done sending previously written packets.
Is it a correct assumption?
I'm working on a video streamer over Multipeer connectivity, and I want to use this property to decide if I should drop a camera frame (if there is no NSStreamEventHasSpaceAvailable), or I can submit it for NSOutputStream.
Imagine a BlueTooth connection, where I really need to drop a lot of camera frame, instead of submit every frame to NSStream.
The NSStreamEventHasSpaceAvailable event indicates that you can write (at least one byte!) to the stream without blocking. That does not mean that previously written data is completely
delivered to the other endpoint of the connection.