Linux网络编程之广播

1.概念

前面介绍的TCP,UDP都是单播方式,即一对一.而广播是一台主机向局域网内的所有主机发送数据。这时,同一网段的所有主机都能接收到数据。发送广播包的步骤大致如下:

(1)确定一个发送广播的接口,如eth0

(2)确定广播的地址,通过ioctl函数,请求码设置为SIOCGIFBRDADDR得到广播的地址

(3)使用这个广播地址进行广播

由于TCP协议是端到端的协议,在通信之前,必须建立连接,三次握手之后才能发送数据。而广播是一对多的通信,所以TCP不支持广播。在局域网内,广播通常用来探测服务器。

2. 探测服务器实例

这个例子通过在局域网内发送广播包,收到广播包的服务器,应答主机。这样,就能够探测到局域网内的服务器。

主机:
 
 /**
客户端实现广播
**/
 #define IP_FOUND "IP_FOUND"
 #define IP_FOUND_ACK "IP_FOUND_ACK"
 #define IFNAME "eth0" 
 #define MCAST_PORT 9999
 int main(int argc,char*argv[]){
 int ret=-1;
 int sock=-1;
 int so_broadcast=1;
 struct ifreq ifr;
 struct sockaddr_in broadcast_addr;//广播地址
struct sockaddr_in from_addr;//服务端地址
int from_len=sizeof(from_addr);
 int count=-1;
 fd_set readfd;//读文件描述符集合
char buffer[1024];
 struct timeval timeout;
 timeout.tv_sec=2;//超时时间为2秒
timeout.tv_usec=0;
 sock=socket(AF_INET,SOCK_DGRAM,0);//建立数据报套接字
if(sock<0){
   printf("HandleIPFound:sock init error\n");
   return;
 }
 //将使用的网络接口名字复制到ifr.ifr_name中,由于不同的网卡接口的广播地址是不一样的,因此指定网卡接口
strncpy(ifr.ifr_name,IFNAME,strlen(IFNAME));
 //发送命令,获得网络接口的广播地址
if(ioctl(sock,SIOCGIFBRDADDR,&ifr)==-1){
     perror("ioctl error");
     return;
 }
 //将获得的广播地址复制到broadcast_addr
 memcpy(&broadcast_addr,&ifr.ifr_broadaddr,sizeof(struct sockaddr_in));
 //设置广播端口号
printf("broadcast IP is:%s\n",inet_ntoa(broadcast_addr.sin_addr));
 broadcast_addr.sin_family=AF_INET;
 broadcast_addr.sin_port=htons(MCAST_PORT);
 //默认的套接字描述符sock是不支持广播,必须设置套接字描述符以支持广播
ret=setsockopt(sock,SOL_SOCKET,SO_BROADCAST,&so_broadcast,sizeof(so_broadcast));
 //发送多次广播,看网络上是否有服务器存在
int times=10;
 int i=0;
 for(i=0;i   //广播发送服务器地址请求
    timeout.tv_sec=2;//超时时间为2秒
        timeout.tv_usec=0;
     ret=sendto(sock,IP_FOUND,strlen(IP_FOUND),0,(struct sockaddr*)&broadcast_addr,sizeof(broadcast_addr));
     if(ret==-1){
         continue;
     }

 //文件描述符清0
 FD_ZERO(&readfd);
 //将套接字文件描述符加入到文件描述符集合中
FD_SET(sock,&readfd);
 //select侦听是否有数据到来
ret=select(sock+1,&readfd,NULL,NULL,&timeout);
 switch(ret){
  case -1:
     break;
  case 0:
     printf("timeout\n");
     break;
  default:
 //接收到数据
 if(FD_ISSET(sock,&readfd)){
     count=recvfrom(sock,buffer,1024,0,(struct sockaddr*)&from_addr,&from_len);//from_addr为服务器端地址
    printf("recvmsg is %s\n",buffer);
     if(strstr(buffer,IP_FOUND_ACK)){
         printf("found server IP is:%s\n",inet_ntoa(from_addr.sin_addr));
         //服务器端的发送端口号
        printf("Server Port:%d\n",htons(from_addr.sin_port));
     }
   return;   
 }
  break;
 }
 }
 return;
 }




服务器:
 
 /**
广播服务器端代码
**/
 #define IP_FOUND "IP_FOUND"
 #define IP_FOUND_ACK "IP_FOUND_ACK"
 #define PORT 9999
 int main(int argc,char*argv[]){
  int ret=-1;
  int sock;
  struct sockaddr_in server_addr;//服务器端地址
 struct sockaddr_in from_addr;//客户端地址
 int from_len=sizeof(struct sockaddr_in);
  int count=-1;
  fd_set readfd;//读文件描述符集合
 char buffer[1024];
  struct timeval timeout;
  timeout.tv_sec=2;
  timeout.tv_usec=0;
  sock=socket(AF_INET,SOCK_DGRAM,0);//建立数据报套接字
 if(sock<0){
     perror("sock error");
     return;
 }

 memset((void*)&server_addr,0,sizeof(struct sockaddr_in));
 server_addr.sin_family=AF_INET;
 server_addr.sin_addr.s_addr=htons(INADDR_ANY);
 server_addr.sin_port=htons(PORT);
 //将地址结构绑定到套接字上./
ret=bind(sock,(struct sockaddr*)&server_addr,sizeof(server_addr));
 if(ret<0){
     perror("bind error");
     return;
 }

 while(1){
 timeout.tv_sec=2;
 timeout.tv_usec=0;
 //文件描述符集合清0
 FD_ZERO(&readfd);
 //将套接字描述符加入到文件描述符集合
FD_SET(sock,&readfd);
 //select侦听是否有数据到来
ret=select(sock+1,&readfd,NULL,NULL,&timeout);//侦听是否可读
printf("ret=%d\n",ret);
 switch(ret){
 case -1://发生错误
break;
 case 0://超时
printf("timeout\n");
 break;
 default:
 if(FD_ISSET(sock,&readfd)){
     count=recvfrom(sock,buffer,1024,0,(struct sockaddr*)&from_addr,&from_len);//接收客户端发送的数据
    //from_addr保存客户端的地址结构
    if(strstr(buffer,IP_FOUND)){
         //响应客户端请求
        //打印客户端的IP地址
            printf("Client IP is%s\n",inet_ntoa(from_addr.sin_addr));
         //打印客户端的端口号
        printf("Client Send Port:%d\n",ntohs(from_addr.sin_port));
         memcpy(buffer,IP_FOUND_ACK,strlen(IP_FOUND_ACK)+1);
         count=sendto(sock,buffer,strlen(buffer),0,(struct sockaddr*)&from_addr,from_len);//将数据发送给客户端
    }
  return;
 }
 break;
 }
 }
 return;
 }

说明: 由于默认的套接字是不支持广播的,所以必须设置套接字选项(setsockopt)来支持广播。接口的广播地址通过ioctl函数得到。广播是基于UDP协议的。MAC地址是FF:FF:FF:FF:FF:FF.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
linux C语言 网络编程教程及源码 一、网络应用层编程 1、Linux网络编程01——网络协议入门 2、Linux网络编程02——无连接和面向连接的区别 3、Linux网络编程03——字节序和地址转换 4、Linux网络编程04——套接字 5、Linux网络编程05——C/S与B/S架构的区别 6、Linux网络编程06——UDP协议编程 7、Linux网络编程07——广播 8、Linux网络编程08——多播 9、Linux网络编程09——TCP编程之客户端 10、Linux网络编程10——TCP编程之服务器 11、Linux网络编程11——tcp、udp迭代服务器 12、Linux网络编程12——tcp三次握手、四次挥手 13、Linux网络编程13——connect()、listen()和accept()三者之间的关系 14、Linux网络编程14——I/O复用之select详解 15、Linux网络编程15——I/O复用之poll详解 16、Linux网络编程16——I/O复用之epoll详解 17、Linux网络编程17——tcp并发服务器(多进程) 18、Linux网络编程18——tcp并发服务器(多线程) 19、Linux网络编程——tcp高效并发服务器(select实现) 20、Linux网络编程——tcp高效并发服务器(poll实现) 21、Linux网络编程——tcp高效并发服务器(epoll实现) 二、网络底层编程(黑客模式) 1、Linux网络编程1——啥叫原始套接字 2、Linux网络编程2——原始套接字编程 3、Linux网络编程3——原始套接字实例:MAC头分析 4、Linux网络编程4——原始套接字实例:MAC地址扫描器 5、Linux网络编程5——IP数据报格式详解 6、Linux网络编程6——TCP、UDP数据包格式详解 7、Linux网络编程7——原始套接字实例:发送UDP数据包 8、Linux网络编程8——libpcap详解 9、Linux网络编程9——libnet详解

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值