天空 Blond - multicast
type: channel
link: https://skyblond.info/tag/multicast/
items: 【代码札记】零基础入门Linux套接字（socket）多播（multicast）

【代码札记】零基础入门Linux套接字（socket）多播（multicast）
type: item
date: 2022-11-15T22:45:00+08:00
description: 这篇文章完全是意料之外的。起因是正在上研究生的大学同学突然拿着一个txt后缀的C代码问我怎么sendto怎么返回参数错误。经过一阵奇妙的发展，最终变成了我用一下午的时间简单入门了一下Linux的套接字多播。本文简单记述了这个过程。背景这里简单介绍一下背景吧。所谓的套接字，就是Socket，它是一个用C语言开发的抽象层，它主要作为计算机进程间通讯的端点，通过抽象，它隐藏了底层通讯的细节，而提供了一个统一的交互界面。套接字根据传输层协议主要分为两类，一类是packets，底层是TCP支持的，用于两个进程间（相对）可靠的一对一通信；另一类是datagrams，底层是UDP支持的，用于不那么可靠的通讯，可以一对一，也可以一对多。说起多播，它介于单播和广播之间。单播其实就是一对一。而广播是向网络中的全体无差别发送。多播，有时候也称为组播，它是向网络中特定一组目标发送数据。多播的用处比较广泛，假如说你想向许多人分发数据，例如直播影像。如果采用单播，那么来一个人你就得建立一个套接字给他发送数据，人多了就得同一份数据向好多个套接字里写入。如果采用广播，那么你的数据会无差别发送给网络中的所有主机，如果别人不想要你的数据，那处理这种数据（哪怕仅仅是简单的拒绝1）就变成了一种负担。采用多播，你可以指定一个多播地址，数据只需要向套接字中写入一次，所有加入了这个地址的主机都会收到这个消息；而没有订阅的主机则不会收到消息2。在这里我们并不以此构建什么应用，只是简单的写一个收发程序来验证这个事情。目标编写一个C程序，使用如下命令行启动：# Sender ./multicast s 172.16.71.138 224.0.0.1 9000 # Receiver ./multicast r 224.0.0.1 9000作为发送者启动时，程序将向多播套接字中写入数据；作为接收者启动时，程序将从多播套接字中读取数据。写入的数据由用户输入。实现有一说一，我从大一之后就没再写过C，一直写Java比较多。如果各位觉得这C代码总有一股子咖啡味儿，还请多担待。建立项目虽然是C语言，但是作为一个Java程序员，多少先得弄个项目。CLion默认采用CMake管理项目，那话不多说，配置文件CMakeLists.txt的内容如下：cmake_minimum_required(VERSION 3.22) project(LinuxNetwork C) set(CMAKE_C_STANDARD 11) add_executable(multicast src/multicast/main.c src/multicast/sender.c src/multicast/receiver.c src/multicast/receiver.h src/multicast/sender.h src/multicast/common.h) 这里要求CMake的最低版本号其实没什么讲究，只因为我自己用的版本是3.22，所以就用了这个。使用的标准是C11。这里为了项目规整，又为了给日后这个同学再来问我问题留出余地，这次的代码我放到了src/multicast文件夹中。等之后他要是还有别的问题，我就可以把之后的代码放到其他文件夹中，从而避免了频繁创建工程。其实写代码的时候顺序是自底向上（先完成底层收发，再写命令行的部分），但是写完代码需要写文章讲解的时候，就变成自顶向下了（先说命令行，再说底层细节），所以实际写代码时候的顺序应该是按文章介绍顺序反着来。项目结构这里一共有这么几个文件：main.c：主函数，负责解析命令行参数，根据参数调用对应的过程sender.c：作为发送者的实现sender.h：发送者的头文件，用于在主函数中引用receiver.c：作为接收者的实现receiver.h：接收者的头文件，用于在主函数中引用common.h：共用的定义，例如消息大小头文件三个头文件起到了类似接口的做用：先定义函数签名，以便其他人能够在代码还没有被写出来时继续各自的开发工作。sender.h：#ifndef LINUXNETWORK_SENDER_H #define LINUXNETWORK_SENDER_H void sender_run(char *local_if, char *multicast_ip, char *port_str); #endif //LINUXNETWORK_SENDER_H 发送者的实现主要有三个参数：本地网络接口（多播从哪个网口发）、多播IP和端口（往哪个多播发）。当用户没有指定本地网络接口的时候，可以将local_if设定为NULL，以便使用默认的设置。receiver.h：#ifndef LINUXNETWORK_RECEIVER_H #define LINUXNETW…