iOS Threads and making UI changes on the main thread - ios

I have the following AcceptCallBack method and was hoping to add an UIActivityIndicator while the method is running, hence the [mvc performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:YES];. invoke is the method which makes the UI changes. And then I have this line [mvc performSelectorOnMainThread:#selector(hide) withObject:nil waitUntilDone:YES]; to remove the UIActivityIndicator. However what seem to happen is that invoke only gets called when AcceptCallBack has finished executing. Are AcceptCallBack and invoke not running on two different threads, therefore allowing them to run simultaneously??
void AcceptCallBack(
CFSocketRef socket,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
NSLog(#"Start Receiving...");
MasterViewController* mvc = (__bridge MasterViewController*)info;
[mvc performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:YES];
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFIndex bytes;
UInt8 buffer[8192];
UInt8 * fileData;
UInt8 recv_len = 0;
/* The native socket, used for various operations */
CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;
/* Create the read and write streams for the socket */
CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock,
&readStream, &writeStream);
if (!readStream || !writeStream) {
close(sock);
fprintf(stderr, "CFStreamCreatePairWithSocket() failed\n");
return;
}
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
bool headerRead = false;
int dataWritten = 0;
NSMutableString* filename = NULL;
NSMutableString * header = [[NSMutableString alloc] init];
while (true) {
memset(buffer, 0, sizeof(buffer));
bytes = CFReadStreamRead(readStream, buffer, sizeof(buffer));
recv_len += bytes;
if (bytes < 0) {
fprintf(stderr, "CFReadStreamRead() failed: %d\n", (int)bytes);
close(sock);
return;
}
if (bytes == 0) {
break;
}
if (!headerRead) {
for (int b=0; b<bytes; b++) {
if (buffer[b] == '\n') {
headerRead = true;
NSLog(#"Header is: %#", header);
NSArray *listItems = [header componentsSeparatedByString:#":"];
filename = [[NSMutableString alloc] init];
[filename appendString:[listItems objectAtIndex:2]];
[filename replaceOccurrencesOfString:#"/" withString:#"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [filename length])];
fileData = (UInt8*)malloc(sizeof(UInt8) * [[listItems objectAtIndex:3] intValue]);
b++;
memcpy(fileData, buffer + b, bytes - b);
dataWritten = bytes - b;
break;
} else {
[header appendFormat:#"%c", buffer[b]];
}
}
} else {
memcpy(fileData + dataWritten, buffer, bytes);
dataWritten += bytes;
}
}
NSString* docFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
NSData * outputData = [[NSData alloc] initWithBytes:fileData length:dataWritten];
if ([outputData writeToFile:docFile atomically:false] == YES) {
NSLog(#"File received and successfully written out to file------------------------------");
MasterViewController * thing = (__bridge MasterViewController*)info;
[thing restClient:NULL loadedFile:docFile];
NSString *destDir = #"/Slide2Me/";
[[thing restClient] uploadFile:filename toPath:destDir
withParentRev:nil fromPath:docFile];
[mvc performSelectorOnMainThread:#selector(hide) withObject:nil waitUntilDone:YES];
} else {
NSLog(#"Failed to write received data to file");
}
}
EDIT
So what I ended up doing to get my desired result is put all the above code in dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0) and sandwich it with the changes I want to make on the main thread like so....
void AcceptCallBack(
CFSocketRef socket,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
NSLog(#"Start Receiving...");
MasterViewController* mvc = (__bridge MasterViewController*)info;
[mvc performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
[the code above];
});
[mvc performSelectorOnMainThread:#selector(hide) withObject:nil waitUntilDone:YES];
}

You can use GCD to make it multithreaded, and give signal when indicator start and stop.
performSelector... will be executed when function is finished..
and It doesn't say how you call the AcceptCallBack method, I guess it's already on main thread. If it is then you need to call the AcceptCallback in another thread and use below code to perform "invoke" method on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
<do work here>
});
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1
EDIT:
I would do it like this
static dispatch_queue_t processing_queue;
static dispatch_queue_t request_processing_queue() {
if (processing_queue == NULL) {
processing_queue = dispatch_queue_create("com.xxx.processing", 0);
}
return processing_queue;
}
void AcceptCallBack(
CFSocketRef socket,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
__block MasterViewController* mvc = (__bridge MasterViewController*)info;
dispatch_async(processing_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[mvc invoke];
});
..... < Do AcceptCallback Code Here >
dispatch_async(dispatch_get_main_queue(), ^{
[mvc hide];
});
});
}
WARNING: This is just pseudo code..

Related

XMPP connect issue in ipv4 network

i tried all the code.i used this code to allow connect ipv6 network for connect XMPP.
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:xmppQueue];
[asyncSocket setPreferIPv4OverIPv6:NO];
- (void)setPreferIPv4OverIPv6:(BOOL)flag
{
// Note: YES means kPreferIPv6 is OFF
dispatch_block_t block = ^{
if (flag)
config &= ~kPreferIPv6;
else
config |= kPreferIPv6;
};
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_async(socketQueue, block);
}
- (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), #"Must be dispatched on socketQueue");
LogVerbose(#"IPv4: %#:%hu", [[self class] hostFromAddress:address4], [[self class] portFromAddress:address4]);
LogVerbose(#"IPv6: %#:%hu", [[self class] hostFromAddress:address6], [[self class] portFromAddress:address6]);
// Determine socket type
BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO;
BOOL useIPv6 = ((preferIPv6 && address6) || (address4 == nil));
// Create the socket
__block int socketFD;
__block NSData *address;
__block NSData *connectInterface;
if (useIPv6)
{
LogVerbose(#"Creating IPv6 socket");
socket6FD = socket(AF_INET6, SOCK_STREAM, 0);
socketFD = socket6FD;
address = address6;
connectInterface = connectInterface6;
}
else
{
LogVerbose(#"Creating IPv4 socket");
socket4FD = socket(AF_INET, SOCK_STREAM, 0);
socketFD = socket4FD;
address = address4;
connectInterface = connectInterface4;
}
if (socketFD == SOCKET_NULL)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:#"Error in socket() function"];
return NO;
}
// Bind the socket to the desired interface (if needed)
if (connectInterface)
{
LogVerbose(#"Binding socket...");
if ([[self class] portFromAddress:connectInterface] > 0)
{
// Since we're going to be binding to a specific port,
// we should turn on reuseaddr to allow us to override sockets in time_wait.
int reuseOn = 1;
setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
}
const struct sockaddr *interfaceAddr = (const struct sockaddr *)[connectInterface bytes];
int result = bind(socketFD, interfaceAddr, (socklen_t)[connectInterface length]);
if (result != 0)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:#"Error in bind() function"];
return NO;
}
}
// Prevent SIGPIPE signals
int nosigpipe = 1;
setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
// Start the connection process in a background queue
int aConnectIndex = connectIndex;
// dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// dispatch_async(globalConcurrentQueue, ^{
int result = connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]);
if (result != 0) {
socket6FD = SOCKET_NULL;
socket4FD = socket(AF_INET, SOCK_STREAM, 0);
socketFD = socket4FD;
address = address4;
connectInterface = connectInterface4;
result = connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]);
}
//});
LogVerbose(#"Connecting...");
return YES;
}
above this method always connect ipv6 in both network.
if i will commet [asyncSocket setPreferIPv4OverIPv6:NO]; this code than proper work in ipv4 network but does not connect ipv6.if i used this code than does not connect ipv4.please help me.i want to connect both network..
try this method of connectWithAddress4...
- (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), #"Must be dispatched on socketQueue");
LogVerbose(#"IPv4: %#:%hu", [[self class] hostFromAddress:address4], [[self class] portFromAddress:address4]);
LogVerbose(#"IPv6: %#:%hu", [[self class] hostFromAddress:address6], [[self class] portFromAddress:address6]);
// Determine socket type
BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO;
BOOL useIPv6 = ((preferIPv6 && address6) || (address4 == nil));
// Create the socket
int socketFD;
NSData *address;
NSData *connectInterface;
if (useIPv6)
{
LogVerbose(#"Creating IPv6 socket");
socket6FD = socket(AF_INET6, SOCK_STREAM, 0);
socketFD = socket6FD;
address = address6;
connectInterface = connectInterface6;
}
else
{
LogVerbose(#"Creating IPv4 socket");
socket4FD = socket(AF_INET, SOCK_STREAM, 0);
socketFD = socket4FD;
address = address4;
connectInterface = connectInterface4;
}
if (socketFD == SOCKET_NULL)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:#"Error in socket() function"];
return NO;
}
// Bind the socket to the desired interface (if needed)
if (connectInterface)
{
LogVerbose(#"Binding socket...");
if ([[self class] portFromAddress:connectInterface] > 0)
{
// Since we're going to be binding to a specific port,
// we should turn on reuseaddr to allow us to override sockets in time_wait.
int reuseOn = 1;
setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
}
const struct sockaddr *interfaceAddr = (const struct sockaddr *)[connectInterface bytes];
int result = bind(socketFD, interfaceAddr, (socklen_t)[connectInterface length]);
if (result != 0)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:#"Error in bind() function"];
return NO;
}
}
// Prevent SIGPIPE signals
int nosigpipe = 1;
setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
// Start the connection process in a background queue
int aStateIndex = stateIndex;
__weak GCDAsyncSocket *weakSelf = self;
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalConcurrentQueue, ^{
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wimplicit-retain-self"
int result = connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]);
__strong GCDAsyncSocket *strongSelf = weakSelf;
if (strongSelf == nil) return_from_block;
if (result == 0)
{
dispatch_async(strongSelf->socketQueue, ^{ #autoreleasepool {
[strongSelf didConnect:aStateIndex];
}});
}
else
{
NSError *error = [strongSelf errnoErrorWithReason:#"Error in connect() function"];
dispatch_async(strongSelf->socketQueue, ^{ #autoreleasepool {
[strongSelf didNotConnect:aStateIndex error:error];
}});
}
#pragma clang diagnostic pop
});
LogVerbose(#"Connecting...");
return YES;
}

CFSocket crashes on successful connection

This is my Client and Server code for CFSocket communication but on successful connection this code crashes.
Client.m
-(void)createConnection
{
CFSocketContext socketContext = {0,(__bridge void *)(self),NULL,NULL,NULL};
_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketConnectCallBack,(CFSocketCallBack)kCFSocketConnectCallBack, &socketContext);
if (_socket != nil) {
struct sockaddr_in addr4;
memset (& addr4, 0, sizeof (addr4));
addr4.sin_len = sizeof (addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons (8888);
NSString *strAddress = #"xxx.xxx.x.xxx";
addr4.sin_addr.s_addr = inet_addr ([strAddress UTF8String]);
CFDataRef address = CFDataCreate (kCFAllocatorDefault, (UInt8 *) & addr4, sizeof (addr4));
CFSocketConnectToAddress (_socket,address,-1);
CFSocketEnableCallBacks(_socket, kCFSocketConnectCallBack);
CFRunLoopRef cRunRef = CFRunLoopGetCurrent ();
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource (kCFAllocatorDefault, _socket, 0);
CFRunLoopAddSource (cRunRef,sourceRef,kCFRunLoopCommonModes);
char ethadd []= "helloworld";
CFDataRef Data = CFDataCreate(NULL, (const UInt8*)ethadd, sizeof(ethadd));
int socket_error = CFSocketSendData (_socket, (CFDataRef) address, (CFDataRef) Data, 10);
if (socket_error < 0){
NSLog(#"Data could not be sent!");
// return EXIT_FAILURE;
}
else NSLog(#"Data Sent");
CFRelease (sourceRef);
}
}
void TCPServerConnectCallBack (CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void * data, void * info) {
if (data != NULL) {
// When the socket is kCFSocketConnectCallBack failure callback failed to return an error code pointer, other returns NULL
NSLog (#"connection failed");
return;
}
Client * client = (__bridge Client *) info;
[client readStream];
}
- (void)readStream {
char buffer [1024];
while (recv (CFSocketGetNative (_socket), // ??Socket associated with the authority has failed to return -1: INVALID_SOCKET is
buffer, sizeof (buffer), 0)) {
NSLog (#"%#", [NSString stringWithUTF8String: buffer]);
}
}
- (void) SendMessage :(NSString *)text {
// [self createConnection];
NSString * stringTosend = text;
char * data = (char *)[stringTosend UTF8String];
send (CFSocketGetNative (_socket), data, strlen (data) + 1, 0);
}
Server.m
-(void)setupSocket
{
setupSocket();
}
int setupSocket () {
_socket = CFSocketCreate (kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)kCFSocketAcceptCallBack, NULL);
if (NULL == _socket) {
NSLog (# "Cannot create socket!");
return 0;
}
int optval = 1;
setsockopt (CFSocketGetNative (_socket), SOL_SOCKET, SO_REUSEADDR, // ??allow reuse of local address and port
(void *) & optval, sizeof (optval));
struct sockaddr_in addr4;
memset (& addr4, 0, sizeof (addr4));
addr4.sin_len = sizeof (addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons (8888);
addr4.sin_addr.s_addr = inet_addr ([#"xxx.xxx.x.xxx" UTF8String]);//htlon(INADDR_ANY)
CFDataRef address = CFDataCreate (kCFAllocatorDefault, (UInt8 *) & addr4, sizeof (addr4));
if (kCFSocketSuccess != CFSocketSetAddress (_socket, address)) {
NSLog (# "Bind to address failed!");
if (_socket)
CFRelease (_socket);
_socket = NULL;
return 0;
}
CFSocketEnableCallBacks(_socket, kCFSocketAcceptCallBack);
CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent ();
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource (kCFAllocatorDefault, _socket, 0);
CFRunLoopAddSource (cfRunLoop, source, kCFRunLoopCommonModes);
return 1;
}
void TCPServerAcceptCallBack (CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void * data, void * info) {
if (kCFSocketAcceptCallBack == type) {
// Local socket handle
CFSocketNativeHandle nativeSocketHandle = * (CFSocketNativeHandle *) data;
uint8_t name [SOCK_MAXADDRLEN];
socklen_t nameLen = sizeof (name);
if (0 != getpeername (nativeSocketHandle, (struct sockaddr *) name, & nameLen)) {
NSLog (#"error");
// Exit (1);
}
NSLog (# "%s connected.", inet_ntoa (((struct sockaddr_in *) name) -> sin_addr));
CFReadStreamRef iStream;
CFWriteStreamRef oStream;
CFReadStreamClientCallBack readStream;
CFReadStreamClientCallBack writeStream;
CFWriteStreamRef wStream;
// Create a socket connection can read and write
CFStreamCreatePairWithSocket (kCFAllocatorDefault, nativeSocketHandle, &iStream, & oStream);
if (iStream && oStream) {
CFStreamClientContext streamContext = {0, NULL, NULL, NULL};
if (! CFReadStreamSetClient (iStream, kCFStreamEventHasBytesAvailable,
readStream, // callback function is called when the data readable
&streamContext)) {
// Exit (1);
}
if (! CFReadStreamSetClient (iStream, kCFStreamEventCanAcceptBytes, writeStream, &streamContext)) {
// Exit (1);
}
CFReadStreamScheduleWithRunLoop (iStream, CFRunLoopGetCurrent (), kCFRunLoopCommonModes);
CFWriteStreamScheduleWithRunLoop (wStream, CFRunLoopGetCurrent (), kCFRunLoopCommonModes);
CFReadStreamOpen (iStream);
CFWriteStreamOpen (wStream);
}
else
{
close (nativeSocketHandle);
}
}
}
// Read data
void readStream (CFReadStreamRef stream, CFStreamEventType eventType, void * clientCallBackInfo) {
UInt8 buff [255];
CFReadStreamRead (stream, buff, 255);
printf ("received:%s", buff);
}
void writeStream (CFWriteStreamRef stream, CFStreamEventType eventType, void * clientCallBackInfo) {
outputStream = stream;
}
//int main()
//{
// char * str = "nihao";
// UInt8 buff [255];
// if (outputStream != NULL) {
// CFWriteStreamWrite (outputStream, buff, strlen (str) + 1);
// }
// else
// {
// NSLog (# "Cannot send data!");
// }
//}
// Open up a thread in the thread function
void runLoopInThread () {
int res = setupSocket ();
if (res) {
EXIT_FAILURE;
}
CFRunLoopRun (); // run the current thread CFRunLoop of objects
}
In the above code crashes for Server when CFSocketConnectToAddress executes in Client and after that client app also crashes.
Dealing with a CFSockets will get you in the world of dealing with POSIX foundations and stuff. If your looking for a simple echo/chat server, you should firstly go to NSStream. It's way easier. Here's an example:
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)HOST, PORT, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
And handling the input be like:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[100240];
NSInteger len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"Server Response: %#", output);
[self stream:theStream didRecieveResponse:output];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
[self requestDidFailWithResults:#"Can not connect to the host!" andError:theStream.streamError];
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
theStream = nil;
break;
case NSStreamEventEndEncountered:
[self requestDidFailWithResults:#"Request ended" andError:theStream.streamError];
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
theStream = nil;
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"OutStream is ready");
break;
default:
[self requestDidFailWithResults:#"Unknown event" andError:theStream.streamError];
}
}
_socket = CFSocketCreate (kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)kCFSocketAcceptCallBack, NULL);
In above method i had to pass my callback method instead of "(CFSocketCallBack)kCFSocketAcceptCallBack".Which fixed my issue.

Storing and Retrieving compressed texts as BLOB from Sqlite via FMDB

I am trying to store text data as compressed blobs.
[db executeUpdate:#"create table blobTable (a text, b blob)"];
[db executeUpdate:#"insert into blobTable (a, b) values (?, compressMe('random text string'))", #"lord of the rings"];
And then I try to query them using:
FMResultSet *rs = [db executeQuery:#"select uncompressMe(b) as k from blobTable where a = ?", #"lord of the rings"];
Where compressMe and uncompressMe are defined as:
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
[queue inDatabase:^(FMDatabase *adb) {
[adb makeFunctionNamed:#"compressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
#autoreleasepool {
const char *c = (const char *)sqlite3_value_text(aargv[0]);
NSString *s = [NSString stringWithUTF8String:c];
NSLog(#"string to compress: %#", s);
NSData *compressedBody = [self gzipDeflate:[s dataUsingEncoding:NSUTF8StringEncoding]];
sqlite3_result_blob(context, (__bridge const void *)(compressedBody), [compressedBody length], nil);
}
}
else {
NSLog(#"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
sqlite3_result_null(context);
}
}];
}];
[queue inDatabase:^(FMDatabase *adb) {
[adb makeFunctionNamed:#"uncompressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_BLOB) {
#autoreleasepool {
NSLog(#"inside uncompressMe");
NSUInteger len = sqlite3_value_bytes(aargv[0]);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, sqlite3_value_blob(aargv[0]), len);
NSData *data = [[NSData alloc] initWithBytes:byteData length:len];
NSData *deflatedBody = [self gzipInflate:data];
NSString *deflatedString = [[NSString alloc] initWithData:deflatedBody encoding:NSUTF8StringEncoding];
sqlite3_result_text(context, (__bridge const void *)(deflatedString), -1, SQLITE_UTF8);
}
}
else {
NSLog(#"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
sqlite3_result_null(context);
}
}];
}];
For the sake of completeness, the zip functions are defined as such:
- (NSData *)gzipInflate:(NSData*)data
{
if ([data length] == 0) return data;
unsigned full_length = [data length];
unsigned half_length = [data length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[data bytes];
strm.avail_in = [data length];
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
while (!done)
{
// Make sure we have enough room and reset the lengths.
if (strm.total_out >= [decompressed length])
[decompressed increaseLengthBy: half_length];
strm.next_out = [decompressed mutableBytes] + strm.total_out;
strm.avail_out = [decompressed length] - strm.total_out;
// Inflate another chunk.
status = inflate (&strm, Z_SYNC_FLUSH);
if (status == Z_STREAM_END) done = YES;
else if (status != Z_OK) break;
}
if (inflateEnd (&strm) != Z_OK) return nil;
// Set real length.
if (done)
{
[decompressed setLength: strm.total_out];
return [NSData dataWithData: decompressed];
}
else return nil;
}
- (NSData *)gzipDeflate:(NSData*)data
{
if ([data length] == 0) return data;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.total_out = 0;
strm.next_in=(Bytef *)[data bytes];
strm.avail_in = [data length];
// Compresssion Levels:
// Z_NO_COMPRESSION
// Z_BEST_SPEED
// Z_BEST_COMPRESSION
// Z_DEFAULT_COMPRESSION
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
do {
if (strm.total_out >= [compressed length])
[compressed increaseLengthBy: 16384];
strm.next_out = [compressed mutableBytes] + strm.total_out;
strm.avail_out = [compressed length] - strm.total_out;
deflate(&strm, Z_FINISH);
} while (strm.avail_out == 0);
deflateEnd(&strm);
[compressed setLength: strm.total_out];
return [NSData dataWithData:compressed];
}
However, the uncompressMe function doesnt seem to succeed. It fails in the Inflate method, which always returns a nil. Any thoughts what I might be doing wrong? I have checked to make sure that the blob column does get populated.
I feel like issue is from below code:
NSUInteger len = sqlite3_value_bytes(aargv[0]);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, sqlite3_value_blob(aargv[0]), len);
NSData *data = [[NSData alloc] initWithBytes:byteData length:len];
Try this to load NSData from database instead of creating byte variable and copying it.
const void *bytes = sqlite3_column_blob(statement, 3);
NSData *data = [[NSData alloc] initWithBytes:bytes length:size];

NSOutputStream not calling delegate's NSStreamEventHasSpaceAvailable

I have implemented socket by using input and output streams. The external architecture takes care of sending one request at a time to write.
However if any request does not return no HasBytesAvailable I need to remove that request from queue and inform about request timeout.
For all other requests, I am able to send/receive data correctly, but if any one of the request time outs then after that HasSpaceAvailable never gets called.
My code is as follows :
#implementation CCCommandSocket
#synthesize connectionTimeoutTimer;
#synthesize requestTimeoutTimer;
/*
* init
*
* #params
* ipAddress :ip address of camera socket
* portNumber :port address of camera socket
*
* #return
* Object of type Socket, which will send connection request to ipAddress,portNumber
*
*/
- (id)init
{
self = [super init];
if (self)
{
ip = #"192.168.42.1";
port = 7878;
[self performSelectorOnMainThread:#selector(connectToCamera) withObject:nil waitUntilDone:YES];
bytesReceivedCondition = [[NSCondition alloc] init];
requestCompletedCondition = [[NSCondition alloc] init];
requestReadyToProcess = [[NSCondition alloc] init];
isBytesReceived = false;
isRequestCompleted = false;
isRequestReadyToProcess = false;
responseString = [[NSString alloc] init];
openBracesCount = 0;
mutex = [[NSLock alloc] init];
}
return self;
}
pragma mark-
pragma establish socket communication.
/*
* connectToCamera
*
*/
- (void) connectToCamera
{
NSString *urlStr = ip;
if (![urlStr isEqualToString:#""])
{
NSURL *website = [NSURL URLWithString:urlStr];
if (!website)
{
NSString* messageString = [NSString stringWithFormat:#"%# is not a valid URL",website];
CCLog(LOG_ERROR, messageString);
return;
}
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(urlStr), port, &readStream, &writeStream);
//cast the CFStreams to NSStreams
inputStream = (__bridge_transfer NSInputStream *)readStream;
outputStream = (__bridge_transfer NSOutputStream *)writeStream;
//set the delegate
[inputStream setDelegate:self];
[outputStream setDelegate:self];
//schedule the stream on a run loop
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//open the stream
[inputStream open];
[outputStream open];
if(readStream==NULL)
{
CCLog(LOG_INFO, #"readstream NULL");
}
if(writeStream == NULL)
{
CCLog(LOG_INFO, #"writeStream NULL");
}
[self startConnectionTimeoutTimer];
}
}
pragma mark -
pragma getter methods
/*
* getIP
*
* #return
* Ip address to which socket is connected
*/
-(NSString *) getIP
{
return ip;
}
/*
* getPort
*
* #return
* Port number to which socket is connected
*/
-(int) getPort
{
return port;
}
pragma mark-
pragma Handle socket callbacks.
(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:stream];
[array addObject:[NSNumber numberWithInt:eventCode]];
[self performSelectorInBackground:#selector(myStream:) withObject:array];
}
(void)myStream:(NSMutableArray*) array
{
NSNumber *number = [array objectAtIndex:1];
int eventCode = [number intValue];
switch(eventCode)
{
case NSStreamEventErrorOccurred:
{
CCLog(LOG_ERROR, #"In Command Socket NSStreamEventErrorOccurred");
//[self disconnect];
//[[ErrorDetails getInstance] reportError:NSStreamEventErrorOccurred];
break;
}
//Read from stream
case NSStreamEventHasBytesAvailable:
{
CCLog(LOG_INFO, #"In Command Socket NSStreamEventHasBytesAvailable");
[self handleCommandPortDataReceived];
break;
}
//Write to stream
case NSStreamEventHasSpaceAvailable:
{
#synchronized(self)
{
[requestReadyToProcess lock];
while (isRequestReadyToProcess == false)
{
[requestReadyToProcess wait];
}
[requestReadyToProcess unlock];
CCLog(LOG_INFO,#"In Command Socket NSStreamEventHasSpaceAvailable");
#try
{
#synchronized(requestString)
{
if(requestString != nil)
{
if(outputStream != nil)
{
int dataSent;
uint8_t* data = (uint8_t *)[requestString cStringUsingEncoding:NSUTF8StringEncoding];
responseString = #"";
//[requestReadyToProcess lock];
isRequestReadyToProcess = false;
//[requestReadyToProcess signal];
dataSent = [outputStream write:data maxLength:strlen((char*)data)];
if(dataSent != -1)
{
NSString* message = [NSString stringWithFormat:#"Bytes written %d for request\n %#",dataSent, requestString];
CCLog(LOG_REQUEST, message);
requestString = nil;
isBytesReceived = false;
[bytesReceivedCondition lock];
while (isBytesReceived ==false)
{
[bytesReceivedCondition wait];
}
[requestCompletedCondition lock];
isRequestCompleted = true;
[requestCompletedCondition signal];
[requestCompletedCondition unlock];
[bytesReceivedCondition unlock];
}
else
{
CCLog(LOG_INFO, #"Command Socket : Request not sent (dataSent == -1)");
responseString = #"{ \"rval\": -104}";
CCLog(LOG_RESPONSE, responseString);
[self removeRequestFromQueue];
}
}
else
{
CCLog(LOG_INFO, #"in else :(outputStream != nil)");
}
}
}
}
#catch (NSException *e)
{
CCLog(LOG_WARNING, e.description);
}
}
break;
}
case NSStreamEventNone:
{
CCLog(LOG_INFO, #"In Command Socket NSStreamEventNone");
break;
}
case NSStreamEventOpenCompleted:
{
CCLog(LOG_INFO, #"In Command Socket NSStreamEventOpenCompleted");
[self stopConnectionTimeoutTimer];
break;
}
case NSStreamEventEndEncountered:
{
CCLog(LOG_INFO, #"Command Socket NSStreamEventEndEncountered");
[self disconnectWithNotification:YES];
break;
}
}
}
/*
* execute
*
* #param
* request :command to be sent over socket to camera
*
* #return
* responce :response received from camera
*
*/
-(NSString *) executeRequest :(NSString *)request
{
CCLog(LOG_INFO, #"Command Socket Executing request");
[self performSelectorOnMainThread:#selector(startRequestTimeoutTimer) withObject:nil waitUntilDone:NO];
isRequestCompleted = false;
requestString = request;
responseString = #"";
[requestReadyToProcess lock];
isRequestReadyToProcess = true;
[requestReadyToProcess signal];
[requestReadyToProcess unlock];
[requestCompletedCondition lock];
while (isRequestCompleted ==false)
{
[requestCompletedCondition wait];
}
CCLog(LOG_INFO, #"Command Socket Execute request : request completed");
[requestCompletedCondition unlock];
CCLog(LOG_RESPONSE, responseString);
return responseString;
}
pragma mark-
pragma Handle connection time out
// Call this when you initiate the connection
- (void)startConnectionTimeoutTimer
{
[self stopConnectionTimeoutTimer]; // Or make sure any existing timer is stopped before this method is called
NSTimeInterval interval = 10.0; // Measured in seconds, is a double
self.connectionTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(handleConnectionTimeout)
userInfo:nil
repeats:NO];
}
(void)handleConnectionTimeout
{
responseString = #"{ \"rval\": -103}";
CCLog(LOG_RESPONSE, responseString);
[self removeRequestFromQueue];
[self disconnectWithNotification:YES];
[self stopConnectionTimeoutTimer];
}
// Call this when you initiate the connection
- (void)startRequestTimeoutTimer
{
[self stopRequestTimeoutTimer]; // Or make sure any existing timer is stopped before this method is called
NSTimeInterval interval = 20.0; // Measured in seconds, is a double
self.requestTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(handleRequestTimeout)
userInfo:nil
repeats:NO];
}
(void)handleRequestTimeout
{
responseString = #"{ \"rval\": -103}";
CCLog(LOG_RESPONSE, responseString);
[self connectToCamera];
[self stopRequestTimeoutTimer];
[self removeRequestFromQueue];
}
// Call this when you successfully connect
- (void)stopRequestTimeoutTimer
{
if (requestTimeoutTimer)
{
[requestTimeoutTimer invalidate];
requestTimeoutTimer = nil;
}
}
-(void) disconnectWithNotification:(BOOL)showNotification
{
CCLog(LOG_INFO, #"Socket Disconnected");
[inputStream close];
[inputStream setDelegate:nil];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
inputStream = nil;
[outputStream close];
[outputStream setDelegate:nil];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
outputStream = nil;
[[CCCore getInstance] disconnectWithNotification:showNotification];
}
// Call this when you successfully connect
- (void)stopConnectionTimeoutTimer
{
if (connectionTimeoutTimer)
{
[connectionTimeoutTimer invalidate];
connectionTimeoutTimer = nil;
}
if (requestTimeoutTimer)
{
[requestTimeoutTimer invalidate];
requestTimeoutTimer = nil;
}
}
-(void) handleCommandPortDataReceived
{
[mutex lock];
[self stopRequestTimeoutTimer];
#try
{
long size = 1024;
uint8_t buf[size];
unsigned int len = 0;
do
{
// read input stream into buffer
strcpy((char *)buf, "\0");
len = [inputStream read:buf maxLength:size];
//NSLog(#"Size = %ld Len = %d, Buf = %s",size, len, (char *)buf);
// Following code checks if we have received complete response by matching "{" and "}"
// from input stream. We continue to form response string unless braces are matched.
if (len > 0)
{
// Create nsdata from buffer
NSMutableData *_data = [[NSMutableData alloc] init];
[_data appendBytes:(const void *)buf length:len];
// create temporary string form nsdata
NSString* currentString = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
// check the occurances of { and } in current string
int currentOpeningBraceCount = [[currentString componentsSeparatedByString:#"{"] count] - 1;
int currentClosingBraceCount = [[currentString componentsSeparatedByString:#"}"] count] - 1;
openBracesCount = (openBracesCount + currentOpeningBraceCount) - currentClosingBraceCount;
responseString = [responseString stringByAppendingString:currentString];
// NSLog(#"Total:%d currentOpen:%d currentClose:%d\n\n",openBracesCount, currentOpeningBraceCount, currentClosingBraceCount);
// NSLog(#"Current String : %#\n\n",currentString);
// NSLog(#"Final String : %#",finalString);
// NSLog(#"+++++++++++++++++++++++++++++");
}
else
break;
} while (openBracesCount != 0);
NSRange range = [responseString rangeOfString:#"get_file_complete"];
if(range.location == NSNotFound)
{
//remove it from queue
[bytesReceivedCondition lock];
isBytesReceived = true;
[bytesReceivedCondition signal];
[bytesReceivedCondition unlock];
}
//responseString = #"";
}
#catch (NSException* e)
{
[self connectToCamera];
}
[mutex unlock];
}
-(void) removeRequestFromQueue
{
//remove it from queue
requestString = nil;
[requestReadyToProcess lock];
isRequestReadyToProcess = false;
[requestReadyToProcess unlock];
[requestCompletedCondition lock];
isRequestCompleted = true;
[requestCompletedCondition signal];
[requestCompletedCondition unlock];
}
#end
Which OS version are you trying this on?? I'm having the similar issue, in 10.7 and up it is all good, but on 10.6 and below I get the very same issue you are having I'm doing some debugging but so far have not come up with a good resolution yet.

How to stop AudioQueue?

i am using text to speech, starting audio works fine, but i cant stop it. here is how i do start audio:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void) {
[[self view] setNeedsDisplay];
[self synthesizeInBackground];
[queue waitUntilAllOperationsAreFinished];
[self setIsSpeaking: false];
[[self view] setNeedsDisplay];
});
synthesizeInBackground
- (void) synthesizeInBackground {
XLog(#"-----------------------------------entered");
queue = [[NSOperationQueue alloc] init];
XLog(#"queue: %#", queue);
operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(synthesize) object:nil];
XLog(#"operation: %#", operation);
[queue addOperation: operation];
}
synthesize
- (void)synthesize {
XLog(#"-----------------------------------entered");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
callback_userdata userdata;
NSError *error = nil;
self.paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
self.documentsDirectory = [self.paths objectAtIndex:0];
self.path = [self.documentsDirectory stringByAppendingPathComponent:#"readSearchresults.txt"];
IvonaStreamer *streamer = [[IvonaStreamer alloc] initWithVoice:voice withText:[NSString stringWithContentsOfFile:self.path encoding:NSUTF8StringEncoding error:&error] atSpeed:[NSNumber numberWithFloat:-1]];
//IvonaStreamer *streamer = [[IvonaStreamer alloc] initWithVoice:voice withText:#"Dies ist ein Testtext." atSpeed:[NSNumber numberWithFloat:-1]];
if (streamer == nil) {
XLog(#"Cannot start streamer");
[self setTtsError: #"Cannot start streamer"];
return;
}
userdata.speak = &(self->isSpeaking);
userdata.streamer = streamer;
#define NUM_BUFFERS 3
#define BUFFER_SIZE 22050
OSStatus err;
AudioQueueRef audioQueue;
//XLog(#"audioQueue: %d", audioQueue);
XLog(#"[voice getSampleRate]: %i", [voice getSampleRate]);
AudioStreamBasicDescription deviceFormat;
deviceFormat.mSampleRate = [voice getSampleRate];
deviceFormat.mFormatID = kAudioFormatLinearPCM;
deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
deviceFormat.mBytesPerPacket = 2;
deviceFormat.mFramesPerPacket = 1;
deviceFormat.mBytesPerFrame = 2;
deviceFormat.mChannelsPerFrame = 1;
deviceFormat.mBitsPerChannel = 16;
deviceFormat.mReserved = 0;
XLog(#"deviceFormat.mSampleRate: %f", deviceFormat.mSampleRate);
/*
XLog(#"deviceFormat.mSampleRate: %f", deviceFormat.mSampleRate);
XLog(#"deviceFormat.mFormatID: %lu", deviceFormat.mFormatID);
XLog(#"deviceFormat.mFormatFlags: %lu", deviceFormat.mFormatFlags);
XLog(#"deviceFormat.mBytesPerPacket %lu", deviceFormat.mBytesPerPacket);
XLog(#"deviceFormat.mFramesPerPacket %lu", deviceFormat.mFramesPerPacket);
XLog(#"deviceFormat.mBytesPerFrame %lu", deviceFormat.mBytesPerFrame);
XLog(#"deviceFormat.mChannelsPerFrame %lu", deviceFormat.mChannelsPerFrame);
XLog(#"deviceFormat.mBitsPerChannel %lu", deviceFormat.mBitsPerChannel);
XLog(#"deviceFormat.mReserved %lu", deviceFormat.mReserved);
*/
err = AudioQueueNewOutput(&deviceFormat,
AudioQueueCallback,
&userdata,
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes,
0,
&audioQueue);
if (err != noErr) {
XLog(#"Cannot create audio output");
[self setTtsError: #"Cannot create audio output"];
[streamer stop];
return;
}
AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning,
AudioQueuePropertyListener, NULL);
for (int i = 0; i < NUM_BUFFERS; i++) {
AudioQueueBufferRef buffer;
err = AudioQueueAllocateBuffer(audioQueue, BUFFER_SIZE, &buffer);
if (err != noErr) {
XLog(#"Cannot allocate audio buffer");
[self setTtsError: #"Cannot allocate audio buffer"];
[streamer stop];
return;
}
AudioQueueCallback(&userdata, audioQueue, buffer);
}
err = AudioQueueStart(audioQueue, NULL);
if (err != noErr) {
XLog(#"Cannot start audio");
[self setTtsError: #"Cannot start audio"];
[streamer stop];
return;
}
CFRunLoopRun();
[streamer stop];
[pool release];
}
AudioQueueCallback
void AudioQueueCallback(void *userData, AudioQueueRef audioQueue,
AudioQueueBufferRef buffer)
{
//XLog(#"-----------------------------------entered");
void *data = buffer->mAudioData;
UInt32 num_bytes = buffer->mAudioDataBytesCapacity;
//XLog(#"num_bytes: %lu", num_bytes);
UInt32 to_write = num_bytes / sizeof(short);
//XLog(#"to_write: %lu", to_write);
NSInteger num_samples;
//XLog(#"num_samples: %i", num_samples);
IvonaStreamer *streamer = ((callback_userdata*) userData)->streamer;
bool *enabled = ((callback_userdata*) userData)->speak;
//XLog(#"streamer.getWarnings: %#", streamer.getWarnings);
if(!*enabled) {
XLog(#"!*enabled");
AudioQueueStop(audioQueue, false);
}
num_samples = [streamer synthSamples:to_write toCArray:data];
//XLog(#"num_samples: %i", num_samples);
if (num_samples > 0) {
//XLog(#"num_samples > 0");
buffer->mAudioDataByteSize = num_samples * sizeof(short);
AudioQueueEnqueueBuffer(audioQueue, buffer, 0, NULL);
} else {
//XLog(#"! (num_samples > 0)");
AudioQueueStop(audioQueue, false);
}
}
AudioQueuePropertyListener
void AudioQueuePropertyListener(void *userData, AudioQueueRef audioQueue,
AudioQueuePropertyID id)
{
XLog(#"-----------------------------------entered");
UInt32 isRunning, size = sizeof(isRunning);
AudioQueueGetProperty(audioQueue, kAudioQueueProperty_IsRunning, &isRunning, &size);
if (isRunning == 0) {
XLog(#"isRunning == 0");
CFRunLoopStop(CFRunLoopGetCurrent());
}
if (isRunning != 0) {
XLog(#"nicht null#######");
}
}
I try to stop in other method(UIAlertView delegate method):
if (alertView.tag == 997) {
if (buttonIndex == 0) {
XLog(#"vorlesen abbrechen geklickt.");
[queue cancelAllOperations];
AudioQueueRef audioQueue;
//AudioQueueDispose(audioQueue, false);
AudioQueueStop(audioQueue, false);
}
i am cancelling all operations and calling AudioQueueDispose, also tried with AudioQueueStop, but nothing works here.
So my question is, HOW can i stop audio here?
AudioQueueStop should work and be sufficient. From Apples Documentation, AudioQueueReset is called from AudioQueueStop.
AudioQueueDispose is a bit too much if you want to start it again later.
I believe that you need to call AudioQueueReset before you call AudioQueueStop.
AudioQueueReset (audioQueue);
AudioQueueStop (audioQueue, YES);
AudioQueueDispose (audioQueue, YES);

Resources