14.5 Socket 应用组播通信

2023-10-15 10:45
文章标签 应用 通信 socket 组播 14.5

本文主要是介绍14.5 Socket 应用组播通信,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

组播通信是一种基于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 采用UDPif ((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 采用UDPif ((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 许可协议。转载请注明出处!

这篇关于14.5 Socket 应用组播通信的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/217157

相关文章

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合

Java中的Lambda表达式及其应用小结

《Java中的Lambda表达式及其应用小结》Java中的Lambda表达式是一项极具创新性的特性,它使得Java代码更加简洁和高效,尤其是在集合操作和并行处理方面,:本文主要介绍Java中的La... 目录前言1. 什么是Lambda表达式?2. Lambda表达式的基本语法例子1:最简单的Lambda表

Python结合PyWebView库打造跨平台桌面应用

《Python结合PyWebView库打造跨平台桌面应用》随着Web技术的发展,将HTML/CSS/JavaScript与Python结合构建桌面应用成为可能,本文将系统讲解如何使用PyWebView... 目录一、技术原理与优势分析1.1 架构原理1.2 核心优势二、开发环境搭建2.1 安装依赖2.2 验

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

SpringShell命令行之交互式Shell应用开发方式

《SpringShell命令行之交互式Shell应用开发方式》本文将深入探讨SpringShell的核心特性、实现方式及应用场景,帮助开发者掌握这一强大工具,具有很好的参考价值,希望对大家有所帮助,如... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

MySQL 分区与分库分表策略应用小结

《MySQL分区与分库分表策略应用小结》在大数据量、复杂查询和高并发的应用场景下,单一数据库往往难以满足性能和扩展性的要求,本文将详细介绍这两种策略的基本概念、实现方法及优缺点,并通过实际案例展示如... 目录mysql 分区与分库分表策略1. 数据库水平拆分的背景2. MySQL 分区策略2.1 分区概念

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.