14.5 Socket 应用组播通信

news2025/2/1 10:57:09

组播通信是一种基于UDP协议的网络通信方式,它允许发送方将消息同时传递给多个接收方。在组播通信中,发送方和接收方都会加入一个共同的组播组,这个组播组对应一个特定的IP地址,所有加入该组播组的主机都能够接收到发送方发送的消息。组播通信可以有效地减少网络流量和网络负载,因为在传统的点对点通信方式下,每个消息都需要单独传输到每个接收方,而在组播通信中,每个消息只需要传输一次,就可以同时传递给多个接收方。

在使用组播模式时,需要在套接字上使用setsockopt()函数来设置套接字的IP_MULTICAST_IF选项,指定本地主机的出站接口地址,用于发送组播数据包。此外,还可以设置IP_ADD_MEMBERSHIP选项,将套接字加入到一个特定的组播组中,以便接收该组播组中的数据包。

在使用组播模式时需要读者注意,组播模式需要使用特定的IP地址范围,如224.0.0.0~239.255.255.255,且需要确保组播组内的所有成员都在同一个网络中。同时,组播模式也不保证数据传输的可靠性,因为UDP本身就是无连接的协议,所以需要在应用程序中自行处理数据丢失或重复的情况。

14.5.1 服务端实现

先来实现服务端代码,首先我们定义一个端口号PORT=9999并定义好组名GROUP="225.1.2.3",接着通过调用两次setsockopt函数,第一次调用指定传入SO_REUSEADDR参数设置为组播模式,第二次调用指定传入IP_ADD_MEMBERSHIP用于设置组,经过两次设置服务端将被绑定到GROUP指定的组名上面,并在底部recvfrom循环等待数据包的到达,当数据包到达后则直接通过sendto发送一个消息给上线客户端。

#include <winsock.h>
#include <iostream>

#pragma comment(lib, "wsock32.lib")
#define PORT  9999
#define GROUP "225.1.2.3"

using namespace std;

int main(int argc, char *argv[])
{
  WSADATA wsaData;
  struct sockaddr_in addr;
  int fd;
  struct ip_mreq mreq;

  // 初始化套接字
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    std::cout << "初始化失败" << std::endl;
    return 0;
  }

  // 创建套接字 SOCK_DGRAM 采用UDP
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
    std::cout << "套接字创建失败" << std::endl;
    return 0;
  }

  // 设置套接字为组播模式
  u_int yes = 1;
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0)
  {
    std::cout << "设置组播模式失败" << std::endl;
    return 0;
  }

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(PORT);

  // 绑定套接字
  if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
  {
    std::cout << "绑定失败" << std::endl;
    return 0;
  }

  // 设置组播模式中的组信息
  mreq.imr_multiaddr.s_addr = inet_addr(GROUP);
  mreq.imr_interface.s_addr = htonl(INADDR_ANY);

  // 设置组
  if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
  {
    int err = GetLastError();
    std::cout << "设置组失败: " << err << std::endl;
    return 0;
  }

  // 循环手法数据
  while (1)
  {
    char recv_buffer[4096];
    int addrlen = sizeof(addr);
    int nbytes;

    // 接收组播数据
    if ((nbytes = recvfrom(fd, recv_buffer, 4096, 0, (struct sockaddr *) &addr, (int *)&addrlen)) < 0)
    {
      std::cout << "接收数据包失败" << std::endl;
      return 0;
    }
    recv_buffer[nbytes] = '\0';
    std::cout << "接收组播数据包: " << recv_buffer << std::endl;

    // 发送组播数据包
    char send_buffer[4096] = "server mesage";
    sendto(fd, send_buffer, strlen(send_buffer), 0, (struct sockaddr *) &addr, sizeof(addr));
  }

  return 0;
}

14.5.2 客户端实现

在组播模式中客户端的修改部分很简单,仅仅只需通过socket(AF_INET, SOCK_DGRAM, 0)函数设置套接字为UDP模式,并填充组名即可,其他通信模式与UDP保持一致。

#include <winsock.h>
#include <iostream>

#pragma comment(lib, "wsock32.lib")
#define PORT  9999
#define GROUP "225.1.2.3"

using namespace std;

int main(int argc, char *argv[])
{
  WSADATA wsaData;
  struct sockaddr_in addr;
  int fd;

  // 初始化套接字
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    std::cout << "初始化失败" << std::endl;
    return 0;
  }

  // 创建套接字 SOCK_DGRAM 采用UDP
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
    std::cout << "套接字创建失败" << std::endl;
    return 0;
  }

  // 设置组播模式组信息
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr(GROUP);
  addr.sin_port = htons(PORT);

  // 循环
  while (1)
  {
    // 发送组播数据包
    char send_buffer[4096] = "Hello, World!";
    if (sendto(fd, send_buffer, strlen(send_buffer), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
      std::cout << "发送失败" << std::endl;
      return 0;
    }

    // 接收组播数据
    int addrlen = sizeof(addr);
    char recv_buffer[4096] = { 0 };
    recvfrom(fd, recv_buffer, 4096, 0, (struct sockaddr *) &addr, (int *)&addrlen);
    std::cout << "接收组播数据包: " << recv_buffer << std::endl;
    Sleep(1000);
  }
  return 0;
}

读者可自行编译上述代码,运行一个服务端并运行多个客户端即可观察组播收发情况,如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/22dcfd42.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1093853.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++概述

一、C特色 1.C是面向对象的高级程序设计语言 2.支持数据封装&#xff0c;将数据和对该数据进行操作的函数封装在一个类中&#xff0c;对象就是某一个具体的类。即类是数据封装的工具&#xff0c;对象是数据封装的实现。 3.具有继承性 4.具有函数重载 二、拓展介绍 1.C标准&a…

基于音频SOC开发板的主动降噪ANC算法源码实现

基于音频SOC开发板的主动降噪ANC算法源码实现 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群附加赠送降噪开发资料,

IO流:java中解码和编码出现乱码说明及代码实现

IO流&#xff1a;java中解码和编码的代码实现 一、UTF-8和GBK编码方式二、idea和eclipse的默认编码方式三、解码和编码方法四、代码实现编码解码 五、额外知识扩展 一、UTF-8和GBK编码方式 如果采用的是UTF-8的编码方式&#xff0c;那么1个英文字母 占 1个字节&#xff0c;1个…

深度学习-优化算法与梯度下降

文章目录 前置知识指数移动平均EMAL2正则&#xff08;L2 Regularization)权重衰减&#xff08;Weight Decay) 优化器SDGMomentumNAGAdagradRMSPropAdaDeltaAdamAdamW综上 学习率StepLRMultiStepLRExponentialCosineAnealingRLROP&#xff08;ReduceLRonPlateau)lambda总结 前置…

【图像处理】图像配准、图像增强和图像分割研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

网络编程基础知识总结——IP,端口,协议

目录 1. 什么是网络编程&#xff1f; 2. 网络编程的三要素 3. IP 3.1 IP地址的概念 3.2 IP地址的分类 3.3 IPv4解析 3.4 Ipv6解析 4. IPv4 的使用细节 5. 特殊IP地址 4. 端口号 5. 协议 5.1 UDP协议 5.2 TCP协议 1. 什么是网络编程&#xff1f; 总的来说就是一句…

RuntimeWarning: More than 20 figures have been opened

在画图操作结束后使用plt.close(all)语句&#xff0c;但是此时图像仍然不能正常显示&#xff0c;之前是可以正常显示的&#xff0c;然后又在最后的画图的部分添加plt.show()语句&#xff0c;可以正常显示图像并且不再出现警告了。

Android---DVM以及ART对JVM进行优化

Dalvik Dalvik 是 Google 公司自己设计用于 Android 平台的 Java 虚拟机&#xff0c;Android 工程师编写的 Java 或者 Kotlin 代码最终都是在这台虚拟机中被执行的。在 Android 5.0 之前叫作 DVM&#xff0c;5.0 之后改为 ART&#xff08;Android Runtime&#xff09;。在整个…

oracle创建数据库,导入dmp操作全家桶

背景&#xff1a;小明在一家IT公司就职&#xff0c;通过查看项目&#xff0c;公司使用的是oracle&#xff0c;几天后&#xff0c;经理要求他从服务器导入数据库到公司服务器&#xff0c;聪明的小明就开始干了起来&#xff0c;整理如下教程。 说明&#xff1a;此次演示环境oracl…

【LeetCode力扣】297. 二叉树的序列化与反序列化

目录 1、题目介绍 2、解题思路 2.1、详细过程图解 2.2、代码描述 2.3、完整代码 1、题目介绍 原题链接&#xff1a;297. 二叉树的序列化与反序列化 - 力扣&#xff08;LeetCode&#xff09; 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,null,null,4,5] 输出&#…

代码随想录算法训练营第二十二天丨 二叉树part09

669. 修剪二叉搜索树 思路 递归法 从图中可以看出需要重构二叉树&#xff0c;想想是不是本题就有点复杂了。 其实不用重构那么复杂。 在上图中我们发现节点0并不符合区间要求&#xff0c;那么将节点0的右孩子 节点2 直接赋给 节点3的左孩子就可以了&#xff08;就是把节点…

mac 移动硬盘推出

最近移动硬盘推出总是出现 磁盘无法推出&#xff0c;因为一个或多个程序正在使用它 的问题 解决办法 首先尽可能关闭所有的应用&#xff0c;然后点击左上角的 点击强制退出&#xff0c;注意其中的预览&#xff0c;这个通常就卡在了这里 然后就可以正常退出了

Python点击exe后报错:Failed to execute script xxxx问题的解决办法

最近工作在弄人脸识别的问题&#xff0c;从gitee来pull了一个但是发现报了一个Failed to execute script XXX的问题 造成这个问题的原因是执行文件exe存放的目录不对&#xff0c;可能在打包前exe文件并不是存在在这个位置。 解决方案将exe文件尝试存在在不同目录下&#xff…

【已解决】No Python at ‘D:\Python\python.exe‘

起因&#xff0c;我把我的python解释器&#xff0c;重新移了个位置&#xff0c;导致我在Pycharm中的爬虫项目启动&#xff0c;结果出现这个问题。 然后&#xff0c;从网上查到了这篇博客: 【已解决】No Python at ‘D:\Python\python.exe‘-CSDN博客 但是&#xff0c;按照上述…

数据结构 - 5(二叉树7000字详解)

一&#xff1a;二叉树的基本概念 1.1树形结构 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 注意&am…

如何删除错误堆栈里的数据

修改某个主数据。然后发现N年前&#xff0c;某位开发在DTP上做了一个错误堆栈。 这里面有很多历史错误信息&#xff0c;有几千条了&#xff0c;一条条删肯定不可能。 如果不删除&#xff0c;DTP增量无法激活&#xff0c;明天处理链肯定出问题。 于是找到一位印度大神的方法&a…

java劳务外包管理系统springboot+vue

当前&#xff0c;系统开发的技术已经发展成熟&#xff0c;而且通过计算机网络可以获取开发工具的使用方法&#xff0c;以及规范化编写的模块化代码&#xff0c;这些知识可以帮助开发者顺利完成本系统的编码工作。 采用JavaMysql的方式设计制作的劳务外包管理系统&#xff0c;在…

【Python、Qt】使用QItemDelegate实现单元格的富文本显示+复选框功能

主打一个 折磨 坑多 陪伴。代码为Python&#xff0c;C的就自己逐条语句慢慢改吧。 Python代码&#xff1a; import sys from types import MethodType from PyQt5.QtCore import Qt,QPoint,QSize,QRect,QEvent from PyQt5.QtGui import QStandardItemModel, QStandardItem,QTe…

LeetCode - 318 最大单词长度乘积(Java JS Py C)

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 318. 最大单词长度乘积 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串数组 words &#xff0c;找出并返回 length(words[i]) * length(words[j]) 的最大值&#xff0c;并且这两个单词…