广播
广播:在一个局域网内部,所有的终端都能够收到数据包。使用广播的发送数据,会使处于同一个局域网内部的所有用户都必须接收到数据,用户不能拒绝。
注意:在使用广播和组播时,发送方和接收方都要处于同一个局域网内。因为广播和组播只能在一个局域网内部发送数据。广播和组播与一般网络通信有所不同,网络通信传输的是数据包;广播传输的数据叫广播包,组播传输的数据叫组播包。
原理: 发送方发送广播包到交换机/路由器,交换机识别到是一个广播包, 于是由交换机转发N份到局域网中的每一个终端。ip 地址=网络号 +主机号,主机号全部为1的ip地址称为广播地址。发送方将ip包的目的地址改为广播地址,路由器收到数据包发现其目的地址是广播地址,于是就将广播包转发给局域网内部的每一台电脑。
在广播中,只有发送端(send)和接收端(receive)一律使用udp协议传送数据。
编写广播程序的步骤:
- 设置允许发送;
- 将目的地址改为广播地址;
- 端口号和接收方一致。
广播的缺点: 如果有大量广播包存在于交换机/路由器中会造成网络风暴,因为交换机要将一个广播包发给局域网下的每一个终端,如果有大量广播包拥塞在网络中,就会造成网络延迟大、卡顿、网速慢等问题。所以为了解决这一问题,有了组播的产生。
广播只需要发送端进行相关设定后就可以向局域网其他终端发送数据包,其他的终端只需要打开接收数据的程序即可。
例、发送一个广播包到局域网内的其他终端上。
头文件com.h
#ifndef UDP_STRUCT_TEST_H
#define UDP_STRUCT_TEST_H
#define PORT 5549
struct mesdata
{
int temp;
int hum;
short cnt;
char des[64];
}__attribute__((packed));
#endif
发送程序send.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include "com.h"
int main(void)
{
int sock_cli;
struct sockaddr_in cliaddr;
int res;
int recv;
struct mesdata clidata;
socklen_t clilen;
int enable = 1;
sock_cli = socket(AF_INET,SOCK_DGRAM,0);
if(sock_cli < 0)
{
perror("socket()");
exit(-1);
}
//允许发送广播数据
setsockopt(sock_cli,SOL_SOCKET,SO_BROADCAST,&enable,sizeof(enable));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = PORT;
cliaddr.sin_addr.s_addr = inet_addr("192.168.3.255");
clidata.temp = 28;
clidata.hum = 64;
clidata.cnt = 0;
strcpy(clidata.des,"the imformation is");
while(1)
{
clidata.temp = htonl(clidata.temp);
clidata.hum = htonl(clidata.hum);
clidata.cnt = htons(clidata.cnt);
res = sendto(sock_cli,&clidata,sizeof(clidata),0,\
(struct sockaddr *)&cliaddr,sizeof(cliaddr));
if(res < 0)
{
perror("sendto()");
exit(-5);
}
clidata.cnt = ntohs(clidata.cnt);
clidata.cnt++;
clidata.temp = ntohl(clidata.temp);
clidata.hum = ntohl(clidata.hum);
sleep(1);
}
close(sock_cli);
exit(0);
}
接收端程序recv.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include "com.h"
int main(void)
{
int sock_serv;
struct sockaddr_in servaddr;
int res;
struct mesdata data;
struct sockaddr_in cliaddr;
int recv;
socklen_t clilen;
int enable = 1;
sock_serv = socket(AF_INET,SOCK_DGRAM,0);
if(sock_serv < 0)
{
perror("socket()");
exit(-1);
}
setsockopt(sock_serv,SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(enable));
servaddr.sin_family = AF_INET;
servaddr.sin_port = PORT;
servaddr.sin_addr.s_addr = INADDR_ANY;
res = bind(sock_serv,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(res < 0)
{
perror("bind()");
exit(-2);
}
while(1)
{
clilen = sizeof(cliaddr);
memset(&data,0,sizeof(data));
recv = recvfrom(sock_serv,&data,sizeof(data),0,\
(struct sockaddr *)&cliaddr,&clilen);
if(recv < 0)
{
perror("recvfrom()");
exit(-3);
}
else if(recv == 0)
{
printf("closeed!\n");
exit(0);
}
data.cnt = ntohs(data.cnt);
data.temp = ntohl(data.temp);
data.hum = ntohl(data.hum);
printf("[%d] %s temp=%d,hum=%d\n",\
data.cnt,data.des,data.temp,data.hum);
}
close(sock_serv);
exit(0);
}
组播
组播:在路由器中建立一个分组,感兴趣的成员加入该组,往改组发送组播包, 路由器识别到组播包,就转发给组内的每一个成员。
通过组播ip识别每一个分组。将数据包的目的ip改为该分组的ip地址,就可以将数据包发送给该组内成员。
例、发送一个组播包到局域网中的一个组。
头文件com.h
#ifndef UDP_STRUCT_TEST_H
#define UDP_STRUCT_TEST_H
#define PORT 5549
struct mesdata
{
int temp;
int hum;
short cnt;
char des[64];
}__attribute__((packed));
#endif
发送端程序send.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include "com.h"
int main(void)
{
int sock_cli;
struct sockaddr_in cliaddr;
int res;
int recv;
struct mesdata clidata;
socklen_t clilen;
int enable = 1;
sock_cli = socket(AF_INET,SOCK_DGRAM,0);
if(sock_cli < 0)
{
perror("socket()");
exit(-1);
}
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = PORT;
cliaddr.sin_addr.s_addr = inet_addr("239.7.21.12");//改为组播地址
clidata.temp = 28;
clidata.hum = 64;
clidata.cnt = 0;
strcpy(clidata.des,"the imformation is");
while(1)
{
clidata.temp = htonl(clidata.temp);
clidata.hum = htonl(clidata.hum);
clidata.cnt = htons(clidata.cnt);
res = sendto(sock_cli,&clidata,sizeof(clidata),0,\
(struct sockaddr *)&cliaddr,sizeof(cliaddr));
if(res < 0)
{
perror("sendto()");
exit(-5);
}
clidata.cnt = ntohs(clidata.cnt);
clidata.cnt++;
clidata.temp = ntohl(clidata.temp);
clidata.hum = ntohl(clidata.hum);
sleep(1);
}
close(sock_cli);
exit(0);
}
接收端程序recv.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include "com.h"
int main(void)
{
int sock_serv;
struct sockaddr_in servaddr;
int res;
struct mesdata data;
struct sockaddr_in cliaddr;
int recv;
socklen_t clilen;
int enable = 1;
struct ip_mreqn multi_castaddr;
sock_serv = socket(AF_INET,SOCK_DGRAM,0);
if(sock_serv < 0)
{
perror("socket()");
exit(-1);
}
/*允许重用IP地址和端口号*/
setsockopt(sock_serv,SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(enable));
servaddr.sin_family = AF_INET;
servaddr.sin_port = PORT;
servaddr.sin_addr.s_addr = INADDR_ANY;
res = bind(sock_serv,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(res < 0)
{
perror("bind()");
exit(-2);
}
multi_castaddr.imr_multiaddr.s_addr = inet_addr("239.7.21.12");//要加入的组播地址
multi_castaddr.imr_address.s_addr = INADDR_ANY;//本机IP地址
multi_castaddr.imr_ifindex = 0;//网卡号,ifconfig命令显示的eth0就是网卡号
setsockopt(sock_serv,IPPROTO_IP,IP_ADD_MEMBERSHIP,\
&multi_castaddr,sizeof(multi_castaddr));//加入组播
while(1)
{
clilen = sizeof(cliaddr);
memset(&data,0,sizeof(data));
recv = recvfrom(sock_serv,&data,sizeof(data),0,\
(struct sockaddr *)&cliaddr,&clilen);
if(recv < 0)
{
perror("recvfrom()");
exit(-3);
}
else if(recv == 0)
{
printf("closeed!\n");
exit(0);
}
data.cnt = ntohs(data.cnt);
data.temp = ntohl(data.temp);
data.hum = ntohl(data.hum);
printf("[%d] %s temp=%d,hum=%d\n",\
data.cnt,data.des,data.temp,data.hum);
}
close(sock_serv);
exit(0);
}