views:

802

answers:

2

Ok, basically I have a run loop going in my application every second or two, while at the same time I have another thread going that is looping through the listenForPackets method; broadcastMessage is only initiated when another action method takes place. The important part of this question is that when the listener thread is running seperately from the main thread, it never print out any of the print commands and it seems to not allow access to a global variable that I have defined called recvMessage which lies outside of the interface and implementation sections.

In my code, I have it set up so that every time it runs through the main run loop, it updates a UILabel in my GUI. When the app is running, my label stays blank the whole time and never changes. I've double-checked the GUI and everything is linked up there correctly and my label is instantiated correctly too (I use the name "label" as an instance of UILabel in the code below). Does anyone have any ideas why my label is updating? The networking aspect of things is fine I believe, because I just got that done and everything is "talking" ok. Maybe it's a variable scope issue that I don't know about, or are separate threads allowed to access global variables, such as the one I have used below (rcvMessage)? I'm fairly new to multi-threaded applications but I don't believe it's really that hard to implement, using NSThread (only one line of code).

Global Variable

NSString *recvMessage;

Main Runloop - the section that updates the label every time it goes through the runloop

if (label.text != recvMessage)
    label.text = recvMessage

Talker Method

-(void)broadcastMessage { // (NSString*)msg {
    msg = @"From_Master";

    NSLog(@"broadcastMessage - Stage 1");
    mc_ttl = 15; // number of node hops the message is allowed to travel across the network
    // define the port we will be using
    mc_port = MYPORT;

    // create a socket for sending to the multicast address
    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
     NSLog(@"ERROR: broadcastMessage - socket() failed");
     return;
    }

    memset(&mc_addr, 0, sizeof(mc_addr));
    mc_addr.sin_family      = AF_INET;
    mc_addr.sin_addr.s_addr = inet_addr(GROUP_ADDRESS);
    mc_addr.sin_port        = htons(MYPORT);

    NSLog(@"broadcastMessage - Stage 2");

// set the TTL (time to live/hop count) for the send
    if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &mc_ttl, sizeof(mc_ttl))) < 0) {
     NSLog(@"ERROR: broadcastMessage - setsockopt() failed");
     return;
    } 

    NSLog(@"broadcastMessage - Stage 3");

    // clear send buffer
    memset(send_str, 0, sizeof(send_str));

    // convert the message to a C string to send
    [msg getCString:send_str maxLength:MAX_LEN encoding:NSASCIIStringEncoding];

    //while (fgets(send_str, MAX_LEN, stdin)) {
    NSLog(@"broadcastMessage - Stage 4");
    NSLog(@"Message =");
    printf(send_str);

    // send string to multicast address
    if ((sendto(sock, send_str, sizeof(send_str), 0, (struct sockaddr *)&mc_addr, sizeof(mc_addr))) < 0) {
     NSLog(@"ERROR: broadcastMessage - sendto() sent incorrect number of bytes");
     //return;
    }
    NSLog(@"Sent Message -");
    printf(send_str);
    NSLog(@"broadcastMessage - Stage 5");

    // clear send buffer
    memset(send_str, 0, sizeof(send_str));
    NSLog(@"broadcastMessage - Stage 6 - Complete");
    close(sock);
}

Listener Method

-(void)listenForPackets {
    listeningFlag_on = 1; // allows reuse of the same socket

    NSLog(@"listenForPackets - Stage 1");
    if ((listeningSock = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP)) < 0) {
     NSLog(@"ERROR: listenForPackets - socket() failed");
     return;
            // make the method return an int instead of void and use this statement to check for errors
    }

    NSLog(@"listenForPackets - Stage 2");

    // set reuse port to on to allow multiple binds per host
    if ((setsockopt(listeningSock, SOL_SOCKET, SO_REUSEADDR, &listeningFlag_on, sizeof(listeningFlag_on))) < 0) {
     NSLog(@"ERROR: listenForPackets - setsockopt() Reuse failed");
     return;
            // make the method return an int instead of void and use this statement to check for errors
    }

    // construct a multicast address structure after erasing anything in the listeningmc_addr structure
    memset(&listeningmc_addr, 0, sizeof(listeningmc_addr));
    listeningmc_addr.sin_family      = AF_INET;
    listeningmc_addr.sin_addr.s_addr = htonl(INADDR_ANY); // different from sender
    listeningmc_addr.sin_port        = htons(MYPORT);

    // bind multicast address to socket
    if ((bind(listeningSock, (struct sockaddr *)&listeningmc_addr, sizeof(listeningmc_addr))) < 0) {
     NSLog(@"ERROR: listenForPackets - bind() failed");
     perror("Bind() -");
     return;       // make the method return an int instead of void and use this statement to check for errors
    }

    //*********************************************************************************

    NSString *ipAddress = [[NSString alloc] initWithString:self.getIPAddress];
    const char *tmp = [ipAddress UTF8String];
    listeningMc_addr_str = tmp;

    printf("%s\n", listeningMc_addr_str);

    listeningMc_req.imr_multiaddr.s_addr = inet_addr(GROUP_ADDRESS);
    listeningMc_req.imr_interface.s_addr = htonl(INADDR_ANY);
    // send an ADD MEMBERSHIP message via setsockopt
    if ((setsockopt(listeningSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &listeningMc_req, sizeof(listeningMc_req))) < 0) {
     NSLog(@"ERROR: listenForPackets - setsockopt() failed");
     int err = errno;
     NSLog(@"errno - %i", err);
     NSLog(@"Error = %s", strerror(err));
     perror("ERROR");
     return;       // make the method return an int instead of void and use this statement to check for errors
    }

    NSLog(@"listenForPackets - Stage 3");
    for (;;) {          // loop forever

     // clear the receive buffers & structs
     memset(listeningRecv_str, 0, sizeof(listeningRecv_str));
     listeningFrom_len = sizeof(listeningFrom_addr);
     memset(&listeningFrom_addr, 0, listeningFrom_len);

     NSLog(@"Test #1 Complete");
     //msgStatus.text = @"Waiting...";

     // block waiting to receive a packet
     listeningFrom_len = sizeof(listeningmc_addr);
     if ((listeningRecv_len = recvfrom(listeningSock, listeningRecv_str, MAX_LEN, 0, (struct sockaddr*)&listeningmc_addr, &listeningFrom_len)) < 0) {
      NSLog(@"ERROR: listenForPackets - recvfrom() failed");
      return;      // make the method return an int instead of void and use this statement to check for errors
     }
     NSLog(@"Test #2 Complete - Received a Message =");
     NSLog(@"listenForPackets - Stage 4");

     // listeningRecv_str
    **tmpString = [[NSString alloc] initWithCString:listeningRecv_str encoding:NSASCIIStringEncoding];
         NSLog(@"Message Received =");
         NSLog(tmpString);
         recvMessage = tmpString;**
     //}
     // received string
     printf("Received %d bytes from %s: ", listeningRecv_len, inet_ntoa(listeningFrom_addr.sin_addr));
     printf("%s", listeningRecv_str);
     //}
    }
    // send a DROP MEMBERSHIP message via setsockopt
    if ((setsockopt(listeningSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &listeningMc_req, sizeof(listeningMc_req))) < 0) {
     NSLog(@"ERROR: listenForPackets - setsockopt() drop membership failed");
     //return 1;       // make the method return an int instead of void and use this statement to check for errors
    }
    close(listeningSock);
    NSLog(@"listenForPackets - Stage 5 - Complete");
}
+4  A: 

Yes, all threads can access global variables. There are certainly problems with how you are using the global--you leak the NSString you create every time the variable is updated, and you are accessing the same memory from two threads without any access control--but there's nothing that would prevent the variable from being updated.

If none of your log messages are being printed, the problem is that the code is never being run, which is why the variable isn't changing. You should take a look at the code that is supposed to kick off this new thread.

smorgan
Thanks, you helped a lot. Is there any article or documentation I could read about variable access control and how to prevent memory leakage? There's not much on the NSThread documentation that I could find.
Josh Bradley
There's nothing thread-related about your leak; for that you just need to a basic guide to memory management in Cocoa, such as http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
smorgan
A: 

Also note that updating any UI components, you need to use the method "performSelectorOnMainThread" to do any value setting of label text or any other GUI elements. The value will not update from background threads.

Kendall Helmstetter Gelner