简体   繁体   中英

UDP Multicast socket doesn't work on a linux 64 bits platform

I wrote a very small C code to open an UDP multicast socket, it works well on a 32 bits platform but when I recompile my code and try it on a linux 64 bits platform it doesn't work. The program is pending undefinitely on the recvfrom() function. I checked if the udp frames were actually received on the specified network interface with tcpdump, but everything works fine. Does anybody have an idea on what is wrong with my code ?

Here is the first code (before your comments) :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <math.h>
#include <errno.h>

static char* server = "231.180.0.1";
static char* network = "66.46.40.10";
static int port = 50001;

static struct sockaddr_in socketAddr;
static unsigned int socketDesc;

long toLong (unsigned char* msg, int offset);

int main (void) {
    struct ip_mreq mreq;
    int bindDesc, socketOptDesc;
    int reuse = 1;
    unsigned int socketLength = sizeof(socketAddr);

    // Allocation
    memset((char *) &socketAddr, 0, sizeof(socketAddr));
    memset(&mreq, 0, sizeof(struct ip_mreq));

    /*
     * Create a datagram socket on which to receive.
     */
    printf("# Init socket (server=%s  network=%s  port=%d)\n", server, network, port);
    socketDesc = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (socketDesc < 0) {
        perror("socket() failed");
    } else {
        /*
         * Enable SO_REUSEADDR to allow multiple instances of this
         * application to receive copies of the multicast datagrams.
         */
        socketOptDesc = setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse));
        if (socketOptDesc < 0) {
            perror("setsockopt() failed");
        } else {
            /*
             * Bind to the proper port number with the IP address
             * specified as INADDR_ANY.
             */
            socketAddr.sin_family = AF_INET;
            socketAddr.sin_port = htons(port);
            socketAddr.sin_addr.s_addr = INADDR_ANY;
            bindDesc = bind(socketDesc, (struct sockaddr*) &socketAddr, sizeof(socketAddr));
            if (bindDesc < 0) {
                perror("bind() failed");
            } else {

                /*
                 * Join the multicast group on the local interface.
                 * Note that this IP_ADD_MEMBERSHIP option must be
                 * called for each local interface over which the multicast
                 * datagrams are to be received.
                 */
                mreq.imr_multiaddr.s_addr = inet_addr(server);
                mreq.imr_interface.s_addr = inet_addr(network);
                socketOptDesc = setsockopt(socketDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
                if (socketOptDesc < 0) {
                    perror("setsockopt() failed");
                } else {
                    printf("# Socket created successfully !\n");
                }
            }
        }
    }

    /*
     * Acquisition Loop
     */
    printf("# Starting reception loop...\n");
    long lastFrameNumber = -1;
    int nbDots = 0;
    while (1) {
        long frameNumber = -1;
        unsigned char buffer[65536];

        // Frame Acquisition
        int ret = recvfrom(socketDesc, buffer, 65536, 0, (struct sockaddr *) &socketAddr, &socketLength);
        if (ret < 0) {
            perror("recvfrom() failed");
        }
        // Reading frame number
        frameNumber = toLong(buffer, 28);

        if (frameNumber < 0) {
            // Context Frame
        } else if (frameNumber == 0) {
            printf("Invalid frame (frameNumber=0)\n");
        } else {
            if (frameNumber > 1 && frameNumber != (lastFrameNumber + 1)) {
                printf("%ld frame(s) lost from frame %ld\n", frameNumber - lastFrameNumber - 1, lastFrameNumber + 1);
            }
        }
        lastFrameNumber = frameNumber;

        if (frameNumber == 1) {
            if (nbDots > 50) {
                printf(".\n");
                nbDots = 0;
            } else {
                printf(".");
                fflush(stdout);
            }
            nbDots++;
        }
    }
    return EXIT_SUCCESS;
}

/* ======================================================================
 * Read 4 bytes from the specified offset and convert it to a long value.
 *
 * @input msg
 *          Byte array representing the message.
 * @input offset
 *          Byte offset.
 * @return
 *          Long value representing the frame number.
 * ====================================================================*/
long toLong (unsigned char* msg, int offset) {
    long value;
    int byte0; // bits 31..24
    int byte1; // bits 23..16
    int byte2; // bits 15..8
    int byte3; // bits 7..0
    byte0 = (0x000000FF & ((int) msg[offset + 0]));
    byte1 = (0x000000FF & ((int) msg[offset + 1]));
    byte2 = (0x000000FF & ((int) msg[offset + 2]));
    byte3 = (0x000000FF & ((int) msg[offset + 3]));
    value = ((long) (byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3)) & 0xFFFFFFFFL;
    return value;
}

EDIT : I updated my code with your comments but it doesn't work either :( Also, I forgot to say that the network is using VLAN. The network interface is eth.40 at 66.46.40.100 but it work on a 32 bit platform so it might not be the problem.

Here is the new code :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <math.h>
#include <errno.h>

static char* server = "231.180.0.1";
static char* network = "66.46.40.100";
static uint16_t port = 50001;

long toLong (unsigned char* msg, int offset);

int main (void) {
    struct sockaddr_in socketAddr;
    struct ip_mreq mreq;
    int bindDesc, socketDesc, socketOptDesc;
    socklen_t reuse = 1;
    socklen_t socketLength = sizeof(socketAddr);

    // Allocation
    memset((char *) &socketAddr, 0, sizeof(socketAddr));
    memset(&mreq, 0, sizeof(struct ip_mreq));

    /*
     * Create a datagram socket on which to receive.
     */
    printf("# Init socket (server=%s  network=%s  port=%d)\n", server, network, port);
    socketDesc = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (socketDesc < 0) {
        perror("socket() failed");
    } else {
        /*
         * Enable SO_REUSEADDR to allow multiple instances of this
         * application to receive copies of the multicast datagrams.
         */
        socketOptDesc = setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, (void *) &reuse, sizeof(reuse));
        if (socketOptDesc < 0) {
            perror("setsockopt() failed");
        } else {
            /*
             * Bind to the proper port number with the IP address
             * specified as INADDR_ANY.
             */
            socketAddr.sin_family = AF_INET;
            socketAddr.sin_port = htons(port);
            socketAddr.sin_addr.s_addr = INADDR_ANY;
            bindDesc = bind(socketDesc, (struct sockaddr*) &socketAddr, sizeof(socketAddr));
            if (bindDesc < 0) {
                perror("bind() failed");
            } else {

                /*
                 * Join the multicast group on the local interface.
                 * Note that this IP_ADD_MEMBERSHIP option must be
                 * called for each local interface over which the multicast
                 * datagrams are to be received.
                 */
                mreq.imr_multiaddr.s_addr = inet_addr(server);
                mreq.imr_interface.s_addr = inet_addr(network);
                socketOptDesc = setsockopt(socketDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
                if (socketOptDesc < 0) {
                    perror("setsockopt() failed");
                } else {
                    printf("# Socket created successfully !\n");
                }
            }
        }
    }

    /*
     * Acquisition Loop
     */
    printf("# Starting reception loop...\n");
    long lastFrameNumber = -1;
    int nbDots = 0;
    while (1) {
        unsigned char buffer[65536];

        // Frame Acquisition
        ssize_t ret = recvfrom(socketDesc, buffer, 65536, 0, (struct sockaddr *) &socketAddr, &socketLength);
        if (ret < 0) {
            perror("recvfrom() failed");
        } else {
            printf("# Receiving frame\n");
        }
    }
    return EXIT_SUCCESS;
}

One obvious 64-bit problem that you have in the code is that the last argument to recvfrom should be a pointer to a socklen_t and not unsigned int that you have in your code. socklen_t will most likely be a 64 bit variable on 64 bit machines, while unsigned int will most likely be 32 bits.

Another problem is socketDesc being unsigned. File descriptors are always signed so that you can actually detect errors from functions that return them. Your checks for errors from all the functions will not work, so it's possible that your code failed much earlier and you didn't notice.

Another possible problem is your toLong function, long is quite often 64 bits on a 64 bits platform while you're treating it as a 32 bit value.

Try building with warnings, the compiler should be quite helpful. This is definitely something that your compiler will warn you about. If that doesn't help, double check the manual pages for all the functions you call and check that the types are right.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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