简体   繁体   English

UDP组播套接字在Linux 64位平台上不起作用

[英]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. 我写了一个很小的C代码来打开UDP多播套接字,它在32位平台上很好用,但是当我重新编译代码并在Linux 64位平台上尝试时,它不起作用。 The program is pending undefinitely on the recvfrom() function. 该程序在recvfrom()函数上无限期挂起。 I checked if the udp frames were actually received on the specified network interface with tcpdump, but everything works fine. 我检查了是否使用tcpdump在指定的网络接口上实际收到了udp帧,但是一切正常。 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. 编辑:我用您的注释更新了我的代码,但它也不起作用:(另外,我忘了说网络正在使用VLAN。网络接口是eth.40,位于66.46.40.100,但它可以在32位平台上工作所以这可能不是问题。

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. 代码中存在一个明显的64位问题,即recvfrom的最后一个参数应该是指向socklen_t的指针,而不是代码中没有unsigned int的指针。 socklen_t will most likely be a 64 bit variable on 64 bit machines, while unsigned int will most likely be 32 bits. socklen_t在64位机器上很可能是64位变量,而unsigned int很可能是32位。

Another problem is socketDesc being unsigned. 另一个问题是socketDesc被取消签名。 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. 另一个可能的问题是您的toLong函数,当您将其视为32位值时, long通常在64位平台上为64位。

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. 如果那没有帮助,请仔细检查手册页以了解您调用的所有函数,并检查类型是否正确。

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

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