linux 组播搜寻ip,Linux环境下服务器利用组播来获取客户端IP

Linux环境下服务器利用组播来获取客户端IP

单播是两个主机之间端对端通信(比如TCP、UDP通信),而广播用于一个主机对整个局域网中所有主机的通信。单播和广播是两个极端,要么对一个主机通信,要么对局域网内所有主机通信。然而在实际情况下,比如要获取局域网内获取执行特殊任务主机IP,单播显然不适用,因为单播必须首先要知道通信两端的IP,而使用广播显得浪费资源。在这种需要对局域网下一组特定的主机进行通信的情况下,就用到了组播。 本文针对以上情况,从组播概念、组播编程和本机IP地址获取三个方面展开阐述。

什么是组播?

组播就是将网络中同一业务类型的主机进行逻辑上的分组,进行数据收发的时候,数据仅仅仅仅在同一分组内进行,没有加入分组的主机不能收发对应的数据。

说的更通俗一点,主机好比你qq中的联系人,组播组就好比你新建的讨论组,只有加入讨论组的成员才能接受发送消息。

组播地址

说起组播地址就不得不说一下IP地址的分类。IP地址共分为五类,分别是A类、B类、C类、D类和E类。五类不同的IP地址。

5类IP地址:

类别

固定最高位

网络位

第一字节范围

A

0

8

0~127

B

10

16

128~191

C

110

24

192~223

D

1110

组播地址

224~239

E

11110

预留

240~255

D类的IP地址就是组播IP地址。其中,组播IP地址又分为局部组播地址、预留组播地址和管理权限组播地址。

局部组播地址:在224.0.0.0~224.0.0.255之间,这个区间的地址视为路由协议和其他用途保留的地址,路由器并不妆发范围的IP包。

预留组播地址:在224.0.1.0~238.255.255.255之间,可用于路由协议。

管理权限组播地址:在239.0.0.0~239.255.255.255之间,类似于私有地址,不能用于网络。

获取本机IP地址

主要用到了ioctl 函数(设备控制接口)来从网卡中获取IP信息和主机信息。顾名思义,ioctl函数就是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下:

int ioctl(int fd, ind cmd, …);

其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。

ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。

获取本机IP地址和主机名的完整代码如下:

#define ETH_NAME "eth"

void getIPHost(char** iphost)

{

int sock;

struct sockaddr_in sin;

struct ifreq ifr;

sock = socket(AF_INET, SOCK_DGRAM, 0);

for(int i = 0; i < 10; i++)

{

char* ENAME = (char*)malloc(5*sizeof(char));

bzero(ENAME, 5);

sprintf(ENAME, "%s%d", ETH_NAME, i);

strncpy(ifr.ifr_name, ENAME, IFNAMSIZ);

free(ENAME);

ifr.ifr_name[IFNAMSIZ - 1] = 0;

if (ioctl(sock, SIOCGIFADDR, &ifr) >= 0)

goto HERE;

}

for(int i = 0; i < 10; i++)

{

char* WNAME = (char*)malloc(6*sizeof(char));

bzero(WNAME, 6);

sprintf(WNAME, "%s%d", WTH_NAME, i);

strncpy(ifr.ifr_name, WNAME, IFNAMSIZ);

free(WNAME);

ifr.ifr_name[IFNAMSIZ - 1] = 0;

if (ioctl(sock, SIOCGIFADDR, &ifr) >= 0)

goto HERE;

}

HERE:

memcpy(&sin, &ifr.ifr_addr, sizeof(sin));

char* hostname = (char*)malloc(256*sizeof(char));

bzero(hostname, 256);

gethostname(hostname, 256*sizeof(char));

char* ip = inet_ntoa(sin.sin_addr);

int lenhost = strlen(hostname);

int lenip = strlen(ip);

*iphost = (char*)malloc((lenhost+lenip+2)*sizeof(char));

bzero(*iphost, (lenhost+lenip+2)*sizeof(char));

sprintf(*iphost, "%s:%s", ip, hostname);

free(hostname);

}

这里要注意一下,代码运行时会报“No Such Device”错误,这是由于系统网卡名不是以“eth”开头,只需将ETH_NAME修改成本机网卡名即可。

组播编程

要进行组播编程必须遵从一定的编程框架。基本顺序如下:

接收端:

1.创建socket

2. 初始化本机地址信息

3.绑定socket和本机地址信息

4.将本机加入组播组

5.接收消息

发送端:

1.创建socket

2. 初始化多播地址

3.发送数据

组播程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的。

1.选项IP_MULTICASE_TTL

选项IP_MULTICAST_TTL允许设置超时TTL,范围为0~255之间的任何值,例如:

unsigned char ttl=255;

setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));

2.选项IP_MULTICAST_IF

选项IP_MULTICAST_IF用于设置组播的默认默认网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据。例如:

struct in_addr addr;

setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr));

参数addr是希望多播输出接口的IP地址,使用INADDR_ANY地址回送到默认接口。

默认情况下,当本机发送组播数据到某个网络接口时,在IP层,数据会回送到本地的回环接口,选项IP_MULTICAST_LOOP用于控制数据是否回送到本地的回环接口。例如:

unsigned char loop;

setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));//参数loop设置为0禁止回送,设置为1允许回送。

3.选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP

加入或者退出一个多播组。例如:

struct ip_mreq mreq;

setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

以下为详细代码:

发送端代码:

#define MCAST_PORT 8888;

#define MCAST_ADDR "224.0.0.100" /*一个局部连接多播地址,路由器不进行转发*/

#define MCAST_INTERVAL 5 /*发送间隔时间*/

int main(int argc, char*argv)

{

int s;

char *iphost;

struct sockaddr_in mcast_addr;

s = socket(AF_INET, SOCK_DGRAM, 0); /*建立套接字*/

if (s == -1)

{

perror("socket()");

return -1;

}

memset(&mcast_addr, 0, sizeof(mcast_addr));/*初始化IP多播地址为0*/

mcast_addr.sin_family = AF_INET; /*设置协议族类行为AF*/

mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);/*设置多播IP地址*/

mcast_addr.sin_port = htons(MCAST_PORT); /*设置多播端口*/

/*获取本机地址*/

getIPHost(&iphost);

/*向多播地址发送数据*/

while(1) {

int n = sendto(s, /*套接字描述符*/

iphost, /*数据*/

sizeof(iphost), /*长度*/

0,(struct sockaddr*)&mcast_addr,

sizeof(mcast_addr)) ;

if( n < 0)

{

perror("sendto()");

return -2;

}

sleep(MCAST_INTERVAL); /*等待5s*/

}

return 0;

}

接收端代码:

#define MCAST_PORT 8888;

#define MCAST_ADDR "224.0.0.100" /*一个局部连接多播地址,路由器不进行转发*/

#define MCAST_INTERVAL 5 /*发送间隔时间*/

#define BUFF_SIZE 256 /*接收缓冲区大小*/

int main(int argc, char*argv[])

{

int s; /*套接字文件描述符*/

struct sockaddr_in local_addr; /*本地地址*/

int err = -1;

s = socket(AF_INET, SOCK_DGRAM, 0); /*建立套接字*/

if (s == -1)

{

perror("socket()");

return -1;

}

/*初始化地址*/

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

local_addr.sin_family = AF_INET;

local_addr.sin_addr.s_addr = htonl(INADDR_ANY);

local_addr.sin_port = htons(MCAST_PORT);

/*绑定socket*/

err = bind(s,(struct sockaddr*)&local_addr, sizeof(local_addr)) ;

if(err < 0)

{

perror("bind()");

return -2;

}

/*设置回环许可*/

int loop = 1;

err = setsockopt(s,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop));

if(err < 0)

{

perror("setsockopt():IP_MULTICAST_LOOP");

return -3;

}

struct ip_mreq mreq; /*加入多播组*/

mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); /*多播地址*/

mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*网络接口为默认*/

/*将本机加入多播组*/

err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof

(mreq));

if (err < 0)

{

perror("setsockopt():IP_ADD_MEMBERSHIP");

return -4;

}

int times = 0;

int addr_len = 0;

char buff[BUFF_SIZE];

int n = 0;

/*循环接收多播组的消息,5次后退出*/

for(times = 0;times<5;times++)

{

addr_len = sizeof(local_addr);

memset(buff, 0, BUFF_SIZE); /*清空接收缓冲区*/

/*接收数据*/

n = recvfrom(s, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr,

&addr_len);

if( n== -1)

{

perror("recvfrom()");

}

/*打印信息*/

printf("Recv %dst message from server:%s\n", times, buff);

sleep(MCAST_INTERVAL);

}

/*退出多播组*/

err = setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof

(mreq));

close(s);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值