目录
通信流程
- 服务器端
- 1.创建通信的套接字
- int fd = socket( af_inet, SOCK_DGRAM, 0)
-
- 绑定 -> 通信的fd 和本地 IP / port 绑定
- struct sockaddr_in addr;
- 3.通信
- 接收数据: recvfrom
- 发送数据: sendto
- 4.关闭通信的fd
- close(fd);
- 客户端
- 1.创建一个通信的套接字
- 2.通信
- 接收数据: recvfrom
- 发送数据: sendto
- 3.关闭通信的文件描述符
- close();
- 1.创建通信的套接字
组播应用
- 一对多点应用:一对多点应用是指一个发送者,多个接收者的应用形式,这是最常见的多播应用形式。典型的应用包括:媒体广播、媒体推送、信息缓存、事件通知和状态监视等。
- 多对一应用:多点对点应用是指多个发送者,一个接收者的应用形式。通常是双向请求响应应用,任何一端(多点或点)都有可能发起请求。典型应用包括:资源查找、数据收集、网络竞拍、信息询问等。
- 多对多应用:多点对多点应用是指多个发送者和多个接收者的应用形式。通常,每个接收者可以接收多个发送者发送的数据,同时,每个发送者可以把数据发送给多个接收者。典型应用包括:多点会议、资源同步、并行处理、协同处理、远程学习、讨论组、分布式交互模拟(DIS)、多人游戏等。
组播IP地址
组播IP地址的作用
标识某一个组播应用/业务(组播源) 代表接收此组播业务的所有主机(接收端) 组播地址为D类地址,
范围是224.0.0.1~239.255.255.255
永久组播地址
所代表的含义已固定,不可更改。
供路由协议、拓扑查找等使用,不用于组播转发。
临时组播地址
组播地址可以被重复使用。
即当一个地址被应用于某个应用时,当此应用关闭不再使用时,此地址就可以被回收,可以被再次应用到其它应用中。
//复用地址
int opt = 1;
if (setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
LOG(ERROR) << "Failed to set socket options so_reuseaddr.";
return false;
}
本地管理地址
仅在本地管理域内有效。
在不同的管理域内重复使用相同的本地管理组地址不会导致冲突
存在问题
主要问题就是丢包和无序
UDP丢包和无序 问题的解决方法 - johnny_HITWH - 博客园 (cnblogs.com)
UDP大批量传输数据时的丢包问题优化_udp发送太快会丢数据-CSDN博客
C++实现udp分包和组包_夏天匆匆2过的博客-CSDN博客
UDP解决丢包问题总结_接收udp组播数据 不丢包怎么实现_GoodLinGL的博客-CSDN博客
网编(20):UDP传输数据经常遇到的问题_udp乱序-CSDN博客
代码实现
Linux C/C++编程:Udp组播(多播)_c udp组播_OceanStar的学习笔记的博客-CSDN博客
Linux C/C++组播_linux组播配置_别,爱℡的博客-CSDN博客
网络组播(Multicast)是一种网络通信模式,允许一组主机(通常称为组成员)通过单一的数据包传输来共享数据。组播通信可以有效地减少网络流量,因为数据只需要一次发送,而不是多次单播或广播。
在组播通信中,每个组成员都加入了一个特定组播组的 IP 地址,而不是单个主机的 IP 地址。发送到这个组播地址的数据包将被传输到所有加入了这个组的主机。组播地址是一个 Class D IP 地址(224.0.0.0 - 239.255.255.255),其中第一个字节是 0xE0 - 0xEF 的范围。
下面是一个使用C++和UDP协议实现网络组播的示例代码。这个示例中有两个程序,一个用于发送消息,另一个用于接收消息。发送程序将消息发送到一个组播地址,接收程序加入了这个组播组,并接收到发送的消息。
发送程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
char group_addr[16]="224.0.0.1";
int socked=socket(AF_INET,SOCK_DGRAM,0);
if(socked<0)
{
perror("socket failed!");
return 2;
}
struct sockaddr_in remote_addr;
memset(&remote_addr,0,sizeof(remote_addr));
remote_addr.sin_family=AF_INET;
remote_addr.sin_addr.s_addr=inet_addr(group_addr);
remote_addr.sin_port=htons(5000);
//设置为广播模式
int broadcast = 1;
if (setsockopt(socked, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == -1) {
printf("setsockopt broadcast failed. \n");
return -1;
}
char buf[1024]="This is a group udp";
int length=0;
uint32_t cnt=0;
while(1)
{
cnt++;
length=sendto(socked,buf,strlen(buf),0,(struct sockaddr *)&remote_addr,sizeof(remote_addr));
printf("Send Message:%s\t, cnt:%d\n", buf, cnt);
sleep(1);
}
close(socked);
return 0;
}
接收程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[]){
int socked=socket(AF_INET,SOCK_DGRAM,0);
if(socked<0)
{
perror("socket failed!");
return 2;
}
int reuse = 1;
if (setsockopt(socked, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
perror("Failed to set SO_REUSEADDR option \n");
return 1;
}
char group[16]="239.255.0.1";
struct sockaddr_in local_addr;
memset(&local_addr,0,sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = htons(5000);
int ret = bind(socked, (struct sockaddr*)&local_addr, sizeof(local_addr));
if(ret<0)
{
perror("bind failed !");
return 3;
}
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(group);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
/*
*
* int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
* param:
* optname
* * IP_MULTICAST_LOOP 支持多播数据回送
* * IP_ADD_MEMBERSHIP 加入多播组
* * IP_DROP_MEMBERSHIP 离开多播组
* optval
* * IP_MULTICAST_LOOP 选项对应传入 unsigned int 来确认是否支持多播数据回送
* * IP_ADD_MEMBERSHIP 传入 ip_mreq
* * IP_DROP_MEMBERSHIP 传入 ip_mreq
*
* */
ret=setsockopt(socked,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
if(ret<0){
perror("setsockopt failed !");
return 3;
}else
{
printf("setsockopt success\n");
}
char buf[1024];
int length=0;
struct sockaddr_in sender;
socklen_t sender_len=sizeof(sender);
uint32_t cnt=0;
while (true){
cnt++;
memset(buf, 0, sizeof(buf));
length=recvfrom(socked, buf, sizeof(buf), 0, (struct sockaddr*)&sender,&sender_len);
buf[length]='\0';
printf("%s %d : %s\t, cnt:%d\n",inet_ntoa(sender.sin_addr),ntohs(sender.sin_port),buf, cnt);
// sleep(1);
}
setsockopt(socked, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq));
close(socked);
return 0;
}