On an iPhone, with ios 6.1.3, I'm trying to write to an outputStream, without an NSRunLoop. My stream is simply initialized by:
session = [[EASession alloc] initWithAccessory:accessory
forProtocol:protocolString];
if (session)
{
NSLog(#"opening the streams for this accessory");
[[session inputStream] open];
[[session outputStream] open];
[session retain];
streamReady = true;
receivedAccPkt = true;
}
Then, elsewhere in the code, when I try to transmit data as follows:
uint8_t requestData[8] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
while (![[eas outputStream] hasSpaceAvailable]);
#synchronized(self) {
len += [[eas outputStream] write:requestData maxLength:8];
}
The 'hasSpaceAvailable' method never returns true, so the code is stuck.
Are there any other initialisation tasks to be done for an output stream, in order to send data?
The EASession documentation states that you need to configure the streams by assigning it a delegate which handles the stream events and schedule it in a run loop.
Using a run loop enables you to drive the streams without worrying about threads. For example, if you write into the output stream via the outputStream property, there's very likely an internal input stream (bound to the public visible output stream) which is used by the EASession object to obtain the bytes written. This internal input stream (which is not visible) may also use the same run loop as the publicly provided output stream outputStream.
Don't mismatch the internal input stream with the public input stream! So basically, there are two public streams and two internal streams. Each public stream has an associated pendant internally: a input stream has a bound output stream, and an output stream has a bound input stream.
You can however, avoid using a run loop for your (output) stream: you just need to guarantee that the write:maxLength: method is executed on a private thread, which is not used by the internal streams by EASession.
You can get your private thread just by utilizing dispatch. You may try the code below:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[outputStream open];
const char* data = ...;
int dataLength = ...;
while (dataLength && !isCancelled) {
int written = [outputStream write:(const uint8_t*)data maxLength:1];
if (written > 0) {
dataLength -= written;
data += written;
}
else if (written < 0 )
{
// error occurred
break;
}
}
[outputStream close];
});
Note: this will block the thread if a previous call to write:maxLength: returned zero and if there is still data to write. Thus, the internal streams must not use the same thread, otherwise you get a dead lock.
When the thread is blocked, it becomes difficult to cancel the block. Perhaps, you should try a run loop approach.
Related
In an iPhone app, I create a CFSocket object from an existing native UDP socket and set up a data callback whenever the socket receives some data. I then add that to my main program loop:
//Set socket descriptor field
cbData.s = udpSocket.getSocketDescriptor();
CFSocketContext udpSocketContext;
memset(&udpSocketContext, 0, sizeof(udpSocketContext));
udpSocketContext.info = &cbData;
cbData.socketRef = CFSocketCreateWithNative(NULL, cbData.s, kCFSocketDataCallBack, &getSocketDataCallBack, &udpSocketContext);
cbData.runLoopSourceRef = CFSocketCreateRunLoopSource( NULL, cbData.socketRef, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), cbData.runLoopSourceRef, kCFRunLoopCommonModes);
I send 1024-byte datagrams over WiFi from a separate Mac server app every 5 mS, and receive them on my iPhone in my getSocketDataCallBack routine.
I expect getSocketDataCallBack to be called every 5 mS (to match the period of the datagrams being sent from the Mac), which happens the majority of times. BUT, the calls often get delayed by 10s or 100s of mS. Thereafter, I get a rapid sequence of callbacks (fractions of a mS) to retrieve the multiple datagrams that have piled up over that delay.
As iOS obviously keeps the delayed datagrams around,
is there any way to grab all the delayed datagrams from the system at
once instead of getSocketDataCallBack being called over and over
in quick succession?
[I do query how many bytes are available in the callback ala:
CFDataRef dataRef = (CFDataRef)data;
numBytesReceived = CFDataGetLength(dataRef);
but 'numBytesReceived' is always reported as 1024.]
Alternatively, is there any way to improve/lessen the socket callback
timing variability through other means?
I'm using socket call back for Inter Process Communication (actually, inter thread communication) with UNIX socket. How we use socket is identical to the TCP/UDP.
The code below is written in c/obj-c and using posix thread. To translate it to Swift/NSThread should not be difficult.
Note the program below works as a server side, which means the program creates socket where the clients connect to. Once the client connected to the socket, the system automatically accepts the connection and allocates
another file descriptor to read/write. The socket call back reflects this two stage operation. Initially we create the socket, we then add as run-loop source so the system can call the call back when the client attempted to connect. The system accepts, then allocates and tells the call back
a file descriptor to read/write with the client. We then create another run-loop source from the read/write fd and add to run-loop. This second call back is called when rx/tx data is ready.
MAIN THREAD:
The main thread creates UNIX socket and worker thread. The socket fd is passed as argument of the worker thread.
#import <stdio.h>
#import <string.h>
#import <stdlib.h>
#import <unistd.h>
#import <pthread.h>
#import <sys/socket.h>
#import <sys/un.h>
#import <sys/stat.h>
#import <sys/types.h>
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
int setup(const char *ipcNode) {
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
return -1;
}
struct sockaddr_un sa = {0};
sa.sun_len = sizeof(sa);
sa.sun_family = AF_UNIX;
strcpy(sa.sun_path, ipcNode);
remove(sa.sun_path);
if (bind(sockfd, (struct sockaddr*)&sa, sizeof(struct sockaddr_un)) == -1) {
close(sockfd);
return -1;
}
// start up worker thread
pthread_attr_t at;
pthread_attr_init(&at);
pthread_attr_setdetachstate(&at, PTHREAD_CREATE_DETACHED);
pthread_t th;
pthread_create(&th, &at, workerThread, (void *)(long)(sockfd));
return 1;
}
WORKER THREAD:
The program works as a server. So, it waits to get connected by client (via connect()). Once it's connected, the system automatically calls accept() and allocates read/write fd to communicate with the client. This fd is passed to accept-call back routine socketDataCallback(). Then we create another call back clientDataCallback() with the read/write fd.
// worker thread
//
void *workerThread(void *tprm) {
int sockfd = (int)tprm;
int retval = listen(sockfd, 1); // mark as "server" side. here, accept only 1 connection request at a time
if (retval != 0) {
return NULL;
}
// create CFSocket and register it as data source.
CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, sockfd, kCFSocketAcceptCallBack, socketDataCallback, nil);
// don't close native fd on CFSocketInvalidate
CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
// create run loop source
CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
// add to run loop
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
CFRelease(socketRunLoop);
CFRelease(socket);
CFRunLoopRun();
// not return here untill run loop stops
close(sockfd);
return NULL;
}
// socket connection w/ client side. create another data source and add to run-loop
//
void socketDataCallback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
CFSocketContext socketContext;
memset(&socketContext, 0, sizeof(CFSocketContext));
int clientfd = *((int *)data); // get file descriptor (fd)
socketContext.info = (void *)((long)clientfd); // set fd at info of socketContext
// create CFSocket for tx/rx w/ connected client
CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, clientfd, kCFSocketReadCallBack | kCFSocketWriteCallBack, clientDataCallback, &socketContext);
CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
CFRelease(socket);
CFRelease(socketRunLoop);
}
// data to/from client
//
void clientDataCallback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
if (callbackType & kCFSocketWriteCallBack) {
// your own tx data prcess here
// txDataCallback(s, callbackType, address, data, info);
}
if (!(callbackType & kCFSocketReadCallBack)) return;
// extract fd
int fd = (int)((long)info);
// read data, and do some work
uint8_t rxdata[1024];
size_t nr = read(fd, rxdata, 1024);
if (!nr) {
// socket closed
handleSocketClosed(s);
return;
}
// your own rx process here
}
// socket closed
//
void handleSocketClosed(CFSocketRef s) {
// any clean up process here, then
CFSocketInvalidate(s);
// stop run loop if necessary
// CFRunLoopStop(CFRunLoopGetCurrent());
}
If you are working at client side, things get a bit easier. You get a read/write fd with connect() call. Then you create CFSockeRef and add to run-loop by using the fd.
Hope this helps.
EDIT: How to wait with POSIX select(). To wait with POSIX select() at worker thread is simpler than socket call back. If you are on client side, then:
int sockfd = socket(...);
bind(sockfd, ...)
connect(sockfd, ...);
while (1) {
int nfds = sockfd+1;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
int retval = select(nfds, &rfds, NULL, NULL, NULL);
if (retval == -1) break;
if (retval > 0) {
uint8_t rxdata[1024];
size_t nr = read(sockfd, rxdata, 1024);
if (!nr) {
// socket closed.
break;
}
// do your rx process here
}
}
Run the code above at your worker thread.
Right now I'm investigating possibility to implement video streaming through MultipeerConnectivity framework. For that purpose I'm using NSInputStream and NSOutputStream.
The problem is: I can't receive any picture so far. Right now I'm trying to pass simple picture and show it on the receiver. Here's a little snippet of my code:
Sending picture via NSOutputStream:
- (void)sendMessageToStream
{
NSData *imgData = UIImagePNGRepresentation(_testImage);
int img_length = (int)[imgData length];
NSMutableData *msgData = [[NSMutableData alloc] initWithBytes:&img_length length:sizeof(img_length)];
[msgData appendData:imgData];
int msg_length = (int)[msgData length];
uint8_t *readBytes = (uint8_t *)[msgData bytes];
uint8_t buf[msg_length];
(void)memcpy(buf, readBytes, msg_length);
int stream_len = [_stream writeData:(uint8_t*)buf maxLength:msg_length];
//int stream_len = [_stream writeData:(uint8_t *)buf maxLength:data_length];
//NSLog(#"stream_len = %d", stream_len);
_tmpCounter++;
dispatch_async(dispatch_get_main_queue(), ^{
_lblOperationsCounter.text = [NSString stringWithFormat:#"Sent: %ld", (long)_tmpCounter];
});
}
The code above works totally fine. stream_len parameter after writing equals to 29627 bytes which is expected value, because image's size is around 25-26 kb.
Receiving picture via NSinputStream:
- (void)readDataFromStream
{
UInt32 length;
if (_currentFrameSize == 0) {
uint8_t frameSize[4];
length = [_stream readData:frameSize maxLength:sizeof(int)];
unsigned int b = frameSize[3];
b <<= 8;
b |= frameSize[2];
b <<= 8;
b |= frameSize[1];
b <<= 8;
b |= frameSize[0];
_currentFrameSize = b;
}
uint8_t bytes[1024];
length = [_stream readData:bytes maxLength:1024];
[_frameData appendBytes:bytes length:length];
if ([_frameData length] >= _currentFrameSize) {
UIImage *img = [UIImage imageWithData:_frameData];
NSLog(#"SETUP IMAGE!");
_imgView.image = img;
_currentFrameSize = 0;
[_frameData setLength:0];
}
_tmpCounter++;
dispatch_async(dispatch_get_main_queue(), ^{
_lblOperationsCounter.text = [NSString stringWithFormat:#"Received: %ld", (long)_tmpCounter];
});
}
As you can see I'm trying to receive picture in several steps, and here's why. When I'm trying to read data from stream, it's always reading maximum 1095 bytes no matter what number I put in maxLength: parameter. But when I send the picture in the first snippet of code, it's sending absolutely ok (29627 bytes . Btw, image's size is around 29 kb.
That's the place where my question come up - why is that? Why is sending 29 kb via NSOutputStream works totally fine when receiving is causing problems? And is there a solid way to make video streaming work through NSInputStream and NSOutputStream? I just didn't find much information about this technology, all I found were some simple things which I knew already.
Here's an app I wrote that shows you how:
https://app.box.com/s/94dcm9qjk8giuar08305qspdbe0pc784
Build the project with Xcode 9 and run the app on two iOS 11 devices.
To stream live video, touch the Camera icon on one of two devices.
If you don't have two devices, you can run one app in the Simulator; however, you can only use the camera on the real device (the Simulator will display the video broadcasted).
Just so you know: this is not the ideal way to stream real-time video between devices (it should probably be your last choice). Data packets (versus streaming) are way more efficient and faster.
Regardless, I'm really confused by your NSInputStream-related code. Here's something that makes a little more sense, I think:
case NSStreamEventHasBytesAvailable: {
// len is a global variable set to a non-zero value;
// mdata is a NSMutableData object that is reset when a new input
// stream is created.
// displayImage is a block that accepts the image data and a reference
// to the layer on which the image will be rendered
uint8_t * buf[len];
len = [aStream read:(uint8_t *)buf maxLength:len];
if (len > 0) {
[mdata appendBytes:(const void *)buf length:len];
} else {
displayImage(mdata, wLayer);
}
break;
}
The output stream code should look something like this:
// data is an NSData object that contains the image data from the video
// camera;
// len is a global variable set to a non-zero value
// byteIndex is a global variable set to zero each time a new output
// stream is created
if (data.length > 0 && len >= 0 && (byteIndex <= data.length)) {
len = (data.length - byteIndex) < DATA_LENGTH ? (data.length - byteIndex) : DATA_LENGTH;
uint8_t * bytes[len];
[data getBytes:&bytes range:NSMakeRange(byteIndex, len)];
byteIndex += [oStream write:(const uint8_t *)bytes maxLength:len];
}
There's a lot more to streaming video than setting up the NSStream classes correctly—a lot more. You'll notice in my app, I created a cache for the input and output streams. This solved a myriad of issues that you would likely encounter if you don't do the same.
I have never seen anyone successfully use NSStreams for video streaming...ever. It's highly complex, for one reason.
There are many different (and better) ways to stream video; I wouldn't go this route. I just took it on because no one else has been able to do it successfully.
I think that the problem is in your assumption that all data will be available in NSInputStream all the time while you are reading it. NSInputStream made from NSURL object has an asynchronous nature and it should be accessed accordingly using NSStreamDelegate. You can look at example in the README of POSInputStreamLibrary.
I am trying to use CommonCrypto to encrypt an NSMutableData object in place (copying the resulting bytes to itself, without duplicating it). Previously, I was using CCCrypt() "one-shot" method, mainly because it seemed simple. I noticed that my data object got duplicated in memory.
To avoid this, I tried using an NSInputStream object with a buffer size of 2048 bytes. I am reading my NSMutableData object, and continuously call CCCryptorUpdate(), to handle the encryption. The problem is, that it still seems to be duplicated. Here's my current code (please note that it's a category on NSMutableData - mainly because of historical reasons - thus the "self" references):
- (BOOL)encryptWithKey:(NSString *)key
{
// Key creation - not relevant to the dercribed problem
char * keyPtr = calloc(1, kCCKeySizeAES256+1);
[key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding];
// Create cryptographic context for encryption
CCCryptorRef cryptor;
CCCryptorStatus status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode, keyPtr, kCCKeySizeAES256, NULL, &cryptor);
if (status != kCCSuccess)
{
MCLog(#"Failed to create a cryptographic context (%d CCCryptorStatus status).", status);
}
// Initialize the input stream
NSInputStream *inStream = [[NSInputStream alloc] initWithData:self];
[inStream open];
NSInteger result;
// BUFFER_LEN is a define 2048
uint8_t buffer[BUFFER_LEN];
size_t bytesWritten;
while ([inStream hasBytesAvailable])
{
result = [inStream read:buffer maxLength:BUFFER_LEN];
if (result > 0)
{
// Encryption goes here
status = CCCryptorUpdate(
cryptor, // Previously created cryptographic context
&result, // Input data
BUFFER_LEN, // Length of the input data
[self mutableBytes], // Result is written here
[self length], // Size of result
&bytesWritten // Number of bytes written
);
if (status != kCCSuccess)
{
MCLog(#"Error during data encryption (%d CCCryptorStatus status)", status);
}
}
else
{
// Error
}
}
// Cleanup
[inStream close];
CCCryptorRelease(cryptor);
free(keyPtr);
return ( status == kCCSuccess );
}
I am definitely missing something obvious here, encryption, and even using input streams is a bit new to me..
As long as you only call CCUpdate() one time, you can encrypt into the same buffer you read from without using a stream. See RNCryptManager.m for an example. Study applyOperation:fromStream:toStream:password:error:. I did use streams here, but there's no requirement that you do that if you already have an NSData.
You must ensure that CCUpdate() is only called one time, however. If you call it multiple times it will corrupt its own buffer. This is an open bug in CommonCryptor (radar://9930555).
As a side note: your key generation is extremely insecure, and use of ECB mode for this kind of data barely qualifies as encryption. It leaves patterns in the ciphertext which can be used to decrypt the data, in some cases just by looking at it. I do not recommend this approach if you actually intend to secure this data. If you want to study how to use these tools well, see Properly Encrypting With AES With CommonCrypto. If you want a prepackaged solution, see RNCryptor. (RNCryptor does not currently have a convenient method for encrypting in-place, however.)
In the line:
result = [inStream read:buffer maxLength:BUFFER_LEN];
the data is read into buffer and result is set to the outcome of the execution.
in lines:
status = CCCryptorUpdate(cryptor, &result, ...
You should be using buffer for the input data, not the status
status = CCCryptorUpdate(cryptor, buffer, ...
Using better names would help eliminate the simple error. If instead of result the variable had been named readStatus the error would most likely not occurred. Likewise instead of naming rthe data variable buffer it had been named streamData things would also have been more clear. Poor naming really can cause errors.
I'm almost done with this task but i'm stuck a point due to which i'm getting partial result.
I have server(linux or windows) and client(iOS) between which TCP IP socket connection exist. I have used form load in my iphone simulator where the connection between server and iphone happens automatically as the application opens. Server send the data back what ever I send on simulator and print it in log. But i'm not able to exactly receive the whole response. For "Innovations" I receive maybe just "in" or "Innova"etc.. Below are the code snippets.
void TCPClient()
{
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream);
[NSThread sleepForTimeInterval:2]; //Delay
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
if(!CFWriteStreamOpen(writeStream))
{
NSLog(#"Error Opening Socket");
}
else
{
UInt8 buf[] = "Innovations";
int bytesWritten = CFWriteStreamWrite(writeStream, buf, strlen((char*)buf));
NSLog(#"Written: %d", bytesWritten);
}
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
if(!CFReadStreamOpen(readStream))
{
NSLog(#"Error reading");
}
else
{
UInt8 bufr[15];
int bytesRead = CFReadStreamRead(readStream, bufr,strlen((char*)bufr));
NSLog(#"Read: %d", bytesRead);
NSLog(#"buffer: %s", bufr);
}
}
Notice in the read I did change the array size. But I still get the error. Same in the case of IBAction of a button. Even in that for every click i'm sending a data and i'm not getting the response of the same data.
Can valuable suggestion???
One error is that
int bytesRead = CFReadStreamRead(readStream, bufr,strlen((char*)bufr));
should be
int bytesRead = CFReadStreamRead(readStream, bufr, sizeof(bufr));
The last parameter of CFReadStreamRead is the capacity of the read buffer and determines the maximum number of bytes read. strlen((char*)bufr) is the length of the string currently in the buffer. You should also NULL-terminat the string in bufr before printing it.
With this modification, your program might work with short strings. But there will be problems as soon as you try to send/receive larger amounts of data.
A socket write can write less bytes than you asked it to, and a socket read can return less bytes than you requested.
Have a look at the Stream Programming Guide which describes how to register the socket streams with the runloop and handle stream events asynchronously.
My pthread_detach calls fail with a "Bad file descriptor" error. The calls are in the destructor for my class and look like this -
if(pthread_detach(get_sensors) != 0)
printf("\ndetach on get_sensors failed with error %m", errno);
if(pthread_detach(get_real_velocity) != 0)
printf("\ndetach on get_real_velocity failed with error %m", errno);
I have only ever dealt with this error when using sockets. What could be causing this to happen in a pthread_detach call that I should look for? Or is it likely something in the thread callback that could be causing it? Just in case, the callbacks look like this -
void* Robot::get_real_velocity_thread(void* threadid) {
Robot* r = (Robot*)threadid;
r->get_real_velocity_thread_i();
}
inline void Robot::get_real_velocity_thread_i() {
while(1) {
usleep(14500);
sensor_packet temp = get_sensor_value(REQUESTED_VELOCITY);
real_velocity = temp.values[0];
if(temp.values[1] != -1)
real_velocity += temp.values[1];
} //end while
}
/*Callback for get sensors thread*/
void* Robot::get_sensors_thread(void* threadid) {
Robot* r = (Robot*)threadid;
r->get_sensors_thread_i();
} //END GETSENSORS_THREAD
inline void Robot::get_sensors_thread_i() {
while(1) {
usleep(14500);
if(sensorsstreaming) {
unsigned char receive;
int read = 0;
read = connection.PollComport(port, &receive, sizeof(unsigned char));
if((int)receive == 19) {
read = connection.PollComport(port, &receive, sizeof(unsigned char));
unsigned char rest[54];
read = connection.PollComport(port, rest, 54);
/* ***SET SENSOR VALUES*** */
//bump + wheel drop
sensor_values[0] = (int)rest[1];
sensor_values[1] = -1;
//wall
sensor_values[2] = (int)rest[2];
sensor_values[3] = -1;
...
...
lots more setting just like the two above
} //end if header == 19
} //end if sensors streaming
} //end while
} //END GET_SENSORS_THREAD_I
Thank you for any help.
The pthread_* functions return an error code; they do not set errno. (Well, they may of course, but not in any way that is documented.)
Your code should print the value returned by pthread_detach and print that.
Single Unix Spec documents two return values for this function: ESRCH (no thread by that ID was found) and EINVAL (the thread is not joinable).
Detaching threads in the destructor of an object seems silly. Firstly, if they are going to be detached eventually, why not just create them that way?
If there is any risk that the threads can use the object that is being destroyed, they need to be stopped, not detached. I.e. you somehow indicate to the threads that they should shut down, and then wait for them to reach some safe place after which they will not touch the object any more. pthread_join is useful for this.
Also, it is a little late to be doing that from the destructor. A destructor should only be run when the thread executing it is the only thread with a reference to that object. If threads are still using the object, then you're destroying it from under them.