Linux网络编程之广播

简介: 1.概念 前面介绍的TCP,UDP都是单播方式,即一对一.而广播是一台主机向局域网内的所有主机发送数据。这时,同一网段的所有主机都能接收到数据。发送广播包的步骤大致如下: (1)确定一个发送广播的接口,如eth0 (2)确定广播的地址,通过ioctl函数,请求码设置为SIOCGIFBRDADDR得到广播的地址 (3)使用这个广播地址进行广播 由于TCP协议是端到端的协议,在通信之前,必须建立连接,三次握手之后才能发送数据。

1.概念

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

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

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

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

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

2. 探测服务器实例

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

主机:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**
客户端实现广播
**/
#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   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;
}


服务器:

#include
#include
#include
#include
#include
#include
#include
/**
广播服务器端代码
**/
#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     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     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.

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
1天前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
4天前
|
运维 网络协议 Linux
Docker网络_docker 网络,来看看这份超全面的《Linux运维面试题及解析》
Docker网络_docker 网络,来看看这份超全面的《Linux运维面试题及解析》
|
6天前
|
网络协议 Linux 网络架构
|
6天前
|
域名解析 网络协议 Linux
linux网络配置详解
linux网络配置详解
24 0
|
6天前
|
网络协议 Java Linux
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
【探索Linux】P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)
14 0
|
6天前
|
存储 网络协议 算法
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)
16 0
|
6天前
|
网络协议 算法 Linux
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
【探索Linux】P.27(网络编程套接字 —— UDP协议介绍 | TCP协议介绍 | UDP 和 TCP 的异同)
16 0
|
6天前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
13 0
|
6天前
|
运维 网络协议 安全
【Shell 命令集合 网络通讯 】Linux 网络抓包工具 tcpdump命令 使用指南
【Shell 命令集合 网络通讯 】Linux 网络抓包工具 tcpdump命令 使用指南
54 0