linux 组播接收和发送代码和组播问题总结
int main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in localSock;
struct sockaddr_in addr;
struct ip_mreq group;
int datalen;
char databuf[4000];
int reuse;
struct timeval tv;
int maxfd;
fd_set readfds;
int retval;
int loopBack = 1;
printf(“devdis start\n”);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd <= 0x0)
{
printf(“create sockfd failed\n”);
return -1;
}
/*端口释放后立即就可以被再次使用*/
reuse = 0x1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
printf(“Setting SO_REUSEADDR failed\n”);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(12345);
addr.sin_addr.s_addr = inet_addr(“224.1.1.1”);
memset(&localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(12345);
localSock.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd, (struct sockaddr*)&localSock, sizeof(localSock)) != 0x0)
{
printf(“bind failed\n”);
return -1;
}
/*设置是否支持本地回环接收*/
loopBack = 0;
if(setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loopBack, sizeof(loopBack));
{
printf(“set IP_MULTICAST_LOOP failed\n”);
return -1;
}
memset(&group, 0, sizeof(group));
group.imr_multiaddr.s_addr = inet_addr(“224.1.1.1“);
group.imr_interface.s_addr =INADDR_ANY;
if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
//if(setsockopt(sockfd, IPPROTO_IP, 12, (char *)&group, sizeof(group)) < 0)
{
printf(“adding multicast group failed\n”);
return -1;
}
maxfd = sockfd;
while(1)
{
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
retval = select(maxfd + 1, &readfds, NULL, NULL, &tv);
if(retval < 0x0)
{
printf(“select err\n”);
}
else if(retval == 0x0)
{
continue;
}
else
{
if(FD_ISSET(sockfd, &readfds))
{
memset(databuf, 0, 4000);
datalen = read(sockfd, databuf, 4000);
printf(“datalen = %d\n”, datalen);
printf(“%s\n”,databuf);
sendto(sockfd, “12345”, 5, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
}
}
}
return 0;
}
1、由于socket 版本不一样,IP_ADD_MEMBERSHIP 宏定义的值不同,在v1 中定义值为5 在v2中定义值为12,如果编译器版
与内核版本不同,则可能出现加入组播组失败。
2、如果设备不添加默认路由,也可能出现加入组播组时(IP_ADD_MEMBERSHIP)返回-1错误。特imr_interfac = INADDR_ANY时。如果imr_interfac = 本地IP地址时不会出现。
3、多网卡设备上发送组播消息,需要指明确指定为要发送组播数据包的网卡IP地址,而不可以使用INADDR_ANY设置;如果使用
INADDR_ANY,则系统会默认根据路由表绑定一个明确的地址,则在接收组播信息时,无法从发送的网卡处接收到数据,发送的网卡没有被添加到组播中,必须使用setsockopt设置IP_MULTICAST_IF选项,从而修改默认的组播出口网卡。否则系统根据路由表发送到默认网关。而不一定是指定的网卡。
4、在多网卡实现多播(如设备搜索)相关的功能时,可以针对多个网卡分别执行一次操作,同时可以区分设备是从哪个网卡搜索到的。
5、主机必须成为一个或多个 IP 多播组的成员,才能接收 IP 多播stru数据报。进程可以使用以下套接字选项请求主机加入多播组:
struct ip_mreq mreq;
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
其中 mreq 为以下结构:
struct ip_mreq {
struct in_addr imr_multiaddr; /* multicast group to join */
struct in_addr imr_interface; /* interface to join on */
}
每个成员关系都与单个接口关联。可以在多个接口上加入同一组。将 imr_interface 地址指定为 INADDR_ANY 以选择缺省的多播接口。还可以通过指定主机的本地地址之一来选择特定的具有多播功能的接口。
6、IP_MULTICAST_IF:
即使主机拥有多个具有多播功能的接口,每个多播传输也是通过单个网络接口发送的。如果主机还用作多播路由器且 TTL 值大于 1,则多播可以转发到源接口之外的接口。可以使用套接字选项覆盖来自给定套接字的后续传输的缺省设置:
struct in_addr addr; setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr))
其中 addr 是所需传出接口的本地 IP 地址。通过指定地址 INADDR_ANY 恢复到缺省接口。使用 SIOCGIFCONF ioctl 获取接口的本地 IP 地址。要确定接口是否支持多播,请使用 SIOCGIFFLAGS ioctl 获取接口标志并测试是否设置了 IFF_MULTICAST 标志。此选项主要用于多播路由器以及其他专门针对 Internet 拓扑的系统服务。
7、IP_MULTICAST_LOOP
如果将多播数据报发送到发送主机本身所属的组,则缺省情况下,本地传送的 IP 层将回送此数据报的副本。另一套接字选项可为发送主机提供针对是否回送后续数据报的显式控制:
u_char loop; setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop))
其中 loop 为 0 即为禁用回送,为 1 即为启用回送。此选项通过消除因接收应用程序自己的传输内容而产生的开销,可提高单台主机上只有一个实例的应用程序的性能。对于可以在一台主机上具有多个实例或者其发送主机不属于目标组的应用程序,不应使用此选项。
如果发送主机属于其他接口的目标组,则发送初始 TTL 值大于 1 的多播数据报可以传送到其他接口上的发送主机。回送控制选项不会影响此类传送。