简体   繁体   English

Objective-C字符串编码问题

[英]objective-C string encoding problem

Ok, I'm fairly new to C sockets but I just need to do some simple sendto() and recvfrom() calls to get a string out across a network, using multicast sockets. 好的,我对C套接字还很陌生,但是我只需要执行一些简单的sendto()和recvfrom()调用即可使用多播套接字在网络上获取字符串。 After looking around and reading several guides (including Beej's), I found the code below which does the job of listening for messages sent over a multicast socket (which is what I need). 环顾四周并阅读了几本指南(包括Beej的指南)后,我发现下面的代码可以侦听通过多播套接字发送的消息(这是我所需要的)。 The program runs fine when it is in main, but my problem arises when I put it in a method (ie a method called "listenForPackets") elsewhere in my project and attempt to run it in another thread on a runloop. 该程序在主程序时运行良好,但是当我将其放在项目其他位置的方法(即称为“ listenForPackets”的方法)中并尝试在Runloop的另一个线程中运行时,我的问题出现了。 After debugging though, the problem comes down to the variable "mc_addr_str" which is assigned to equal argv[1] in the main method. 但是,在调试之后,问题归结于变量“ mc_addr_str”,该变量在main方法中分配为等于argv [1]。

#include <sys/types.h>  // for type definitions
#include <sys/socket.h> // for socket API calls
#include <netinet/in.h> // for address structs
#include <arpa/inet.h>  // for sockaddr_in
#include <stdio.h>      // for printf() and fprintf()
#include <stdlib.h>     // for atoi()
#include <string.h>     // for strlen()
#include <unistd.h>     // for close()

#define MAX_LEN  1024   // maximum receive string size
#define MIN_PORT 1024   // minimum port allowed
#define MAX_PORT 65535  // maximum port allowed

int main(int argc, char *argv[]) {

    int sock;                     // socket descriptor
    int flag_on = 1;              // socket option flag
    struct sockaddr_in mc_addr;   // socket address structure
    char recv_str[MAX_LEN+1];     // buffer to receive string
    int recv_len;                 // length of string received
    struct ip_mreq mc_req;        // multicast request structure
    char* mc_addr_str;            // multicast IP address
    unsigned short mc_port;       // multicast port
    struct sockaddr_in from_addr; // packet source
    unsigned int from_len;        // source addr length

    // validate number of arguments
    if (argc != 3) {
        fprintf(stderr, 
                "Usage: %s <Multicast IP> <Multicast Port>\n", 
                argv[0]);
        exit(1);
    }

    mc_addr_str = argv[1];      // arg 1: multicast ip address
    mc_port = atoi(argv[2]);    // arg 2: multicast port number

    // validate the port range
    if ((mc_port < MIN_PORT) || (mc_port > MAX_PORT)) {
        fprintf(stderr, "Invalid port number argument %d.\n",
                mc_port);
        fprintf(stderr, "Valid range is between %d and %d.\n",
                MIN_PORT, MAX_PORT);
        exit(1);
    }

    // create socket to join multicast group on
    if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        perror("socket() failed");
        exit(1);
    }

    // set reuse port to on to allow multiple binds per host
    if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag_on, sizeof(flag_on))) < 0) {
        perror("setsockopt() failed");
        exit(1);
    }

    // construct a multicast address structure
    memset(&mc_addr, 0, sizeof(mc_addr));
    mc_addr.sin_family      = AF_INET;
    mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    mc_addr.sin_port        = htons(mc_port);

    // bind multicast address to socket
    if ((bind(sock, (struct sockaddr *) &mc_addr, sizeof(mc_addr))) < 0) {
        perror("bind() failed");
        exit(1);
    }

    // construct an IGMP join request structure
    mc_req.imr_multiaddr.s_addr = inet_addr(mc_addr_str);
    mc_req.imr_interface.s_addr = htonl(INADDR_ANY);

    // send an ADD MEMBERSHIP message via setsockopt
    if ((setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mc_req, sizeof(mc_req))) < 0) {
        perror("setsockopt() failed");
        exit(1);
    }

    for (;;) {          // loop forever

        // clear the receive buffers & structs
        memset(recv_str, 0, sizeof(recv_str));
        from_len = sizeof(from_addr);
        memset(&from_addr, 0, from_len);

        // block waiting to receive a packet
        if ((recv_len = recvfrom(sock, recv_str, MAX_LEN, 0, (struct sockaddr*)&from_addr, &from_len)) < 0) {
            perror("recvfrom() failed");
            exit(1);
        }

        // output received string
        printf("Received %d bytes from %s: ", recv_len, inet_ntoa(from_addr.sin_addr));
        printf("%s", recv_str);
    }

    // send a DROP MEMBERSHIP message via setsockopt
    if ((setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &mc_req, sizeof(mc_req))) < 0) {
        perror("setsockopt() failed");
        exit(1);
    }

    close(sock);  
}

Now, via help from another SO member, I have a method that will return the IPaddress to me as a NSString (I'm using it elsewhere in my program also, so I need to keep it returning NSString). 现在,在另一个SO成员的帮助下,我有一个方法可以将IPaddress作为NSString返回给我(我也在程序的其他地方使用它,因此我需要保持它返回NSString)。

-(NSString *)getIPAddress {
    NSString *address = @"error";
    struct ifaddrs *interfaces; // = NULL;
    struct ifaddrs *temp_addr; // = NULL;
    int success = 0;

    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(&interfaces);
    if (success == 0)  
    {
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        while(temp_addr != NULL)  
        {
            if(temp_addr->ifa_addr->sa_family == AF_INET)
            {
                // Check if interface is en0 which is the wifi connection on the iPhone  
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"])  
                {
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }

    // Free memory
    freeifaddrs(interfaces); 
    return address; 
}

I thought I could just do a simple little conversion 我以为我可以做一个简单的小转换

mc_addr_str = (someConversion)getIPAddress;

What I have so far is: 到目前为止,我有:

NSString *ipAddress = [[NSString alloc] initWithString:self.getIPAddress];

mc_addr_str = [ipAddress cStringUsingEncoding:[NSString defaultCStringEncoding]];

When I do this, the program makes it to the setsockopt call and then fails with an error code of -1 (I assume that's a general error code that lets the program know something bad happened and needs to abort). 当我这样做时,程序进入setsockopt调用,然后失败,错误代码为-1(我认为这是一个通用错误代码,它使程序知道发生了什么不好的事情并需要中止)。 Also, when I am assigning mc_addr_str in the previous statement, I get 另外,当我在上一条语句中分配mc_addr_str时,我得到

warning: assignment discards qualifiers from pointer target type

I'm not sure where my problem is arising from now. 我不确定从现在开始我的问题出在哪里。 Do I have a casting error during the assignment to mc_addr_str or did I use the wrong encoding? 在分配给mc_addr_str的过程中出现转换错误还是使用了错误的编码? Any input is appreciated! 任何输入表示赞赏!

cStringUsingEncoding returns a const char * , and your mc_add_str is a char * . cStringUsingEncoding返回一个const char * ,而您的mc_add_str是一个char * That's why the compiler is issuing a warning. 这就是编译器发出警告的原因。

The address you get from getIPAddresss() is the address of the interface, not of the multicast group. 从getIPAddresss()获得的地址是接口的地址,而不是多播组的地址。 Here is how the address is being used in your example, can you spot the problem? 这是您的示例中地址的使用方式,可以发现问题吗?

// construct an IGMP join request structure
mc_req.imr_multiaddr.s_addr = inet_addr(mc_addr_str);
mc_req.imr_interface.s_addr = htonl(INADDR_ANY)

You set the multicast address to the address in mc_addr_str, which holds the interface address. 您将多播地址设置为mc_addr_str中的地址,该地址保存了接口地址。 That doesn't seem right does it? 那似乎不对吗? ;) ;)

Here's what you should do: 这是您应该做的:

// construct an IGMP join request structure
mc_req.imr_multiaddr.s_addr = inet_addr("225.0.0.37");
mc_req.imr_interface.s_addr = inet_addr(mc_addr_str);

The address range from 224.0.0.0 to 239.255.255.255 is reserved for multicast addresses. 地址范围从224.0.0.0到239.255.255.255保留用于多播地址。 Don't use 224.0.0.0 to 224.0.0.255 though since these are reserved for multicast routing information. 不要使用224.0.0.0到224.0.0.255,因为它们是为多播路由信息保留的。 If you use any other addresses your setsockopt() will fail the way it does in your example. 如果您使用任何其他地址,则setsockopt()会像您在示例中那样失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM