linux系统下UDP组播指定网卡通信
linux系统下多网卡UDP通信时出现指定网卡发送数据失败的问题,需注意一下问题:
- 关闭系统防火墙
- 组播网关设置
- 指定需要发送数据的IP
此程序只实现了多网卡同时接收数据和指定网卡发送数据的功能,未作将网卡A数据收到之后从网卡B转发出去操作,此部分逻辑功能需要再次添加实现。
实现环境
接收端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define GROUP_IP "224.0.1.0"
int main(int argc, char const *argv[])
{
/* code */
int serv_sock;
struct sockaddr_in my_addr,client_addr;
// 1. 创建通信的套接字
serv_sock = socket(AF_INET, SOCK_DGRAM, 0);
if(serv_sock == -1)
{
perror("socket error");
exit(0);
}
//设置组播属性
struct ip_mreq mreq; //组播结构体
/* use setsockopt() to request that the kernel join a multicast group */
mreq.imr_multiaddr.s_addr=inet_addr(GROUP_IP); //组播组的ip地址
mreq.imr_interface.s_addr=htonl(INADDR_ANY); //加入的客户端主机的ip地址 INADDR_ANY为0.0.0.0,泛指本机,表示本机所有的ip.
inet_pton(AF_INET, GROUP_IP, &mreq.imr_multiaddr);
inet_pton(AF_INET, "192.168.4.29", &mreq.imr_interface);
//客户端只有在加入多播组后才能接受多播组的数据
setsockopt(serv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&mreq,sizeof(mreq));
//绑定本机地址
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9999); // 端口号
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY 任意网卡
if( bind(serv_sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 )
{
printf("udp server bind error\n");
return -1;
}
printf("udp bind success [%s] [%d]\n",GROUP_IP,my_addr.sin_port);
//接收数据
char recvBuf[1024]={0};
socklen_t addrlen = sizeof(struct sockaddr_in);
char *ip = NULL;
int port = 0;
int ret = 0;
while (1)
{
memset(recvBuf,0,sizeof(recvBuf));
ret = recvfrom(serv_sock,recvBuf,sizeof(recvBuf),0,(struct sockaddr *)&client_addr,&addrlen);
ip = inet_ntoa(client_addr.sin_addr);
port = ntohs(client_addr.sin_port);
printf("[%s][%d]buf:%s ret:%d\n",ip,port,recvBuf,ret);
}
close(serv_sock);
return 0;
}
客户端发送端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define GROUP_IP "224.0.1.0"
#define GROUP_PORT 9999
int main(int argc, char const *argv[])
{
int serv_sock;
struct sockaddr_in my_addr,zubo_addr;
// 1. 创建通信的套接字
serv_sock = socket(AF_INET, SOCK_DGRAM, 0);
if(serv_sock == -1)
{
perror("socket error");
exit(0);
}
/*
将本地socket添加到多播组中,注意,此处针对struct ip_mreq结构体需要填充两个成员,
成员ipmr.imr_interface.s_addr的值指定的是将要发送的网卡的ip地址,
成员impr.imr_multiaddr指定的是组播地址;
如果指定为INADDR_ANY则系统会绑定一个默认网卡的具体ip(根据默认网关选择),则会出现特定网卡可以发送和接收组播信息,另一网卡不可以。
即指定INADDR_ANY并不能把所有网卡都添加多播组中,必须明确指定对应网卡ip才可以。*/
struct in_addr addr = {0};
addr.s_addr=inet_addr("192.168.4.29"); //指定需要发送的网卡
struct ip_mreq ipmr;
ipmr.imr_interface.s_addr = addr.s_addr;
ipmr.imr_multiaddr.s_addr = inet_addr(GROUP_IP);
setsockopt(serv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(const char*)&ipmr,sizeof(ipmr));
setsockopt(serv_sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr));
//给组播地址发送数据
zubo_addr.sin_family = AF_INET;
zubo_addr.sin_port = htons(GROUP_PORT); // 端口号
zubo_addr.sin_addr.s_addr = inet_addr(GROUP_IP); // IP地址
// 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
inet_pton(AF_INET, GROUP_IP, &zubo_addr.sin_addr.s_addr);
char sendBuf[1024] = {0};
int num = 0;
int ret = 0;
while (1)
{
memset(sendBuf,0,sizeof(sendBuf));
sprintf(sendBuf, "###### hello, world! ######%d\n", num++);
// 往组播地址发送数据
ret = sendto(serv_sock, sendBuf, strlen(sendBuf)+1, 0, (struct sockaddr*)&zubo_addr, sizeof(struct sockaddr_in));
printf("发送的组播的数据: %s\n", sendBuf);
memset(sendBuf,0,sizeof(sendBuf));
sleep(2);
}
close(serv_sock);
return 0;
}
实现效果
执行 recvUDP接收 程序
执行sendUDP从千兆网卡发出去,下图为PC2(192.168.4.29)linux系统接收到kylin发送过来的数据
执行 sendUDP从万兆网卡发送出去,下图为PC2(192.168.1.98)windows系统UDP测试工具接收到的数据