使用UDP实现广播
概念
只有用户数据报(UDP协议)套接字才能广播
同时发给局域网中的所有主机,称为广播
具体广播地址 可以通过 ifconfig 命令 看 关键字 broadcast 后面的地址
广播的用处
ARP协议 通过 IP 地址获取对方的mac地址,就是使用广播实现的。
广播的流程
-------
发送者:|
------- 1.创建套接字 socket( )
-----------------------------------
2.设置为允许发送广播权限 setsockopt( )
-----------------------------------
3.填充广播信息结构体 sockaddr_in
4.发送数据 sendto( )
接收者:
1.创建套接字 socket( )
2.填充广播信息结构体 sockaddr_in
3.将套接字与广播信息结构体绑定 bind( )
4.接收数据 recvfrom( )
使用 setsockopt 设置允许发送广播权限
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
参数:
sockfd:套接字
level:选项的级别
SOL_SOCKET 套接字级别
optname:选项的名称
SO_BROADCAST 是否允许发送广播
optval:结构体
1 允许
0 不允许
optlen:optval的大小
setsockopt 其他用法
-
使用setsockopt设置超时检测 :多线程实现TCP并发服务器使用setsockopt设置超时检测
-
使用setsockopt设置端口复用 :多线程实现TCP并发服务器使用setsockopt设置超时检测-使用setsockopt设置端口复用
-
使用 setsockopt 设置加入多播组 : 使用UDP实现组播-使用 setsockopt 设置加入多播组
代码实现
接收端—01receive.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define ERRLOG(errmsg) \
do \
{ \
printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
perror(errmsg); \
exit(-1); \
} while (0)
//创建套接字-填充服务器网络信息结构体-绑定
int socket_bind(const char *argv[]);
int main(int argc, const char *argv[])
{
//检测命令行参数个数
if (3 != argc)
{
printf("Usage : %s <IP> <PORT>\n", argv[0]);
exit(-1);
}
//创建套接字-填充服务器网络信息结构体-绑定
int sockfd = socket_bind(argv);
//用来保存发送端信息的结构体
// UPD网络通信 如果需要给发送端回信,就必须保存发送端的网络信息结构体
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
socklen_t client_addr_len = sizeof(client_addr);
char buff[128] = {0};
while (1)
{
//阻塞接收发送端发来的数据
if (-1 == recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&client_addr, &client_addr_len))
ERRLOG("recvfrom error");
printf("客户端 (%s:%d) 发来数据:[%s]\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
}
//关闭监听套接字 一般不关闭
close(sockfd);
return 0;
}
//创建套接字-填充服务器网络信息结构体-绑定
int socket_bind(const char *argv[])
{
// 1.创建套接字 //IPV4 //UDP
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
ERRLOG("socket error");
// 2.填充广播信息结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
//端口号 填 8888 9999 6789 ...都可以
server_addr.sin_port = htons(atoi(argv[2]));
// ip地址 组播的IP地址 224.0.0.1 – 239.255.255.255
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
//结构体长度
socklen_t server_addr_len = sizeof(server_addr);
// 3.将套接字和组播信息结构体绑定
if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, server_addr_len))
ERRLOG("bind error");
return sockfd;
}
发送端—02send.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define ERRLOG(errmsg) \
do \
{ \
printf("%s--%s(%d):", __FILE__, __func__, __LINE__); \
perror(errmsg); \
exit(-1); \
} while (0)
int main(int argc, const char *argv[])
{
//检测命令行参数个数
if (3 != argc)
{
printf("Usage : %s <IP> <PORT>\n", argv[0]);
exit(-1);
}
// 1.创建套接字 //IPV4 //UDP
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
ERRLOG("socket error");
// 2.填充组播信息结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); // 清空
server_addr.sin_family = AF_INET; // IPV4
//端口号 填 8888 9999 6789 ...都可以
// atoi字符串转换成整型数
// htons将无符号2字节整型 主机-->网络
server_addr.sin_port = htons(atoi(argv[2]));
// ip地址 是广播的ip地址 192.168.xxx.255
// inet_addr字符串转换成32位的网络字节序二进制值
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
//结构体长度
socklen_t server_addr_len = sizeof(server_addr);
/*---------------------------------------------------------------------------------*/
//设置允许发送广播
int on_off = 1; //允许
// /套接字 /套接字级别 /是否允许发送广播
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on_off, sizeof(on_off)))
ERRLOG("setsockopt error");
/*---------------------------------------------------------------------------------*/
char buff[128] = {0};
while (1)
{
printf("请输入 : ");
scanf("%s", buff);
if (0 == strcmp(buff, "quit"))
break;
//将数据以广播的形式发送----发送到广播的ip地址
if (-1 == sendto(sockfd, buff,sizeof(buff), 0, (struct sockaddr *)&server_addr, server_addr_len))
ERRLOG("sendto error");
}
//关闭监听套接字 一般不关闭
close(sockfd);
return 0;
}
执行结果
注意
在 发送端 使用 setsockopt 设置允许发送广播权限
应该插放在 socket创建套接字 之后,sendto将数据以广播的形式发送之前
设置允许发送广播
int on_off = 1; //允许
/套接字 /套接字级别 /是否允许发送广播
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on_off, sizeof(on_off)))
ERRLOG("setsockopt error");