广播地址: 主机号最大的地址;
以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
● 前面介绍的数据包发送方式只有一个接受方,称为单播
● 如果同时发给局域网中的所有主机,称为广播
(同一局域网内的主机都会接收到,如果其他主机没有加入广播站,就会将消息丢弃)
● 只有用户数据报(使用UDP协议)套接字才能广播
● 一般被设计为局域网搜索协议
setsockopt 设置套接字的属性
头文件:
#include
#include
#include
原型: int setsockopt(int sockfd,int level,int optname,\
void *optval,socklen_t optlen)
功能: 获得/设置套接字属性
参数:
sockfd:套接字描述符
level:协议层
optname:选项名
optval:选项值
optlen:选项值大小
返回值: 成功 0 失败-1
选项名称 |
说明 |
数据类型 |
==== SOL_SOCKET 应用层 ==== |
||
SO_BROADCAST |
允许发送广播数据 |
int |
SO_DEBUG |
允许调试 |
int |
SO_DONTROUTE |
不查找路由 |
int |
SO_ERROR |
获得套接字错误 |
int |
SO_KEEPALIVE |
保持连接 |
int |
SO_LINGER |
延迟关闭连接 |
struct linger |
SO_OOBINLINE |
带外数据放入正常数据流 |
int |
SO_RCVBUF |
接收缓冲区大小 |
int |
SO_SNDBUF |
发送缓冲区大小 |
int |
SO_RCVLOWAT |
接收缓冲区下限 |
int |
SO_SNDLOWAT |
发送缓冲区下限 |
int |
SO_RCVTIMEO |
接收超时 |
struct timeval |
SO_SNDTIMEO |
发送超时 |
struct timeval |
SO_REUSEADDR |
允许重用本地地址和端口 |
int |
SO_TYPE |
获得套接字类型 |
int |
SO_BSDCOMPAT |
与BSD系统兼容 |
int |
==== IPPROTO_IP IP层/网络层 ==== |
||
IP_HDRINCL |
在数据包中包含IP首部 |
int |
IP_OPTINOS |
IP首部选项 |
int |
IP_TOS |
服务类型 |
int |
IP_TTL |
生存时间 |
int |
IP_ADD_MEMBERSHIP |
将指定的IP加入多播组 |
struct ip_mreq |
==== IPPRO_TCP 传输层 ==== |
||
TCP_MAXSEG |
TCP最大数据段的大小 |
int |
TCP_NODELAY |
不使用Nagle算法 |
int |
端口与地址复用:
int opt = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
开通广播权限:
int optval =1;
setsockopt(sockfd,SOL_SOCKKET,SO_BROADCAST,&optval,sizeof(optval));
1. 创建用户数据报套接字;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2.setsockopt可以设置套接字属性,先设定该套接字允许发送广播
int optval = 1;
// SOL_SOCKET 传输层 SO_BROADCAST 允许发送广播
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeo(optval));
3. 接收方地址指定为广播地址,指定端口号
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
4. 发送数据包
sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&addr,sizeof(addr));
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc != 3)
{
fprintf(stderr, "用法: %s <广播地址> <端口>\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket错误");
return -1;
}
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
struct sockaddr_in broadcast_addr;
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_port = htons(atoi(argv[2]));
broadcast_addr.sin_addr.s_addr = inet_addr(argv[1]);
char buf[128] = "";
while (1)
{
sendto(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr));
printf("广播消息已发送。\n");
sleep(2); // 根据需要调整延迟
}
close(sockfd);
return 0;
}
(基本无需改动)
1. 创建用户数据报套接字
sockfd = socket(AF_INET,SOCK_DGRAM,0);
2. 绑定IP地址(广播IP或0.0.0.0)和端口
struct sockaddr_in saddr,caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
//广播地址或0.0.0.0
//0.0.0.0 是一个特殊的IP地址,用于表示服务器端将监听所有可用的网络接口
// 而不仅仅是IP地址,广播地址也会监听。
socklen_t len = sizeof(caddr);
bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
3. 等待接收数据
recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&caddr,&len);
//填充结构体
struct sockaddr_in saddr,caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
//接收者需要加入广播站
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
int len = sizeof(caddr);
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc != 2)
{
fprintf(stderr, "用法: %s <端口>\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket错误");
return -1;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(atoi(argv[1]));
local_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
{
perror("bind错误");
return -1;
}
char buf[128];
struct sockaddr_in sender_addr;
socklen_t sender_len = sizeof(sender_addr);
while (1)
{
int recv_bytes = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&sender_addr, &sender_len);
if (recv_bytes < 0)
{
perror("接收错误");
return -1;
}
buf[recv_bytes] = '\0';
printf("来自IP: %s 端口: %d 消息: %s\n", inet_ntoa(sender_addr.sin_addr),
ntohs(sender_addr.sin_port), buf);
}
close(sockfd);
return 0;
}
广播方式发给所有的主机,过多的广播会大量的占用网络带宽,造成广播风暴,影响正常的通信
IP的二级划分中 D类IP:
第一字节的前四位固定为 1110
D类IP : 224.0.0.1 - 239.255.255.255
1. 创建用户数据报套接字 UDP
2. 加入多播组 : 接收者加入多播组
3. 绑定组播IP地址(绑定组播IP或0.0.0.0)和端口
4. 等待接收数据
//多播结构体
struct ip_mreq{
struct in_addr imr_multiaddr; //指定多播组IP
struct in_addr imr_interface; //本地IP,通常指定为 INADDR_ANY--0.0.0.0
}
struct in_addr{
_be32 s_addr; //IP地址(大端)
}
//核心代码 ------------------------------------
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(argv[1]); //填充多播组
mreq.imr_interface.s_addr = inet_addr("0.0.0.0"); //自动获取本机IP
//改变套接字属性
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq));
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc != 3)
{
fprintf(stderr, "用法: %s <组播地址> <端口>\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket错误");
return -1;
}
struct sockaddr_in multicast_addr;
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_port = htons(atoi(argv[2]));
multicast_addr.sin_addr.s_addr = inet_addr(arg v[1]);
// 将套接字加入到组播组
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
mreq.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
perror("setsockopt错误");
return -1;
}
char buf[128];
struct sockaddr_in sender_addr;
socklen_t sender_len = sizeof(sender_addr);
while (1)
{
int recv_bytes = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&sender_addr, &sender_len);
if (recv_bytes < 0)
{
perror("接收错误");
return -1;
}
buf[recv_bytes] = '\0';
printf("来自IP: %s 端口: %d 消息: %s\n", inet_ntoa(sender_addr.sin_addr),
ntohs(sender_addr.sin_port), buf);
}
close(sockfd);
return 0;
}
1. 创建用户数据报套接字
2. 指定接收方地址指定为组播地址(224.0.0.1 - 239.255.255.255都可以)
指定接收端端口信息
3. 发送数据包
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc != 4)
{
fprintf(stderr, "用法: %s <组播地址> <端口> <消息>\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket错误");
return -1;
}
struct sockaddr_in multicast_addr;
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_port = htons(atoi(argv[2]));
multicast_addr.sin_addr.s_addr = inet_addr(argv[1]);
char buf[128];
snprintf(buf, sizeof(buf), "%s", argv[3]);
while (1)
{
sendto(sockfd, buf, strlen(buf), 0,
(struct sockaddr *)&multicast_addr, sizeof(multicast_addr));
printf("消息已发送。\n");
sleep(2); // 根据需要调整延迟
}
close(sockfd);
return 0;
}
● 单播方式只能发给一个接收方。
● 广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响通信。
● 组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
● 组播方式既可以发给多个主机,又能避免像广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)