广播
1.定义
前面介绍的数据包发送方式只有一个接受方,称为单播。如果同时发给局域网中的所有主机,称为广播
只有用户数据报(使用UDP协议)套接字才能广播
2.广播地址
以192.168.1.x网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
3.初始化套接字注意事项
a.创建用户数据报套接字
缺省创建的套接字不允许广播数据包,需要设置属性
setsockopt可以设置套接字属性
b.接收方地址指定为广播地址
192.168.1.255 // 只能接收该网段的广播数据
0.0.0.0 // 既可以接收单播、广播、组播
4.setsockopt函数
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
头文件:<sys/socket.h>
level : 选项级别(例如SOL_SOCKET)
optname : 选项名(例如SO_BROADCAST)
optval : 存放选项值的缓冲区的地址
optlen : 缓冲区长度
返回值:成功返回 0
失败返回 -1并设置errno
组播(多播)
1.定义
广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
2.地址
D类地址(组播地址)
范围:
224.0.0.1 – 239.255.255.255
3.发送方与接收方的设置
A.发送方
1)创建用户数据报套接字
2)发送数据的时候指定接收方地址为组播地址
3)指定端口信息
4)数据收发
B.接收方
1)创建用户数据报套接字
2)加入组播
3)绑定
4)数据收发
广播例子
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
void *thread_udp_recv(void *parg)
{
int socket_fd = (int)parg;
int len,addr_len;
char recv_buf[1500];
struct sockaddr_in dest_addr;
printf("thread_udp_recv in running....\n");
while(1)
{
/* 接收前清空recvbuf */
bzero(recv_buf,sizeof recv_buf);
addr_len = sizeof dest_addr;
bzero(recv_buf,sizeof recv_buf);
len = recvfrom(socket_fd,recv_buf,sizeof recv_buf,0,(struct sockaddr *)&dest_addr,&addr_len);
if(len <= 0)
{
perror("recv fail:");
break;
}
char *p = inet_ntoa(dest_addr.sin_addr);
printf("dest_info = [ip]%s,[port]%d \n",p, ntohs (dest_addr.sin_port)) ;
}
}
int main(int argc, char **argv)
{
int socket_fd;
int rt;
int len;
struct sockaddr_in local_addr,dest_addr;
/* 创建套接字,类型为UDP协议 */
socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(socket_fd < 0)
{
perror("creaet socket for udp fail");
return -1;
}
/* 设置本地IP地址与端口属性 */
local_addr.sin_family = AF_INET; //IPv4
local_addr.sin_port = htons(16888); //接收数据端口
local_addr.sin_addr.s_addr = inet_addr("192.168.1.205" ); //设置为自己的IP地址,只接收单播数据
int on=1;
/* 设置socket允许重复使用地址与端口,SO_REUSEADDR值为2 */
setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof on);
/* 使能广播功能,SO_BROADCAST值为6 */
on=1;
setsockopt(socket_fd,SOL_SOCKET,SO_BROADCAST,&on,sizeof on);
/* 进行绑定端口与地址 */
//把套接字和自身的信息绑定在一起
len = sizeof local_addr;
rt = bind(socket_fd ,(struct sockaddr*)&local_addr, len );
if (rt == -1)
{
perror("bind failed");
return -1;
}
/* 设置本地IP地址与端口属性 */
dest_addr.sin_family = AF_INET; //IPv4
dest_addr.sin_port = htons(16888); //接收数据端口
dest_addr.sin_addr.s_addr = inet_addr("192.168.1.255" );//设置目的IP地址为广播地址 192.168.1.255代表该网段的广播地址
pthread_t tid;
/* 创建接收线程,用于处理文字信息与文件收发,并传入socket_fd到线程当中 */
pthread_create(&tid,NULL,&thread_udp_recv,(void *)socket_fd);
while(1)
{
len = sendto(socket_fd,"hello world",11,0,(struct sockaddr *)&dest_addr,sizeof dest_addr);
if(len > 0)
{
printf("sendto success,len=%d\n",len);
}
system("clear");
sleep(3);
}
return 0;
}
组播例子
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int socket_fd;
int rt;
int len;
struct sockaddr_in local_addr,dest_addr;
/* 创建套接字,类型为UDP协议 */
socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(socket_fd < 0)
{
perror("creaet socket for udp fail");
return -1;
}
/* 设置本地IP地址与端口属性 */
local_addr.sin_family = AF_INET; //IPv4
local_addr.sin_port = htons(16888); //接收数据端口
local_addr.sin_addr.s_addr = inet_addr("192.168.1.5" ); //设置为自己的IP地址,只接收单播数据
int on=1;
/* 设置socket允许重复使用地址与端口,SO_REUSEADDR值为2 */
setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof on);
/* 进行绑定端口与地址 */
//把套接字和自身的信息绑定在一起
len = sizeof local_addr;
rt = bind(socket_fd ,(struct sockaddr*)&local_addr, len );
if (rt == -1)
{
perror("bind failed");
return -1;
}
/* 设置本地IP地址与端口属性 */
dest_addr.sin_family = AF_INET; //IPv4
dest_addr.sin_port = htons(16888); //接收数据端口
dest_addr.sin_addr.s_addr = inet_addr("224.10.10.1" );//设置目的IP地址为组播地址
while(1)
{
len = sendto(socket_fd,"hello world",11,0,(struct sockaddr *)&dest_addr,sizeof dest_addr);
if(len > 0)
{
printf("sendto success,len=%d\n",len);
}
sleep(3);
}
return 0;
}
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int socket_fd;
int rt;
int len,addr_len;
char recv_buf[1024]={0};
struct sockaddr_in local_addr,dest_addr;
/* 创建套接字,类型为UDP协议 */
socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(socket_fd < 0)
{
perror("creaet socket for udp fail");
return -1;
}
struct ip_mreq mreq;
bzero(&mreq,sizeof mreq);
mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1"); //设置当前的组播地址
mreq.imr_interface.s_addr = inet_addr("0.0.0.0"); //将系统的IP对应的网卡接口加入到组播
/* 加入组播 */
setsockopt(socket_fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof mreq);
/* 设置本地IP地址与端口属性 */
local_addr.sin_family = AF_INET; //IPv4
local_addr.sin_port = htons(16888); //接收数据端口
local_addr.sin_addr.s_addr = inet_addr("0.0.0.0" );//设置当前系统能够接收单播,组播,广播信息
int on=1;
/* 设置socket允许重复使用地址与端口,SO_REUSEADDR值为2 */
setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof on);
/* 进行绑定端口与地址 */
//把套接字和自身的信息绑定在一起
len = sizeof local_addr;
rt = bind(socket_fd ,(struct sockaddr*)&local_addr, len );
if (rt == -1)
{
perror("bind failed");
return -1;
}
while(1)
{
addr_len = sizeof dest_addr;
bzero(recv_buf,sizeof recv_buf);
len = recvfrom(socket_fd,recv_buf,sizeof recv_buf,0,(struct sockaddr *)&dest_addr,&addr_len);
if(len > 0)
{
char *p = inet_ntoa(dest_addr.sin_addr);
printf("dest_info = [ip]%s,[port]%d \n",p, ntohs (dest_addr.sin_port)) ;
printf("recvfrom len =%d\n",len);
printf("recvfrom data=%s\n\n",recv_buf);
/* 将接收到信息进行重发 */
sendto(socket_fd,recv_buf,len,0,(struct sockaddr *)&dest_addr,sizeof dest_addr);
}
}
return 0;
}