文章目录
1,广播
- 前面介绍的数据包发送方式只有一个接受方,称为单播
- 如果同时发给局域网中的所有主机,称为广播
- 只有用户数据报(使用UDP协议)套接字才能广播
- 广播地址
·以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
·发到该地址的数据包被所有的主机接收
·255.255.255.255在所有网段中都代表广播地址
1.1,广播收发
广播发送 | 广播接受 |
---|---|
创建用户数据报套接字fd = socket(AF_INET,SOCK_DGRAM,0) 缺省创建的套接字不允许广播数据包,需要设置属性 ---setsockopt可以设置套接字属性 接收方地址指定为广播地址 指定端口信息 发送数据包 |
创建用户数据报套接字 绑定本机IP地址和端口 ---绑定的端口必须和发送方指定的端口相同 等待接收数据 |
1.2,广播收发—示例
1.2.1,net.h
#ifndef __NET_H__
#define __NET_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define SERV_PORT 5005
#define QUIT_STR "quite"
#endif
1.2.2,sender.c
/* boardcast demo */
/*./sender receiver_ip receiver_port */
#include "net.h"
void usage(char *s)
{
printf("this is a boardcast demo!\n");
printf("Usage:\n\t%s <receiver_ip> <receiver_port>\n",s);
printf("\treceiver_ip: boardcast_receiver ip address\n");
printf("\treceiver_port: boardcast_receiver port(>5000)\n ");
}
int main(int argc, const char *argv[])
{
int fd;
short port;
struct sockaddr_in sin;
if(argc != 3)
{
usage((char *)argv[0]);
exit(1);
}
if((port = atoi(argv[2])) < 5000)
{
usage((char *)argv[0]);
exit(1);
}
/* 1 创建socket fd(创建用户数据报套接字) */
if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程
{
perror("socket");
exit(-1);
}
/*设置允许广播*/
int b_br =1;
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&b_br,sizeof(int));
/* 2 连接服务器 */
/* 2.1 填充struct sockaddr_in结构体变量*/
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);//转为网络字节序端口号
#if 0
#else
if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
{
perror("inet_pton");
goto _error1;
}
#endif
printf("boardcast sender starting ...... OK!\n");
/* 3 读写*/
char buf[BUFSIZ];
while(1)
{
fprintf(stderr,"Please input the string to boardcast receiver:");
bzero(buf,BUFSIZ);
if(fgets(buf,BUFSIZ-1,stdin) == NULL)
{
perror("fgets");
continue;
}
sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin));
if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
{
printf("boardcast sender is existed!\n");
break;
}
}
_error1:
close(fd);
return 0;
}
1.2.3,receiver.c
#include "net.h"
int main(int argc, const char *argv[])
{
int fd;
struct sockaddr_in sin;
/* 1 创建socket fd */
if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程,套接字类型为SOCK_DGRAM
{
perror("socket");
exit(-1);
}
/* 2 允许绑定地址快速重用 */
int b_reuse = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
/* 3 绑定 */
/* 3.1 填充struct sockaddr_in 结构体变量*/
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
#if 1
/* 让服务器可以绑定在任意的IP上*/
sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
{
perror("inet_pton");
goto _error1;
}
#endif
/* 3.2 绑定*/
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
{
perror("bind");
goto _error1;
}
printf("boardcast receiver starting .... OK!\n");
char buf[BUFSIZ];
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1)
{
/* 让系统自动获取客户端的信息*/
bzero(buf,BUFSIZ);
if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0)
{
perror("recvfrom");
continue ;
}
char cin_ipv4_addr[16];
if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0)
{
perror("inet_ntop");
goto _error1;
}
printf("Receive broadcast Data: %s",buf);
if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
{
printf("boardcast sender (%s:%d) is existing!\n",cin_ipv4_addr,ntohs(cin.sin_port));
}
}
_error1:
close(fd);
return 0;
}
1.2.4,运行结果
接收端
linux@linux:~/test/network/network_broadcast$ ./receiver
boardcast receiver starting .... OK!
Receive broadcast Data: 1
Receive broadcast Data: 2
Receive broadcast Data: 3
Receive broadcast Data: 4
Receive broadcast Data: quite
boardcast sender (192.168.1.100:34256) is existing!
发送端
linux@linux:~/test/network/network_broadcast$ ./sender 192.168.1.255 5005
boardcast sender starting ...... OK!
Please input the string to boardcast receiver:1
Please input the string to boardcast receiver:2
Please input the string to boardcast receiver:3
Please input the string to boardcast receiver:4
Please input the string to boardcast receiver:quite
boardcast sender is existed!
2,组播(多播)
- 单播方式只能发给一个接收方。
- 广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
- 组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
- 多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
2.1,网络地址
- A类地址
第1字节为网络地址,其他3个字节为主机地址。第1字节的最高位固定为0
1.0.0.1 – 126.255.255.255 - B类地址
第1字节和第2字节是网络地址,其他2个字节是主机地址。第1字节的前两位固定为10
128.0.0.1 – 191.255.255.255 - C类地址
前3个字节是网络地址,最后1个字节是主机地址。第1字节的前3位固定为110
192.0.0.1 – 223.255.255.255 - D类地址(组播地址)
不分网络地址和主机地址,第1字节的前4位固定为1110
224.0.0.1 – 239.255.255.255
组播地址:224.0.0.1 – 239.255.255.255(中间除掉广播地址)
2.2,组播收发
组播发送 | 组播接受 |
---|---|
创建用户数据报套接字fd = socket(AF_INET,SOCK_DGRAM,0) 接收方地址指定为组播地址 指定端口信息 发送数据包 |
创建用户数据报套接字 加入多播组 绑定本机IP地址和端口 ---绑定的端口必须和发送方指定的端口相同 等待接收数据 |
2.2.1,加入多播组
struct ip_mreq
{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(“235.10.10.3”);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
2.3,组播收发 — 示例
2.3.1,net.h
#ifndef __NET_H__
#define __NET_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define MULTICAST_IP "235.10.10.3"
#define SERV_PORT 5003
//#define BACKLOG 5
#define QUIT_STR "quite"
#endif
2.3.2,sender.c
/* multicast demo */
/*./sender receiver_ip receiver_port */
#include "net.h"
void usage(char *s)
{
printf("this is a multicast demo!\n");
printf("Usage:\n\t%s <receiver_ip> <receiver_port>\n",s);
printf("\treceiver_ip: multicast_receiver ip address(between 224~239 segment)\n");
printf("\treceiver_port: multicast_receiver port(>5000)\n ");
}
int main(int argc, const char *argv[])
{
int fd;
short port;
struct sockaddr_in sin;
if(argc != 3)
{
usage((char *)argv[0]);
exit(1);
}
if((port = atoi(argv[2])) < 5000)
{
usage((char *)argv[0]);
exit(1);
}
/* 1 创建socket fd(创建用户数据报套接字) */
if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程
{
perror("socket");
exit(-1);
}
/* 2 连接服务器 */
/* 2.1 填充struct sockaddr_in结构体变量*/
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);//转为网络字节序端口号
#if 0
#else
if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
{
perror("inet_pton");
goto _error1;
}
#endif
printf("multicast sender starting ...... OK!\n");
/* 3 读写*/
char buf[BUFSIZ];
while(1)
{
fprintf(stderr,"Please input the string to boardcast receiver:");
bzero(buf,BUFSIZ);
if(fgets(buf,BUFSIZ-1,stdin) == NULL)
{
perror("fgets");
continue;
}
sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin));
if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
{
printf("multicast sender is existed!\n");
break;
}
}
_error1:
close(fd);
return 0;
}
2.3.3,recviver.c
#include "net.h"
int main(int argc, const char *argv[])
{
int fd;
struct sockaddr_in sin;
/* 1 创建socket fd */
if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程,套接字类型为SOCK_DGRAM
{
perror("socket");
exit(-1);
}
/* 2 允许绑定地址快速重用 */
int b_reuse = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
/* 加入多播组 */
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
/* 3 绑定 */
/* 3.1 填充struct sockaddr_in 结构体变量*/
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
#if 1
/* 让服务器可以绑定在任意的IP上*/
sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
{
perror("inet_pton");
goto _error1;
}
#endif
/* 3.2 绑定*/
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
{
perror("bind");
goto _error1;
}
printf("multicast receiver starting .... OK!\n");
char buf[BUFSIZ];
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1)
{
/* 让系统自动获取客户端的信息*/
bzero(buf,BUFSIZ);
if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0)
{
perror("recvfrom");
continue ;
}
char cin_ipv4_addr[16];
if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0)
{
perror("inet_ntop");
goto _error1;
}
printf("Receive multicast Data: %s",buf);
if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
{
printf("multicast sender (%s:%d) is existing!\n",cin_ipv4_addr,ntohs(cin.sin_port));
}
}
_error1:
close(fd);
return 0;
}
2.3.4,运行结果
接收端
linux@linux:~/test/network/network_multicast$ ./receiver
multicast receiver starting .... OK!
Receive multicast Data: 1
Receive multicast Data: 2
Receive multicast Data: 3
Receive multicast Data: 4
Receive multicast Data: 5
Receive multicast Data: 6
Receive multicast Data: quite
multicast sender (192.168.1.100:48837) is existing!
发送端
linux@linux:~/test/network/network_multicast$ ./sender 235.10.10.3 5003
multicast sender starting ...... OK!
Please input the string to boardcast receiver:1
Please input the string to boardcast receiver:2
Please input the string to boardcast receiver:3
Please input the string to boardcast receiver:4
Please input the string to boardcast receiver:5
Please input the string to boardcast receiver:6
Please input the string to boardcast receiver:quite
multicast sender is existed!