UDP的socket绑定到IP地址后无法接受广播数据

UDP的socket绑定到IP地址后无法接受广播数据

fannyth
05-11-10, 18:37
由于考虑到我的程序要在多网卡的机器上运行,所以我将网卡的ip地址绑定到了socket
server_addr.sin_addr.s_addr = inet_addr(servIP);但是这样就无法收到广播数据?同时我也绑定到了一个广播地址接受时发现没次客户端来的数据都连续接受两次;请问是为什么?

以下是我的测试程序:

//server.cpp
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>

char * host_name = "192.168.0.255";
int port = 10051;

int main(void)
{
struct sockaddr_in serv_addr,addr;
char buf[256];

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror("Error: socket";
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host_name);//htonl(INADDR_ANY);//

int bd = bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
while(1)
{
int len = sizeof(serv_addr);
memset(&serv_addr, 0, len);
int ret = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&serv_addr,(socklen_t*)&len);
printf("recv=%d buf=%s
",ret, buf);
printf("RecvieIP:%s Recive Port:%d
", inet_ntoa(serv_addr.sin_addr),
ntohs(serv_addr.sin_port));
ret = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in));
}

}


//client.cpp

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>

char * host_name = "192.168.0.255";

int port = 10051;
int main(void)
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror("Error: socket";
return -1;
}

struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(host_name);
int so_broadcast;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast,
sizeof(so_broadcast));

char buf[256]="client .............";
sockaddr from ;
int len;
sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in));
while(1)
{
recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&serv_addr,(socklen_t*)&len);
printf("server buf= %s
",buf);
}
}
haohao_h
05-11-10, 21:12
你给你的server帮定一个广播地址叫它怎么接受数据!
fannyth
05-11-10, 21:26
你给你的server帮定一个广播地址叫它怎么接受数据!

因为我要接受广播数据如果绑定道指定的ip地址,就无法收到客户端发来的数据,我现在绑定广播地址后每次都要收到两次相同的数据.我的两个ip地址分别是192.168.0.2 和196.168.0.172.
haohao_h
05-11-11, 08:42
因为我要接受广播数据如果绑定道指定的ip地址,就无法收到客户端发来的数据,我现在绑定广播地址后每次都要收到两次相同的数据.我的两个ip地址分别是192.168.0.2 和196.168.0.172.
推荐你看一本书,richard stevens的unix 网络编程卷一
x11
05-11-11, 11:10
双网卡最好不要设成一个网段

你两块网卡一个网段,结果两个网卡都收到广播数据
fannyth
05-11-11, 11:20
双网卡最好不要设成一个网段

你两块网卡一个网段,结果两个网卡都收到广播数据

是的,但是程序给别人用的时候很难保证别人机器的双网卡是否在一个网段内。我想知道的是如果将socket绑定到指定的地址后是否就无法接受广播包了。
x11
05-11-11, 12:01
应该不会
int so_broadcast =1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast));[U]
fannyth
05-11-11, 12:15
应该不会
int so_broadcast =1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast));[U]

我试过了,我在接受端将socket绑定到了一个ip地址,同时在接受端我也加了这句,但还是收不到广播包
x11
05-11-11, 12:36
嗯,bind到网卡地址是不能接收广播包的

那你就别bind了呗
Westwind
05-11-11, 12:48
嗯,bind到网卡地址是不能接收广播包的

那你就别bind了呗

udp广播的递送规则是:
如果没有设置BLOADCASE选项的不递送。
如果bind端口不匹配不递送该套接口
如果绑定的不是INADDR_ANY话
那么必须BIND的地址和目的地址匹配才能递送:
也就是说你必须BIND一个广播地址或者绑定INADDR_ANY
第三
如果你的udp调用了connect
那么源地址和源端口不匹配也不递送
否则递送

你可以设置其中一个网卡DISABLE广播能力
fannyth
05-11-11, 13:36
看来我要重新考虑解决我问题的思路;我把问题再说一遍,大家看看有什么好的思路来解决。

由于考虑到我的程序要在多网卡的机器上运行,所以我希望socket既能接受广播的数据又能接受单播的数据。同时在接受广播数据时只接受一次。因为服务器是多网卡的;而我的客户端在启动时还不知道自己的ip地址;所以客户端发送的广播地址是255.255.255.255;所以我想是否可以用两个soket一个用来接受广播,同时自动进行包过虑。另一个用来接受单播数据?不知这样可以吗?会不会有什么影响?
Westwind
05-11-11, 14:04
看来我要重新考虑解决我问题的思路;我把问题再说一遍,大家看看有什么好的思路来解决。

由于考虑到我的程序要在多网卡的机器上运行,所以我希望socket既能接受广播的数据又能接受单播的数据。同时在接受广播数据时只接受一次。因为服务器是多网卡的;而我的客户端在启动时还不知道自己的ip地址;所以客户端发送的广播地址是255.255.255.255;所以我想是否可以用两个soket一个用来接受广播,同时自动进行包过虑。另一个用来接受单播数据?不知这样可以吗?会不会有什么影响?

你可以这样做。

2个udp socket
单播的那个socket bind INADDR_ANY并且设置SO_BROADCAST选项为0,此时他只接受单播数据报

广播那个socket bind 255.255.255.255,并且设置SO_BROADCASE选项为1,此时他只接受UDP广播。

为了防止接收到同一个广播的2分copy,如果两个网卡位于同一个子网,那么就用ifconfig命令disable其中一块网卡的BROADCAST标志,让其不能接受以太网广播。

你也可以使用ioctl的SIOCSIFFLAGS方法去掉一个接口的标志IFF_BROADCAST,使之不能接受以太网广播
fannyth
05-11-11, 16:54
你可以这样做。

2个udp socket
单播的那个socket bind INADDR_ANY并且设置SO_BROADCAST选项为0,此时他只接受单播数据报

广播那个socket bind 255.255.255.255,并且设置SO_BROADCASE选项为1,此时他只接受UDP广播。

为了防止接收到同一个广播的2分copy,如果两个网卡位于同一个子网,那么就用ifconfig命令disable其中一块网卡的BROADCAST标志,让其不能接受以太网广播。

你也可以使用ioctl的SIOCSIFFLAGS方法去掉一个接口的标志IFF_BROADCAST,使之不能接受以太网广播


谢谢,你说的很详细了,我可以试试;但我想如果设置另一块网卡不能接受广播数据,是否会对其它程序有影响?所以我想能自己在接受广播数据的同时做一个检测,检测收到的数据是否是指定网卡接受到的如果是则处理,否则丢弃;但不知道如何检测?同时对效率的影响有多大?
Westwind
05-11-12, 00:27
谢谢,你说的很详细了,我可以试试;但我想如果设置另一块网卡不能接受广播数据,是否会对其它程序有影响?所以我想能自己在接受广播数据的同时做一个检测,检测收到的数据是否是指定网卡接受到的如果是则处理,否则丢弃;但不知道如何检测?同时对效率的影响有多大?

这个也有可能,至少存在一个socket api可以返回数据报的目的地址,
recvmsg
recvmsg是非SVR4系统的recv,recvfrom,readv的底层实现,在recvmsg之下就是syscall了。
不过这个函数挺复杂的,你需要学习该函数的用法,并且该函数是不可移植的,至少windows不提供该方法。

你可能感兴趣的:(UDP的socket绑定到IP地址后无法接受广播数据)