首页 > 系统相关 >2024-02-29-Linux高级网络编程(3-UDP编程-TFTP、广播、多播)

2024-02-29-Linux高级网络编程(3-UDP编程-TFTP、广播、多播)

时间:2024-02-29 15:55:38浏览次数:22  
标签:02 多播 addr int 编程 sockfd 接字 include

3. UDP编程-TFTP、广播、多播

3.1 TFTP简介、通信过程

3.1.1 TFTP概述

TFTP:简单文件传送协议(Trivial File Transfer Protocol),最初用于引导无盘系统,被设计用来传输小文件

特点:基于UDP实现,不进行用户有效性认证

数据传输模式

  1. octet:二进制模式
  2. netascii:文本模式
  3. mail:已经不再支持

3.1.2 TFTP通讯过程

image-20240229110505785

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用临时端口与客户端进行通信
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认如果出现超时,则需要重新发送最后的包(数据或ACK)
  5. 数据的长度以512 Bytes传输
  6. 小于512Bytes的数据意味着传输结束

3.1.3 TFTP协议分析

image-20240229111603268

注意:
1. 以上的0代表的是\0
1. 不同的差错码对应不同的错误信息

3.1.4 案例

3.2 UDP广播

3.2.1 广播的概念

广播:由一台主机向该主机所在子网内的所有主机发送数据的方式;例如192.168.3.103主机发送广播信息,则·192.168.3.1~192.168.3.254·所有主机都可以接收到数据

广播只能用UDP或原始IP实现,不能用TCP

3.2.2 广播的用途

单个服务器与多个客户主机通信时减少分组流通以下几个协议都用到广播

  1. 地址解析协议(ARP.)
  2. 动态主机配置协议(DHCP )
  3. 网络时间协议(NTP)

3.2.3 广播的特点

  1. 处于同一子网的所有主机都必须处理数据
  2. UDP数据包会沿协议栈向上一直到UDP层
  3. 运行音视频等较高速率工作的应用,会带来大负载
  4. 局限于局域网内使用

3.2.4 广播地址

IP格式:{网络ID,主机ID}

网络ID表示由子网掩码中1覆盖的连续位;主机ID表示由子网掩码中0覆盖的连续位

定向广播地址:主机ID全1

  1. 例:对于192.168.220.0/24,其定向广播地址为192.168.220.255
  2. 通常路由器不转发该广播

受限广播地址255.255.255.255
路由器从不转发该广播

3.2.5 广播流程

发送者:

  1. 第一步,创建套接字 socket()
  2. 第二步,设置运行发送广播权限setsockopt()
  3. 第三步,向广播地址发送数据sendto()

接收者:

  1. 第一步,创建套接字 socket()
  2. 第二步,将套接字与广播的信息结构体绑定bind()
  3. 第三步,接收数据recvfrom()

3.2.6 套接口选项

int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
功能:设置一个套接字的选项(属性);
参数:
	socket:文件描述符;
	level:协议层次
		SOL_SOCKET 		套接字层次
		IPPROTO TCP 	tcp层次
		IPPROTO IP 		IP层次
	option_name: 选项的名称
        SO_BROADCAST 	允许发送广播数据 (SOL_SOCKET层次的)
    option_value:    设置的选项的值,设置是否允许发送前面套接字的值
         int类型的值,存储的是bool的数据(1和0)
         0  不允许
		1  允许
	option_len: option_value的长度
 返回值:
        成功0 
        失败-1

3.2.7 广播案例

broadcast_send.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define DEFAULT_PORT 32345
#define BROADCAST_ADDRESS "255.255.255.255"

int main() {
    int sockfd;
    struct sockaddr_in broadcast_addr;
    int broadcast_enable = 1;

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }

    // 设置套接字选项,允许广播
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast_enable, sizeof(broadcast_enable)) == -1) {
        perror("setsockopt");
        exit(1);
    }

    // 设置广播地址
    memset(&broadcast_addr, 0, sizeof(broadcast_addr));
    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_port = htons(DEFAULT_PORT);
    if (inet_pton(AF_INET, BROADCAST_ADDRESS, &(broadcast_addr.sin_addr)) <= 0) {
        perror("inet_pton");
        exit(1);
    }

    // 构造广播数据
    char *message = "Hello, broadcast!";
    int message_len = strlen(message);

    // 发送广播
    if (sendto(sockfd, message, message_len, 0, (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) == -1) {
        perror("sendto");
        exit(1);
    }

    printf("Broadcast message sent.\n");

    // 关闭套接字
    close(sockfd);

    return 0;
}

broadcast_recv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define DEFAULT_PORT 32345

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    char buffer[1024];

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }

    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(DEFAULT_PORT);

    // 绑定套接字到服务器地址
    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(1);
    }

    printf("Waiting for broadcast message...\n");

    while (1) {
        // 接收广播消息
        client_addr_len = sizeof(client_addr);
        int recv_len = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client_addr, &client_addr_len);
        if (recv_len == -1) {
            perror("recvfrom");
            exit(1);
        }

        buffer[recv_len] = '\0';

        printf("Received broadcast message from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        printf("Message: %s\n", buffer);
    }

    // 关闭套接字
    close(sockfd);

    return 0;
}

image-20240229143402305

3.3 UDP多播

3.3.1 多播的概念

多播: 数据的收发仅仅在同一分组中进行

多播的特点

  1. 多播地址标示一组接口
  2. 多播可以用于广域网使用
  3. 在IPv4中,多播是可选的

多播(Multicast)地址是一种特殊的IP地址,用于在多播通信中标识一组接收者。

多播地址范围是224.0.0.0 ~ 239.255.255.255

其中,224.0.0.0224.0.0.255是保留的用于特殊目的的多播地址范围,称为本地链路多播地址。

224.0.1.0238.255.255.255是全球范围的多播地址范围,称为全局多播地址。

239.0.0.0239.255.255.255是管理员可用的本地多播地址范围。

3.3.2 多播流程

发送者:

  1. 第一步,创建套接字 socket()
  2. 第三步,向多播地址发送数据sendto()

接收者:

  1. 第一步,创建套接字 socket()
  2. 第二步,设置为加入多播组setsockopt()
  3. 第三步,将套接字与多播信息结构体绑定bind()
  4. 第四步,接收数据recvfrom()

3.3.3 多播地址结构体

在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示

struct in_addr{
    in_addr_t s_addr;
}

struct ip_mreq{
    struct in_addr imr_multiaddr; //多播组IP,如224.0.0.1
    struct in_addr imr_interface; //将要添加到多播组的IP,可以用INADDR_ANY
}

3.3.4 多播套接口选项

#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
功能:设置一个套接字的选项(属性);
参数:
	socket:文件描述符
	level:协议层次
        IPPROTO IP层次 
    option_name:选项的名称
		IP_ADDIMEMBERSHIP  加入多播组
    option_value:设置的选项的值
        struct ip_mreq{
            struct in_addr imr_multiaddr; //组播ip地址
            struct in_addr imr_interface; //主机地址, INADDR_ANY 任意主机地址(自动获取你的主机地址)     
        }
		
    option_len:option_value的长度;
返回值:
	成功:0
	失败:-1

加入多播组示例

char group[INET_ADDRSTRLEN] = "224.0.0.1";
//定义一个多播地址
struct ip_mreq mreq;

//添加一个多播组IP
mreq.imr_interface.s_addr = inet_addr(group);

//添加一个将要添加到多播组的IP
mreq.imr_interface.s_addr =htonl(INADDR_ANY);

setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

10_groupcast_send.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define DEFAULT_PORT 12346
#define MULTICAST_ADDRESS "239.0.0.1"

int main() {
    int sockfd;
    struct sockaddr_in multicast_addr;
    char *message = "Hello, multicast!";
    int message_len = strlen(message);

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }

    // 设置多播地址
    memset(&multicast_addr, 0, sizeof(multicast_addr));
    multicast_addr.sin_family = AF_INET;
    multicast_addr.sin_port = htons(DEFAULT_PORT);
    if (inet_pton(AF_INET, MULTICAST_ADDRESS, &(multicast_addr.sin_addr)) <= 0) {
        perror("inet_pton");
        exit(1);
    }

    // 发送多播数据
    if (sendto(sockfd, message, message_len, 0, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)) == -1) {
        perror("sendto");
        exit(1);
    }

    printf("Multicast message sent.\n");

    // 关闭套接字
    close(sockfd);

    return 0;
}

11_groupcast_recv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define DEFAULT_PORT 12346
#define MULTICAST_ADDRESS "239.0.0.1"

int main() {
    int sockfd;
    struct sockaddr_in local_addr;
    struct ip_mreq multicast_req;
    char buffer[1024];

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(1);
    }

    // 设置本地地址
    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(DEFAULT_PORT);

    // 绑定套接字到本地地址
    if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1) {
        perror("bind");
        exit(1);
    }

    // 加入多播组
    memset(&multicast_req, 0, sizeof(multicast_req));
    multicast_req.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDRESS);
    multicast_req.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicast_req, sizeof(multicast_req)) == -1) {
        perror("setsockopt");
        exit(1);
    }

    printf("Waiting for multicast message...\n");

    while (1) {
        // 接收多播消息
        int recv_len = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, NULL, NULL);
        if (recv_len == -1) {
            perror("recvfrom");
            exit(1);
        }

        buffer[recv_len] = '\0';

        printf("Received multicast message: %s\n", buffer);
    }

    // 关闭套接字
    close(sockfd);

    return 0;
}

image-20240229154854379

标签:02,多播,addr,int,编程,sockfd,接字,include
From: https://www.cnblogs.com/hasaki-yasuo/p/18044461

相关文章

  • 洛谷题单指南-二分查找与二分答案-P1102 A-B 数对
    原题链接:https://www.luogu.com.cn/problem/P1102题意解读:寻找A-B=C的数对数量,C大于0,B一定比A小,枚举B,找A是否存在即可。解题思路:先将数据由小到大排序,接下来介绍两种方法:二分、双指针1、二分枚举第1~n-1个数,作为B,寻找A=B+C的数量,只需要通过二分查找第一A和最后一个A的位置l、......
  • national-olympiad-in-informatics-in-provinces-2022-travels
    NationalOlympiadinInformaticsinProvinces2022travels(有些时间点懒得回忆了,就不写具体时间了。)这次去广大附中考,要求提前\(3\)天到附近的酒店隔离。隔离酒店环境不错,比想象中好很多。服务也还可以,午饭晚饭都挺合我胃口,就是饭不够吃。和WaterAche一间房。这几天摆......
  • noip2020
    NOIp2020游记第一次打NOIp,有点小紧张/kel8:30开考,8:15进考场顺便带了一大包巧克力进场,想着考试的时候吃开考打开文件夹一看string就大窘了,字符串算法刚好没学啊/fadT1一看第一反应网络流,大喜,前两天刚复习兴致勃勃准备开始打dinic,然后发现这题和网络流一点关系没有随便打......
  • 2024-02-29-Linux高级网络编程(2-UDP编程)
    2.UDP编程2.1字节序概述字节序是指多字节数据的存储顺序分类:小端格式:将低位字节数据存储在低地址(LSB)大端格式:将高位字节数据存储在低地址2.1.1如何判断当前系统的字节序#include<stdio.h>unionun{/*data*/inta;charb;};intmain(intargc......
  • 快速改善代码质量的20 条编程规范
    命名大到项目名,模块名,包名,对外暴露的接口,笑道类名,函数名,变量名,参数名。只要做开发,我们就逃不过起名字这一关,命名的好坏,对于代码的可读性来说非常中国要。甚至起到决定性的作用。除此之外,命名能力也体现了一个程序员的而基本的素养。这也是我们把命名放到第一位的原因。 取一个......
  • 界面控件DevExpress WinForms 2024产品路线图预览(一)
    DevExpressWinForm拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForm能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任!本文将介绍2024年DevExpressWinForms第一个主要更新......
  • 【STL和泛型编程】3. set、map分析(及typename起源)
    前置知识:红黑树原理 【数据结构】7.平衡搜索树(AVL树和红黑树),红黑树的平衡性有利于search和insert红黑树的迭代器begin()左侧end()右侧迭代顺序56781011121315不能使用迭代器修改Key的值,例如将6改成50会破坏红黑树的性质1.RB-tree在g++编译......
  • 20240228打卡
    早上python程序设计课,主要对py的安装配置以及基础的语法进行了学习,并通过实际例子体会其鲜明的特点然后工程数学课,主要学习了最优化模型,并顺带复习了以前线性代数的相关知识,老师让我们要会用matlab,但好贵,学生认证后还要29美元qwq。掏钱是不可能滴,先用python试试看吧下午虽然没课......
  • 20240228日记
    今天难得的没加班,为了新住所的事情,特意去山姆超市买了碗,为了续费省30,拿了一盒100+的蓝莓。这就是新型的为了省钱而花钱吧。今天的工作也不算顺利,但是现在只想摆烂了。对于工作的怨气,很大程度是因为对于领导的胡乱指挥。但是我今天仔细思考了下为什么会有这样的信息偏差;为什么我目......
  • 题解 gym102900J 【Octasection】
    代码:#include<iostream>#include<algorithm>#include<stack>#include<vector>#include<cstdio>usingnamespacestd;typedefstructRectangle_tag{ intx1; intx2; inty1; inty2; Rectangle_tag(intx1_,intx2_,int......