[C++项目] Web Server(7):UDP通信
UDP通信
数据传输
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
- sockfd : 通信的fd
- buf : 要发送的数据
- len : 发送数据的长度
- flags: 0
- dest_addr : 通信的另外一端的地址信息
- addrlen : 地址的内存大小
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) ;
参数:
- sockfd : 通信的fd
- buf : 接收数据的数组
- len : 数组的大小
- flags : 0
- src_addr : 传出参数,用来保存另外一端的地址信息,可以用于判断是否是指定的IP传来的信息,不需要可以指定为NULL
- addrlen : 地址的内存大小
客户端代码
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
//创建UDP套接字
int fd = socket(PF_INET,SOCK_DGRAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(8888);
inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr.s_addr);
saddr.sin_family = AF_INET;
int len = sizeof(saddr);
int i = 0;
while (1) {
char buf[1024] = {0}, rebuf[1024] = {0};
sprintf(buf,"this is %d",i++);
// 发送数据
sendto(fd,buf,strlen(buf)+1,0,(struct sockaddr*)&saddr,len);
//接受数据
recvfrom(fd,rebuf,sizeof(rebuf),0,(struct sockaddr*)&saddr, &len);
printf("服务器数据:%s\n",rebuf);
sleep(1);
}
close(fd);
}
服务器端代码
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
//创建UDP套接字
int fd = socket(PF_INET,SOCK_DGRAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_family = AF_INET;
bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
while (1) {
char buf[1024] = {0};
struct sockaddr_in caddr;
int len = sizeof(caddr);
// 接受数据
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&caddr, &len);
char address[20];
printf("客户端IP:%s, 端口:%d\n",inet_ntop(AF_INET, &caddr.sin_addr.s_addr,address,sizeof(address)),ntohs(caddr.sin_port));
printf("接受数据:%s\n",buf);
// 发送数据
sendto(fd,buf,strlen(buf)+1,0,(struct sockaddr*)&caddr,len);
}
close(fd);
}
注意 UDP通信不需要额外的操作 比如多线程或者epoll也可以接受多个客户端
广播
广播是一种将消息发送给网络中的所有主机的通信方式。它通常使用 UDP(用户数据报协议)来实现,因为 UDP 是一种无连接的协议,可以将数据包发送到广播地址,从而达到广播的效果。
在 TCP(传输控制协议)通信中,没有直接支持广播的机制。TCP 是一种面向连接的协议,它通过建立可靠的点对点连接来进行通信。每个 TCP 连接都是一个独立的双向通信通道,只有发送方和接收方之间可以进行通信。这种连接的特性使得 TCP 无法直接实现广播。
如果需要在 TCP 网络中实现广播功能,可以使用其他的机制,例如在应用层上实现自定义的广播协议,或者使用多播(Multicast)来实现类似的功能。多播是一种将数据包发送给一组特定主机的通信方式,可以在 UDP 或 IP 层级上使用。
在UDP通信中,可以通过设置套接字属性来使用广播。
//设置广播属性的函数
int setsockopt(int sockfd, int level, int optname, const void *optva1, socklen_t optlen) ;
- sockfd : 文件描述符
- level : SOL_SOCKET
- optname : SO_BROADCAST
- optval : int类型的值,为1表示允许广播
- optlen : optval的大小
更详细的对socket属性的设置可以参照[C++项目] Web Server(4):TCP 通信流程与端口复用中的说明
广播的客户端的代码
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
//创建UDP套接字
int fd = socket(PF_INET,SOCK_DGRAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_family = AF_INET;
bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
int len = sizeof(saddr);
while (1) {
char rebuf[1024] = {0};
//接受数据
recvfrom(fd,rebuf,sizeof(rebuf), 0, NULL, NULL);
printf("服务器数据:%s\n",rebuf);
}
close(fd);
}
广播的服务器端的代码
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
//创建UDP套接字
int fd = socket(PF_INET,SOCK_DGRAM,0);
// 设置广播属性
int pval = 1;
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&pval,sizeof(pval));
struct sockaddr_in addr;
addr.sin_port = htons(8888);
addr.sin_family = AF_INET;
inet_pton(AF_INET,"192.168.112.255",&addr.sin_addr.s_addr);
// bind(fd, (struct sockaddr*)&addr, sizeof(addr));
int len = sizeof(addr);
int i = 1;
while (1) {
char buf[1024]= {0};
sprintf(buf,"this is %d",i++);
// 发送数据
sendto(fd,buf,strlen(buf)+1,0,(struct sockaddr*)&addr,len);
}
close(fd);
}
多播(组播)
单播地址标识单个IP接口,广播地址标识某个子网的所有IP接口,多播地址标识一组IP接口。
单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方案。
多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上的接口接收。
另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网使用。
客户端需要加入多播组,才能收到多播的数据。
多播地址
IP多播通信必须依赖于IP多播地址,在IPv4中它的范围从224.0.0.0 到239.255.255.255,并被划分为三类:
- 局部链接多播地址
- 预留多播地址
- 管理权限多播地址

设置多播
int setsockopt(int sockfd, int level,int optname, const void *optval, socklen_t optlen) ;
//服务器设置多播的信息,外出接口
- leve1 : IPPROTO_IP
- optname : IP_MULTICAST_IF
- optval : struct in_ddr
//客户端加入到多播组:
- leve1 : IPPROTO_IP
- optname : IP_ADD_MEMBERSHIP
- optval : struct ip_mreq
struct ip_mreq
{
//组播组的IP地址
struct in_addr imr_multiaddr;
//本地某一网络设备接口的IP地址
struct in_addr imr_address;
int imr_ifindex; // 网卡编号
};
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
多播客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
int main(){
//创建UDP套接字
int fd = socket(PF_INET,SOCK_DGRAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_family = AF_INET;
bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
// 加入多播组
struct ip_mreq pval;
inet_pton(AF_INET,"239.0.0.10", &pval.imr_multiaddr.s_addr);
pval.imr_interface.s_addr = INADDR_ANY
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&pval,sizeof(pval));
int len = sizeof(saddr);
while (1) {
char rebuf[1024] = {0};
//接受数据
recvfrom(fd,rebuf,sizeof(rebuf), 0, NULL, NULL);
printf("服务器数据:%s\n",rebuf);
}
close(fd);
}
多播服务器代码
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
//创建UDP套接字
int fd = socket(PF_INET,SOCK_DGRAM,0);
// 设置多播属性
struct in_addr pval;
inet_pton(AF_INET,"239.0.0.10",&pval.s_addr);
setsockopt(fd,IPPROTO_IP,IP_MULTICAST_IF,&pval,sizeof(pval));
// 初始化客户端地址
struct sockaddr_in addr;
addr.sin_port = htons(8888);
addr.sin_family = AF_INET;
inet_pton(AF_INET,"239.0.0.10",&addr.sin_addr.s_addr);
// bind(fd, (struct sockaddr*)&addr, sizeof(addr));
int len = sizeof(addr);
int i = 1;
while (1) {
char buf[1024]= {0};
sprintf(buf,"多播:this is %d",i++);
// 发送数据
sendto(fd,buf,strlen(buf)+1,0,(struct sockaddr*)&addr,len);
sleep(1);
}
close(fd);
}