【C语言系统编程】【第三部分:网络编程】3.2 数据传输和协议

3.2 数据传输和协议

这一部分将探索网络传输中数据的组织和操纵方式,包括数据封包和拆包、数据完整性校验以及数据序列化与反序列化的方法。这些知识对确保数据可靠和高效传输至关重要。

3.2.1 数据传输
3.2.1.1 数据封包与拆包
  • 定义:数据封包是指将数据按照一定的协议格式进行组织,将其封装成包以便于在网络上传输。数据拆包则是指在接收端将封装的数据包还原回原始数据的过程。

  • 作用:封包将分散的数据组合成固定格式的数据包,以确保数据传输的可靠性和可解析性;拆包则确保接收到的数据包能够还原回具有实际意义的原始数据。

  • 示例代码解析

#include <stdio.h>
#include <string.h>

// 定义结构体 Packet
struct Packet {
    unsigned int length;  // 数据包长度 [1]
    char data[256];       // 数据内容 [2]
};

// 函数 pack_data:打包数据
void pack_data(const char *input, struct Packet *packet) {
    packet->length = strlen(input);  // 设置数据包长度 [3]
    strcpy(packet->data, input);     // 拷贝数据到包中 [4]
}

// 函数 unpack_data:解包数据
void unpack_data(const struct Packet *packet, char *output) {
    strncpy(output, packet->data, packet->length); // 拷贝数据 [5]
    output[packet->length] = '\0';                 // 追加字符串结束符 [6]
}

int main() {
    char message[] = "Hello, world!";
    struct Packet packet;
    char unpacked_message[256];

    pack_data(message, &packet);                  // 打包数据 [7]
    unpack_data(&packet, unpacked_message);       // 解包数据 [8]

    printf("Original message: %s\n", message);
    printf("Packed message length: %d\n", packet.length);
    printf("Unpacked message: %s\n", unpacked_message);

    return 0;
}
  • [1] 数据包长度unsigned int length 用于存储数据包的长度,以字节为单位。
  • [2] 数据内容char data[256] 定义了一个最大存储256字节的字符数组,用于承载数据内容。
  • [3] 设置数据包长度:通过 strlen() 函数计算 input 的长度,以标记实际承载数据的长度。
  • [4] 拷贝数据到包中strcpy() 函数用于将字符串从 input 拷贝到结构体的 data 字段中。
  • [5] 拷贝数据strncpy() 从数据包的 data 字段拷贝出 length 字节的数据放入输出缓冲区 output
  • [6] 追加字符串结束符:对 output 在最后一位追加空字符结束符,以形成正确的字符串。
  • [7] 打包数据:调用 pack_data()message 中的数据打包到 packet
  • [8] 解包数据:通过 unpack_data()packet 解包出数据到 unpacked_message
在上述代码中,`pack_data`函数将字符串`input`封装到`Packet`结构中,而`unpack_data`函数将`Packet`结构中的数据解封回原始字符串。
3.2.1.2 数据完整性与校验(Checksum)
  • 定义:校验和是一种用于检测数据传输错误的技术,通过对数据进行一定的数学运算生成校验值,并在数据传输时附加到数据末尾,接收端通过相同运算验证数据的完整性。

  • 作用:确保数据在传输过程中没有受到损坏或篡改。

  • 示例代码解析

#include <stdio.h>

// 函数 checksum:计算字符串数据的校验和
unsigned int checksum(const char *data) {
    unsigned int sum = 0; // 初始化校验和值为 0 [1]
    while (*data) {       // 遍历字符串直到末尾 [2]
        sum += *data++;   // 将每个字符的 ASCII 值累加到 sum 中 [3]
    }
    return ~sum;          // 返回 sum 的按位取反值 [4]
}

int main() {
    char message[] = "Hello, world!";
    unsigned int cs = checksum(message); // 计算校验和 [5]

    printf("Message: %s\n", message);
    printf("Checksum: %u\n", cs); // 打印校验和 [6]

    return 0;
}
  • [1] 初始化校验和值unsigned int sum = 0 初始化了一个无符号整数用于累加字符串中每个字符的 ASCII 值。
  • [2] 遍历字符串:通过 while (*data) 循环遍历字符串各个字符,直到遇到空字符 \0(字符串结束)。
  • [3] 字符累加sum += *data++ 累加每个字符的 ASCII 值到 sum 中,并将指针移向下一个字符。
  • [4] 按位取反~sum 返回累加结果的按位取反值,常用于生成更复杂的校验和以用于校验机制。
  • [5] 计算校验和:在 main 函数中调用 checksum() 函数对 message 进行校验和计算。
  • [6] 打印校验和:通过 printf() 输出原始消息及其对应的校验和值。
在上述代码中,`checksum`函数计算字符串`message`的校验和,结果用于检测数据传输中的错误。
3.2.1.3 数据序列化与反序列化(JSON, XML, Protocol Buffers, etc.)
  • 定义
    • 数据序列化:将复杂的数据结构转换为便于存储和传输的格式,如JSON、XML、Protocol Buffers等。
    • 数据反序列化:将序列化格式的数据转换回原来的数据结构。
  • 作用:增强不同系统之间的数据交换能力,将数据在不同语言和平台之间传输和解释。
  • 示例(使用JSON):
#include <stdio.h>
#include <jansson.h> // 用于 JSON 库的包含 [1]

// 定义结构体 Person
typedef struct {
    char name[50];
    int age;
} Person;

// 函数 serialize:序列化 Person 对象
void serialize(const Person *p, char *out) {
    json_t *root = json_object(); // 创建 JSON 对象 [2]
    json_object_set_new(root, "name", json_string(p->name)); // 设置名称字段 [3]
    json_object_set_new(root, "age", json_integer(p->age));  // 设置年龄字段 [4]

    strcpy(out, json_dumps(root, 0)); // 将 JSON 对象序列化为字符串 [5]
    json_decref(root); // 渐减对象引用计数以释放资源 [6]
}

// 函数 deserialize:反序列化 JSON 字符串
void deserialize(const char *in, Person *p) {
    json_t *root;
    json_error_t error;

    root = json_loads(in, 0, &error); // 从字符串加载 JSON 对象 [7]
    if (!root) {
        fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); // 错误处理
        return;
    }

    json_t *name = json_object_get(root, "name"); // 获取名称字段 [8]
    json_t *age = json_object_get(root, "age");   // 获取年龄字段 [9]

    if (json_is_string(name)) {
        strcpy(p->name, json_string_value(name)); // 复制字符串值 [10]
    }
    
    if (json_is_integer(age)) {
        p->age = json_integer_value(age); // 获取整数值 [11]
    }

    json_decref(root); // 释放 JSON 对象 [12]
}

int main() {
    Person p1 = {"John Doe", 30};
    char json_data[256];

    serialize(&p1, json_data); // 序列化 Person 到 JSON 字符串 [13]
    printf("Serialized JSON: %s\n", json_data);

    Person p2;
    deserialize(json_data, &p2); // 从 JSON 字符串反序列化到 Person [14]
    printf("Deserialized Person: Name = %s, Age = %d\n", p2.name, p2.age);

    return 0;
}
  • [1] 使用 Jansson 库jansson.h 是一个用于处理 JSON 数据的 C 库。
  • [2] 创建 JSON 对象json_t *root = json_object(); 创建一个新的 JSON 对象。
  • [3] 设置名称字段:使用 json_object_set_new()name 字段添加到 JSON 对象中,值为字符串。
  • [4] 设置年龄字段:使用 json_object_set_new()age 字段添加到 JSON 对象中,值为整数。
  • [5] 序列化为字符串json_dumps(root, 0) 将 JSON 对象转换为 JSON 格式的字符串,并复制到输出缓冲区 out
  • [6] 释放资源json_decref(root); 減少 root 的引用计数,并在需要时释放内存。
  • [7] 从字符串加载 JSON 对象:将 JSON 格式的字符串转换回 JSON 对象。
  • [8] 获取名称字段:从 JSON 对象中获取 name 字段。
  • [9] 获取年龄字段:从 JSON 对象中获取 age 字段。
  • [10] 复制字符串值:如果是字符串值,使用 strcpy 复制到 Person 结构体中的 name 字段。
  • [11] 获取整数值:如果是整数值,使用 json_integer_value 获取并赋值给 Person 结构体中的 age
  • [12] 释放 JSON 对象:释放 root 对象的内存。
  • [13] 序列化 Person 到 JSON 字符串:通过 serialize() 函数将 Person 对象转换为 JSON 格式字符串。
  • [14] 从 JSON 字符串反序列化到 Person:使用 deserialize() 函数将 JSON 字符串转换回 Person 对象。
在上述代码中,使用`jansson`库函数将名为`Person`的结构体序列化为JSON格式字符串,并将其反序列化回来。
3.2.2 常见协议

在网络编程中,理解和掌握一些常见的应用层协议非常重要。以下是一些广泛使用的协议及其基础概述。

3.2.2.1 HTTP/HTTPS 协议基础

HTTP(HyperText Transfer Protocol):是用于万维网上信息传输的基础协议。HTTPS(HTTP Secure) 则是在HTTP的基础上加入了SSL/TLS层,用于加密通信。

  • 作用:用于传输网页文档、表单数据、图像等资源。
  • 特点
    • 请求 - 响应模型:客户端发出请求,服务器进行响应。
    • 无状态:每次请求均独立,服务器不会保留之前请求的状态。
    • 安全性:HTTPS通过SSL/TLS来加密数据,保证数据的机密性和完整性。

典型HTTP/HTTPS请求示例

GET /index.html HTTP/1.1
Host: www.example.com

HTTP/1.1 200 OK
Content-Type: text/html

<html>...</html>
3.2.2.2 FTP 协议基础

FTP(File Transfer Protocol) 用于在客户端和服务器之间传输文件,允许文件的上传、下载、删除等操作。

  • 作用:进行文件传输。
  • 特点
    • 双通道通信:控制通道和数据通道分开(21端口用于控制,20端口用于数据)。
    • 身份验证:支持匿名登录和基于用户名、密码的登录。

典型FTP命令示例

USER username
PASS password
LIST
RETR filename
3.2.2.3 SMTP 和 POP3 协议基础

SMTP(Simple Mail Transfer Protocol) 用于邮件发送,POP3(Post Office Protocol 3) 用于邮件接收。

  • SMTP
    • 作用:邮件发送。
    • 特点
      • 服务器间邮件传递
      • 用户名密码验证常用于发送邮件。
    • 典型SMTP命令
      HELO domain.com
      MAIL FROM:<sender@domain.com>
      RCPT TO:<recipient@domain.com>
      DATA
      
  • POP3
    • 作用:邮件接收。
    • 特点
      • 下载邮件到本地并从服务器删除(通常)。
      • 简单且高效,适合于低带宽环境。
    • 典型POP3命令
      USER username
      PASS password
      LIST
      RETR 1
      
3.2.2.4 DNS 协议基础

DNS(Domain Name System):用于将域名解析为IP地址,以便客户端可以找到并连接到服务器。

  • 作用:域名解析。
  • 特点
    • 分层结构:分为根域、顶级域、二级域等。
    • 缓存机制:为了提高查询效率,DNS采用多级缓存。
    • 递归查询与迭代查询:通过多级DNS服务器进行查询。

DNS查询示例

$ nslookup www.example.com
Server:  dns.example.com
Address:  192.0.2.1

Name:    www.example.com
Address:  93.184.216.34

这些协议是网络编程中非常基础和重要的部分。理解这些协议的工作原理和使用方式,对于开发可靠的网络应用程序至关重要。随时深入理解和正确实现这些协议,可以有效避免在实际项目中出现的一些常见错误。

3.2.3 自定义协议

在进行网络编程时,有时需要设计并实现自定义协议,以满足特定应用的需求。自定义协议设计需要考虑到数据的传输、安全、效率等多方面因素。

3.2.3.1 自定义协议设计原则

在设计自定义协议时,有几个基本原则需要遵循:

  • 清晰性:协议应具有清晰的语法和语义,使开发者能快速理解和实现。
  • 扩展性:协议应允许未来的功能扩展,而不破坏现有功能。
  • 安全性:数据传输过程中应考虑加密和校验,以保证数据不被篡改。
  • 高效性:应尽可能减少网络带宽占用,提升数据传输效率。
  • 容错性:协议应能处理各种网络异常情况,如数据丢失、重复和延迟。
3.2.3.2 数据帧格式与解析

设计数据帧格式时需要考虑:

  • 头部信息:包括协议版本、数据类型、序列号等。
  • 长度信息:数据帧的总长度,以便接收方知道何时完成接收。
  • 实际数据:包含业务相关的数据。
  • 校验信息:如校验和,用于校验数据完整性。

示例:自定义协议的数据帧格式

------------------------------
| Version | Type | Length | Payload | Checksum |
------------------------------
| 1 Byte  | 1 Byte | 2 Bytes | Variable | 2 Bytes  |
------------------------------

解析数据帧示例代码

  • 示例代码解析
#include <stdio.h>
#include <stdint.h>
#include <string.h>

// 数据帧结构
#pragma pack(1) // 指定内存对齐为1字节 [1]
typedef struct {
    uint8_t version;      // 版本号 [2]
    uint8_t type;         // 类型 [3]
    uint16_t length;      // 数据长度 [4]
    uint8_t payload[256]; // 载荷数据 [5]
    uint16_t checksum;    // 校验和 [6]
} DataFrame;

// 校验和计算函数
uint16_t calculate_checksum(DataFrame* frame) {
    uint16_t checksum = 0;
    // 累加所有字节
    uint8_t* data = (uint8_t*)frame;
    for (size_t i = 0; i < frame->length + 4; i++) { // 计算除校验和外的所有字节 [7]
        checksum += data[i];
    }
    return checksum;
}

// 数据帧解析函数
int parse_data_frame(uint8_t* data, size_t data_len, DataFrame* frame) {
    if (data_len < 6) { // 基本帧长验证 [8]
        return -1; // 数据不足
    }

    memcpy(frame, data, data_len); // 拷贝数据到帧 [9]
    uint16_t received_checksum = frame->checksum; // 提取收到的校验和 [10]
    frame->checksum = 0; // 清除校验和字段以便计算 [11]

    if (calculate_checksum(frame) != received_checksum) { // 校验和比较 [12]
        return -2; // 校验和错误
    }

    return 0; // 解析成功
}

int main() {
    // 示例数据
    uint8_t data[] = {1, 2, 0, 4, 'h', 'e', 'l', 'l', 0}; // 版本 1, 类型 2, 长度 4, 载荷 "hell"
    DataFrame frame;
    int ret = parse_data_frame(data, sizeof(data), &frame); // 解析数据帧 [13]

    if (ret == 0) {
        printf("解析成功: 版本=%d 类型=%d 长度=%d 数据=%s\n",
               frame.version, frame.type, frame.length, frame.payload);
    } else {
        printf("解析失败: 错误码=%d\n", ret); // 打印错误信息 [14]
    }

    return 0;
}
  • [1] 内存对齐#pragma pack(1) 指令用于设置结构体成员对齐为1字节,以确保数据布局一致,适合于协议或文件读写。
  • [2] 版本号uint8_t version 存储数据帧的版本信息,通常用于区分不同的协议版本。
  • [3] 类型uint8_t type 表示数据帧的类型,可以根据应用进行定义和解读。
  • [4] 数据长度uint16_t length 指示实际有效载荷的字节数。
  • [5] 载荷数据uint8_t payload[256] 用于存储实际传输的数据。
  • [6] 校验和uint16_t checksum 用于数据完整性的验证。
  • [7] 校验和计算:在 calculate_checksum 函数中,以字节累加方式计算校验和,范围涵盖长度+4(version、type、length加实际载荷)。
  • [8] 基本帧长验证:在 parse_data_frame 调用前, 检查数据长度是否足以存储基本帧信息。
  • [9] 拷贝数据到帧:使用 memcpy 函数将源数据复制到数据帧结构中。
  • [10] 提取收到的校验和:存储收到数据的校验和以便后续比较。
  • [11] 清除校验和字段以便计算:将帧中的校验和设为0便于重新计算。
  • [12] 校验和比较:计算当前帧的校验和并与收到的校验和比较以验证数据完整性。
  • [13] 解析数据帧parse_data_frame 函数利用示例数据解析成 DataFrame 结构。
  • [14] 打印错误信息:根据 parse_data_frame 的返回值打印结果或错误信息。
3.2.3.3 常见错误处理机制(超时重传,错误校正)
  • 超时重传:设置一个超时时间,如果在规定时间内未收到应答,则重传数据。
  • 错误校正:如使用前向错误纠正(Forward Error Correction, FEC)技术,在发送数据时添加冗余信息,接收端可以通过冗余信息纠正错误。

超时重传示例代码

  • 示例代码解析
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h> // 引入 sleep 函数

#define TIMEOUT 5 // 超时时间(秒) [1]

// 模拟数据发送函数
bool send_data(uint8_t* data, size_t len) {
    return true; // 假设发送成功
}

// 模拟应答接收函数
bool receive_ack() {
    sleep(3); // 假设3秒后收到应答 [2]
    return true; // 假设成功接收到应答
}

int main() {
    uint8_t data[] = "Hello, world!";
    bool success = false;

    for (int attempt = 0; attempt < 3; attempt++) { // 尝试发送3次 [3]
        printf("发送数据,尝试 %d...\n", attempt + 1);
        if (send_data(data, sizeof(data))) {
            printf("等待应答...\n");
            for (int t = 0; t < TIMEOUT; t++) { // 等待时间循环 [4]
                if (receive_ack()) {
                    success = true; // 收到应答 [5]
                    break;
                }
                sleep(1); // 等待一秒后重试 [6]
            }
        }

        if (success) {
            printf("数据发送成功并收到应答。\n");
            break;
        } else {
            printf("超时未收到应答,重试...\n");
        }
    }

    if (!success) {
        printf("最终数据发送失败。\n");
    }

    return 0;
}
  • [1] 超时时间#define TIMEOUT 5 定义了等待应答的最大时间为 5 秒。在这段时间内,如果没有收到应答则认为超时。

  • [2] 模拟应答延迟:在 receive_ack() 函数中,通过 sleep(3) 模拟延迟 3 秒后收到应答。此处假设在给定时间后,系统能接收到应答。

  • [3] 发送重试机制for (int attempt = 0; attempt < 3; attempt++) 定义了最多重试 3 次发送数据以确保数据成功发送并接收到应答。

  • [4] 等待时间循环:内部的 for (int t = 0; t < TIMEOUT; t++) 用于处理超时,在不超过 timeout 的时间内等待应答。

  • [5] 收到应答:如果 receive_ack() 返回 true,则表示成功接收到应答,设置 success = true 并跳出等待循环。

  • [6] 一秒重试:使用 sleep(1) 让程序在每秒后检查一次是否收到应答,以实现逐秒检查,直到超时。

以上代码展示了如何设计自定义协议,解析数据帧,以及实施常见的错误处理机制。通过自定义协议,开发者可以灵活控制数据传输的各个方面,以满足特定应用的需求。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GPG error golang 1.19

1. 问题描述及原因分析 在飞腾2000的服务器&#xff0c;OS为Kylin Linux Advanced Server release V10环境下&#xff0c;docker版本为18.09.0&#xff08;docker-engine-18.09.0-101.ky10.aarch64&#xff09;&#xff0c;基于容器镜像golang:1.19编译新的容器镜像&#xff0…

C++黑暗迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> #include <cstdlib> #include <ctime> using namespace std; struct near {int i;int ia;int ix;int iy;int iwalk; }; v…

22.1 k8s不同role级别的服务发现

本节重点介绍 : 服务发现的应用3种采集的k8s服务发现role 容器基础资源指标 role :nodek8s服务组件指标 role :endpoint部署在pod中业务埋点指标 role :pod 服务发现的应用 所有组件将自身指标暴露在各自的服务端口上&#xff0c;prometheus通过pull过来拉取指标但是promet…

SQL中基本SELECT语句及常见关键字的使用(内连接,左/右连接)

这里写目录标题 SQL中基本SELECT语句的使用SQL语法简介DDL、DML、DCLSEECT SELECT常用关键词group by分组having筛选limit限定条数UION和UION ALL合并SQL执行顺序 联表查询多表查询示例特殊用法&#xff1a;笛卡尔积&#xff08;交叉连接&#xff09;等值连接vs非等值连接自连接…

VScode 自定义代码配色方案

vscode是一款高度自定义配置的编辑器, 我们来看看如何使用它自定义配色吧 首先自定义代码配色是什么呢? 看看我的代码界面 简而言之, 就是给你的代码的不同语义(类名, 函数名, 关键字, 变量)等设置不同的颜色, 使得代码的可读性变强. 其实很多主题已经给出了定制好的配色方案…

D3.js中国地图可视化

1、项目介绍 该项目来自Github&#xff0c;基于D3.js中国地图可视化。 D3.js is a JavaScript library for manipulating documents based on data. It uses HTML, SVG, and CSS to display data. The full name of D3 is "Data-Driven Documents," which means it a…

【Flume Kafaka实战】Using Kafka with Flume

一 目标 在Cloudera Manager中创建两个Flume的Agent&#xff0c;Agent1从local file中获取内容&#xff0c;写入到kafka的队列中。Agent2以Agent1的sink作为source&#xff0c;将数据从kafka中读取出来&#xff0c;写入到HDFS中。 二 实战 2.1 Kafka Sink 第一步&#xff0…

828华为云征文|部署多功能集成的协作知识库 AFFiNE

828华为云征文&#xff5c;部署多功能集成的协作知识库 AFFiNE 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 AFFiNE3.1 AFFiNE 介绍3.2 AFFiNE 部署3.3 AFFiNE 使用 四、…

Nginx基础详解5(nginx集群、四七层的负载均衡、Jmeter工具的使用、实验验证集群的性能与单节点的性能)

续Nginx基础详解4&#xff08;location模块、nginx跨域问题的解决、nginx防盗链的设计原理及应用、nginx模块化解剖&#xff09;-CSDN博客 目录 14.nginx集群&#xff08;前传&#xff09; 14.1如何理解单节点和集群的概念 14.2单节点和集群的比较 14.3Nginx中的负载均衡…

StopWath,apache commons lang3 包下的一个任务执行时间监视器的使用

StopWath是 apache commons lang3 包下的一个任务执行时间监视器&#xff0c;与我们平时常用的秒表的行为比较类似&#xff0c;我们先看一下其中的一些重要方法&#xff1a; <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependen…

过渡到内存安全语言:挑战和注意事项

开放源代码安全基金会 ( OpenSSF )总经理 Omkhar Arasaratnam 讨论了内存安全编程语言的演变及其为应对 C 和 C 等语言的局限性而出现的现象。 内存安全问题已存在五十多年&#xff0c;它要求程序员从内存管理任务中抽离出来。 Java、Rust、Python 和 JavaScript 等现代语言通…

八大排序详解

文章目录 目录1. 排序的概念及其运用1.1 排序的概念1.2 排序的运用1.3 常见的排序算法 2. 常见排序算法的实现2.1 插入排序2.1.1 基本思想2.1.2 直接插入排序2.1.3 希尔排序 2.2 选择排序2.2.1 基本思想2.2.2 直接选择排序2.2.3 堆排序 2.3 交换排序2.3.1 基本思想2.3.2 冒泡排…

SSL VPN | Easyconnect下载安装使用 (详尽)

EasyConnect是一款远程连接工具&#xff0c;为用户提供简便、快捷的远程访问和控制解决方案。 目录 下载 安装 使用 卸载 下载 通过链接进入官网技术支持板块 深信服技术支持-简单、高效、自助化服务 (sangfor.com.cn)https://support.sangfor.com.cn/ 选择软件下载 在安…

【C语言】指针篇 | 万字笔记

写在前面 在学习C语言过程&#xff0c;总有一个要点难点离不开&#xff0c;那就是大名鼎鼎的C语言指针&#xff0c;也是应为有指针的存在&#xff0c;使得C语言一直长盛不衰。因此不才把指针所学的所有功力都转换成这个笔记。希望对您有帮助&#x1f970;&#x1f970; 学习指…

【2025】基于Hadoop短视频流量数据分析与可视化(源码+文档+调试+答疑)

文章目录 前言一、主要技术&#xff1f;二、项目内容1.整体介绍&#xff08;示范&#xff09;2.运行截图3.部分代码介绍 总结更多项目 前言 随着我国经济的高速发展与人们生活水平的日益提高&#xff0c;人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加快的当下&am…

unix中的exec族函数介绍

一、前言 本文将介绍unix中exec族函数&#xff0c;包括其作用以及使用方法。当一个进程调用fork函数创建一个新进程后&#xff0c;新进程可以直接执行原本正文段的其他内容&#xff0c;但更多时候&#xff0c;我们在一个进程中调用fork创建新的进程后&#xff0c;希望新进程能…

杭州电子科技大学《2019年+2023年861自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《杭州电子科技大学861自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2019年真题 2023年真题 Part1&#xff1a;2019年2023年完整版真题 2019年真题 2…

ubuntu 开启root

sudo passwd root#输入以下命令来给root账户设置密码 sudo passwd -u root#启用root账户 su - root#要登录root账户 root 开启远程访问&#xff1a; 小心不要改到这里了&#xff1a;sudo nano /etc/ssh/ssh_config 而是&#xff1a;/etc/ssh/sshd_config sudo nano /etc/ssh…

猫猫cpu的缓存

原题过长&#xff0c;放一下题目大意 题目大意 给你 m m m 个 1 1 1 到 n n n 之间的整数&#xff0c;你要找到若干个大小为固定的 k k k 的闭区间&#xff0c;使得所有这些数都在你找到的某个区间内。你需要最小化这些区间的并集的大小&#xff0c;并输出此大小。本题里…

基于单片机的两轮直立平衡车的设计

本设计基于单片机设计的两轮自平衡小车&#xff0c;其中机械部分包括车体、车轮、直流电机、锂电池等部件。控制电路板采用STC12C5A60S2作为主控制器&#xff0c;采用6轴姿态传感器MPU6050测量小车倾角&#xff0c;采用TB6612FNG芯片驱动电机。通过模块化编程完成了平衡车系统软…