解决UDP加入多播组失败问题:IP_ADD_MEMBERSHIP: No buffer space available

一、问题背景

    手上有个嵌入式linux的开发项目,用的海思 dv300 的芯片。有个需求是需要加入UDP多播组实现局域网的通信。但是使用setsockopt加入多播组时总是失败,错误信息是:No buffer space available。示例代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

int udp_server_init(uint16_t port)
{
	int sockfd;

	if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
	{
		perror("socket");
		return -1;
	}


	struct sockaddr_in server_addr;

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    
    int opt = 1;

	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt)) == -1)
	{
		close(sockfd);
		perror("SO_REUSEADDR");
		return -3;
	}


	if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
	{
		close(sockfd);
		perror("bind");
		return -2;
	}



	return sockfd;
}




void udp_test(void)
{
	//	创建一个 UDP 的搜索 服务 套接字

	int socket_fd;

	if ((socket_fd = udp_server_init(60002)) < 0)
	{
		printf("Create udp socket error[%d]\r\n", socket_fd);

		return;
	}

	//	加入多播组

	struct ip_mreq mreq;

	memset(&mreq, 0, sizeof(mreq));

	mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.249");
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);


    int loop = 1;
    if( -1 == setsockopt(socket_fd,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop)))  // 允许内环
	{
		perror("IP_MULTICAST_LOOP"); // 设置失败也没关系
	}

	/* 把本机加入组播地址,即本机网卡作为组播成员,只有加入组才能收到组播消息 */
	if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1)
	{
		perror("IP_ADD_MEMBERSHIP");
		pthread_exit(NULL);
	}else
	{
		printf("IP_ADD_MEMBERSHIP OK!\r\n");
	}

	int buffersize = 1024;
	if(setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)) == -1) // 设置接收缓存
	{
		perror("SO_RCVBUF");
		pthread_exit(NULL);
	}

	socklen_t opt = 1;

	if (setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, (const void *)&opt, sizeof(opt)) == -1) // 允许广播
	{
		close(socket_fd);
		perror("SO_BROADCAST");
		pthread_exit(NULL);
	}

    fcntl(socket_fd, F_SETFL, fcntl(socket_fd, F_GETFL, 0) | O_NONBLOCK); // 设置为非阻塞IO

	while (1)
	{
		usleep(1000 * 10);

        uint8_t recv_buff[1024];
        struct sockaddr_in remote_addr;		//	远程地址
        socklen_t remote_addr_size = sizeof(remote_addr);
        
        //recvfrom(sockfd, recv_buff, sizeof(recv_buff), 0, addr, addr_len)
        if(recvfrom(socket_fd, recv_buff, sizeof(recv_buff), 0,  (struct sockaddr *)&remote_addr, &remote_addr_size) > 0)
        	printf("recv from %s, port %d\r\n", inet_ntoa(remote_addr.sin_addr), remote_addr.sin_port);
    }
}

int main(void)
{
    udp_test();
    return 0;
}

二、分析与测试

    我用同样的程序,在另一家的设备上(同样也是海思 dv300 的芯片)完全没问题。查了很多资料都没找到有用的信息。这时觉得可能是不是内核参数关于网络这块的问题,就找了一下关于UDP组播的内核参数igmp_max_memberships
    OK,先查看一下内核参数:sysctl net.ipv4.igmp_max_memberships。显示:net.ipv4.igmp_max_memberships = 0。这尼玛就很无语了,原来是这个地方在作怪。
    修改一下sysctl -w net.ipv4.igmp_max_memberships=20。然后怀着激动的心情再去跑一下程序,可以了!害!

三、最终解决方案

    我觉得有两种解决方案,一种是在程序刚跑起来的时候先调用system执行shell命令。但是这个每次重启会恢复,但是程序跑起来又可以设置,勉强能用。

system("sysctl -w net.ipv4.igmp_max_memberships=20");

    第二种是重新编译内核,这要让内核的同学去做了… …

四、补充

    参数igmp_max_memberships含义:一个socket所允许加入的多播组的最大数量。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值