【1】广播是一对多通信。
主机地址全1是该网段的广播地址。
广播只能发向同一个网段,并且该网段内的所有主机都可以接收。
由于广播是一对多通信,因此不能使用面向链接的流式套接字,只能使用无连接的数据报套接字。
要使用广播,需要了解IPv4特定的广播地址。可以用下面的命令来显示所选用的广播地址。
【2】关于setsockopt()函数
功能:
setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
#include <sys/socket.h>
int setsockopt(int s,int level,int optname,const char *optval,int optlen);
参数:
s:标识一个套接字的描述符。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区长度。
返回值:
若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
【3】广播示例–1
广播包的发送流程如下:
(1)创建UDP套接字
(2)指定目标地址和端口
(3)设置套接字选项允许发送广播包
(4)发送数据包
发送广播包代码实例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define N 64//定义数据大小
typedef struct sockaddr SA;
int main(int argc, const char *argv[])
{
int sockfd;//创建套接字
char buf[N]="This is a broadcast package\n";
int on=1;
struct sockaddr_in dstaddr;//本地地址
if(argc<3)
{
printf("Usage :<%s><ip><port>\n",argv[0]);
return -1;
}
if((sockfd=socket(PF_INET,SOCK_DGRAM,0))==-1)//建立数据包套接字
{
perror("fail to socket");
exit(-1);
}
bzero(&dstaddr,sizeof(dstaddr));//数据清零
dstaddr.sin_family=PF_INET;//协议族
dstaddr.sin_port=htons(atoi(argv[2]));//端口
dstaddr.sin_addr.s_addr=inet_addr(argv[1]);//本地地址
if(setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on))<0)//设置套接字文件描述符sock为可以进行广播
{
perror("fail to setsockopt");
exit(-1);
}
while(1)
{
sendto(sockfd,buf,N,0,(SA *)&dstaddr,sizeof(dstaddr));//发送数据包
sleep(1);
}
return 0;
}
接收广播包代码实例:
广播包接收流程:
(1)创建UDP套接字
(2)绑定地址和端口
(3)接收数据包
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define N 64//缓冲区大小
typedef struct sockaddr SA;
int main(int argc, const char *argv[])
{
int sockfd;
char buf[N];
struct sockaddr_in myaddr,
peeraddr;
socklen_t peerlen=sizeof(peeraddr);
if(argc<3)//检查命令行参数
{
printf("Usage:<%s><ip><port>\n",argv[0]);
return -1;
}
if((sockfd=socket(PF_INET,SOCK_DGRAM,0))==-1) //创建套接字文件描述
{
perror("fail to socket");
exit(-1);
}
bzero(&myaddr,sizeof(myaddr));//清零
myaddr.sin_family=PF_INET;//协议族
myaddr.sin_port=htons(atoi(argv[2]));//端口号
myaddr.sin_addr.s_addr=inet_addr(argv[1]);//本地地址
if(bind(sockfd,(SA *)&myaddr,sizeof(myaddr))<0)//绑定
{
perror("fail to bind");
exit(-1);
}
while(1)
{
recvfrom(sockfd,buf,N,0,(SA *)&peeraddr,&peerlen);//接收数据包
printf("[%s:%d]%s\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
}
return 0;
}
【4】广播示例–2
说明:服务器在局域网上倾听,当有数据到来时候,来判断数据是否有关键字IP_FOUND,当存在此关键字,发送IP_FOUND_ACK到客户端。
客户端判断是否有服务器响应IP_FOUND请求,判断接受的字符串是否包含IP_FOUND_ACK来确定局域网是否存在服务器。
客户端:
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<time.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<pthread.h>
#include<sys/ioctl.h>
#include <net/if.h>
#define BUFF_SIZE 32//缓冲去大小
#define IP_FOUND "IP_FOUND"//ip发现命令
#define IP_FOUND_ACK "IP_FOUND_ACK"//ip发现应答命令
#define IFNAME "169.254.255.255"
int main(int argc, const char *argv[])
{
char buff[BUFF_SIZE];
int ret=-1;
int sock=-1;
int broadcastfd=-1;
struct sockaddr_in broadcastfd_addr;//本地地址
struct sockaddr_in from;//服务器地址
struct ifreq ifr;
int from_len;
int count=-1;
fd_set readfd;
struct timeval timeout;//超时设置
timeout.tv_sec =2;
timeout.tv_usec =0;
sock=socket(AF_INET,SOCK_DGRAM,0);//建立套接字
if(sock<0)
{
perror("fail to socket");
exit(1);
}
memcpy(ifr.ifr_name,IFNAME,strlen(IFNAME));//将需要使用的网络接口字符复制到结构中
if(ioctl(sock,SIOCGIFBRDADDR,&ifr)==-1)//发送命令,获取网络接口广播地址
{
perror("fail to ioctl");
exit(1);
}
memcpy(&broadcastfd_addr,&ifr.ifr_broadaddr,sizeof(struct sockaddr_in));//将获取的广播地址赋值给broadcastfd_addr
broadcastfd_addr.sin_port=htons(7788);//设置广播端口号
ret=setsockopt(sock,SOL_SOCKET,SO_BROADCAST,&broadcastfd,sizeof(broadcastfd));//设置套接字文件描述符sock可以进行广播
/*主处理过程*/
int times=10;
int i=10;
for(i=0;i<times;i++)
{
ret=sendto(sock,IP_FOUND,strlen(IP_FOUND),0,(struct sockaddr*)&broadcastfd_addr,sizeof(broadcastfd_addr));//广播发送服务器处理请求
if(ret==-1)
{
continue;
}
}
FD_ZERO(&readfd);//文件描述符集合清零
FD_SET(sock,&readfd);//将套接字加入集合
ret=select(sock+1,&readfd,NULL,NULL,&timeout);//selest侦听是否有数据
switch(ret)
{
case 0: break;//超时
case -1: break;//错误
default:
if(FD_ISSET(sock,&readfd))//有数据来
{
count=recvfrom(sock,buff,BUFF_SIZE,0,(struct sockaddr*)&from,&from_len);//接收数据
printf("recv msg is %s\n",buff);
if(strstr(buff,IP_FOUND_ACK))//判断是否吻合
{
printf("found server,IP:%d\n",inet_ntoa(from.sin_addr));
}
break;//成功并退出
}
}
}
服务器端:
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<time.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<arpa/inet.h>
#include<pthread.h>
#define BUFF_SIZE 32
#define IP_FOUND "IP_FOUND"//ip发现命令
#define IP_FOUND_ACK "IP_FOUND_ACK"//ip发现应答命令
#define SERVER_ADDR "169.254.5.225"
int main(int argc, const char *argv[])
{
char buff[BUFF_SIZE];
int ret=-1;
int sock=-1;
struct sockaddr_in local;//本地地址
struct sockaddr_in from;//客户端地址
int from_len;
int count=-1
fd_set readfd;
struct timeval timeout;//超时设置
timeout.tv_sec =2;
timeout.tv_usec =0;
printf("==>HandleIPFound\n");
sock=socket(AF_INET,SOCK_DGRAM,0);//建立数据报套接字
if(sock<0)
{
perror("fail to socket");
exit(1);
}
memset((void*)&local,0,sizeof(local));//数据清零
local.sin_family=AF_INET;//协议族
local.sin_addr.s_addr=inet_addr(SERVER_ADDR );//本地地址
local.sin_port=htons(7788);//端口号
if((ret==(bind(sock,(struct sockaddr*)&local,sizeof(local))))==-1)//绑定
{
perror("fail to bind");
exit(1);
}
for(;;)
{
FD_ZERO(&readfd);//清空集合
FD_SET(sock,&readfd);//将套接字文件描述符加入集合
ret=select(sock+1,&readfd,NULL,NULL,&timeout);//select侦听是否有数据
switch(ret)
{
case 0: break;//超时
case -1: break;//错误
default:
if(FD_ISSET(sock,&readfd))
{
count=recvfrom(sock,buff,BUFF_SIZE,0,(struct sockaddr*)&from,&from_len);//接收数据
printf("recv msg is %s\n",buff);
if(strstr(buff,IP_FOUND))//判断是否吻合
{
memcpy(buff,IP_FOUND_ACK,strlen(IP_FOUND_ACK)+1);//复制数据
count=sendto(sock,buff,strlen(buff),0,(struct sockaddr*)&from,from_len);//发送给客户端
}
}
}
}
printf("<==HandleIPFound\n");
return;
}