当前位置: 首页 > news >正文

网络编程基础知识——从基础到实操

1 域名系统

1.1 DNS的构成

域名空间

域名空间是一个树状结构,最顶层是根域(用.表示)。根域下面是顶级域名(Top - Level Domain,TLD),顶级域名分为两类:

  • 通用顶级域名(gTLD):如.com(商业机构)、.org(非盈利组织)、.net(网络服务机构)等。这些域名在早期主要是按照机构的性质来划分的。例如,很多商业公司会选择注册.com域名,像www.amazon.com(亚马逊公司)。
  • 国家代码顶级域名(ccTLD):如.cn(中国)、.us(美国)、.jp(日本)等。这些域名是按照国家或地区划分的,用于标识该国家或地区的机构、企业或个人的网站。比如www.beijing.gov.cn是一个中国北京市的政府网站域名。
    在顶级域名下面还有二级域名、三级域名等。例如www就是一个常见的三级域名,它通常用于标识一个网站的主页面。域名空间的这种层次结构使得域名的管理更加有序,也便于人们理解和记忆。
域名服务器
  • 根域名服务器:根域名服务器是整个DNS体系的顶层,它保存着顶级域名服务器的地址信息。当一个DNS查询请求无法在本地域名服务器中找到答案时,就会向根域名服务器发起查询。全球有13组根域名服务器,它们通过分布式部署来保证系统的稳定性和可靠性。
  • 顶级域名服务器(TLD服务器):它负责管理顶级域名下的域名信息。例如,对于.com顶级域名,其对应的TLD服务器存储了所有以.com结尾的域名的权威信息,包括这些域名对应的二级域名、三级域名等的解析信息。
  • 权威域名服务器:这是为特定域名提供权威解析信息的服务器。一个企业或组织可以有自己的权威域名服务器,用于管理自己域名下的所有记录,如主机记录(A记录)、邮件交换记录(MX记录)等。例如,一个公司有自己的域名example.com,它在自自己的权威域名服务器上设置了mail.example.com对应的IP地址,用于邮件服务器的解析。
  • 本地域名服务器(递归域名服务器):这是用户最常接触的域名服务器。当用户在浏览器中输入一个网址时,本地域名服务器会首先查看自己的缓存,如果缓存中有该域名对应的IP地址,就会直接返回结果。如果没有,它会按照一定的顺序向其他域名服务器(如根域名服务器、TLD服务器等)查询,直到获取到正确的IP地址并返回给用户。

工作原理

  • 递归查询
    当用户向本地域名服务器发起域名解析请求时,本地域名服务器会负责完成整个查询过程。如果本地域名服务器的缓存中没有该域名的解析记录,它会向根域名服务器发送查询请求。根域名服务器会告诉本地域名服务器应该向哪个TLD服务器查询。本地域名服务器再向TLD服务器查询,TLD服务器会告诉它向权威域名服务器查询。最后,权威域名服务器返回域名对应的IP地址给本地域名服务器,本地域名服务器再将这个结果返回给用户。在整个过程中,用户只需要等待本地域名服务器完成查询并返回结果,用户端不需要进行多次查询
  • 迭代查询
    在迭代查询中,本地域名服务器只是将查询请求转发给根域名服务器,根域名服务器会告诉本地域名服务器应该向哪个TLD服务器查询,然后本地域名服务器再自己去向TLD服务器查询。如果TLD服务器也没有该域名的解析记录,它会告诉本地域名服务器向权威域名服务器查询。这种查询方式要求用户端的设备(如计算机)自己去跟踪查询过程,直到获取到最终的IP地址。相比递归查询,**迭代查询对本地域名服务器的资源占用较少,但用户端设备需要做更多的工作 **

记录类型

记录类型功能描述示例
A记录将域名映射为IPv4地址。www.example.com192.0.2.45
AAAA记录将域名映射为IPv6地址。www.example.com2001:0db8:85a3:0000:0000:8a2e:0370:7334
CNAME记录将一个域名指向另一个域名。blog.example.comwww.example.com
MX记录指定域名的邮件服务器。example.commail.example.com
NS记录指定域名的权威域名服务器。example.comns1.example.comns2.example.com
TXT记录包含任意文本信息,用于域名验证、反垃圾邮件等。SPF记录:v=spf1 include:_spf.google.com ~all

DNS编程基础

#include <iostream>
#include <boost/asio.hpp>

int main() {
    boost::asio::io_context io_context;
    boost::asio::ip::tcp::resolver resolver(io_context);

    // 解析域名
    auto endpoints = resolver.resolve("www.example.com", "http");

    // 遍历解析结果
    for (auto& endpoint : endpoints) {
        std::cout << "IP Address: " << endpoint.endpoint().address().to_string() << std::endl;
    }

    return 0;
}

在Boost.Asio中,resolver.resolve方法用于同步解析域名,并返回一个包含解析结果的迭代器范围。这个范围是一个boost::asio::ip::tcp::resolver::results_type类型的对象,它是一个迭代器范围,包含了所有可能的解析结果(即IP地址和端口号的组合)

auto endpoints = resolver.resolve("www.example.com", "http");

resolver:这是一个boost::asio::ip::tcp::resolver对象,用于执行域名解析。
resolve:这是resolver对象的成员函数,用于同步解析域名。
第一个参数是域名(如"www.example.com")。
第二个参数是服务名(如"http"),它通常是一个端口号的字符串表示。对于HTTP服务,默认端口号是80。
endpoints:这是解析结果,类型为boost::asio::ip::tcp::resolver::results_type。它是一个迭代器范围,包含了所有可能的解析结果。
results_type 的结构
results_type是一个迭代器范围,每个迭代器指向一个boost::asio::ip::tcp::resolver::iterator,而每个迭代器又包含一个boost::asio::ip::tcp::endpoint对象。endpoint对象包含了IP地址和端口号的信息。

进程

定义

**进程(Process)**是计算机科学中的一个核心概念,它是指计算机程序在执行时的一个实例。简单来说,进程是程序运行时的动态实体,它包含了程序的代码、数据、运行时的上下文信息(如寄存器状态、堆栈信息等)以及操作系统分配给它的资源(如内存、文件句柄

进程与程序的区别
  • 程序(Program):程序是一组指令的集合,是静态的代码和数据的组合,存储在磁盘或其他存储介质中。
  • 进程(Process):进程是程序在执行时的一个实例,是动态的,包含了程序运行时的状态和资源分配信息。

进程的状态

进程在其生命周期中会经历多种状态,这些状态反映了进程在操作系统调度中的行为。

    1. 新建(New)
      进程刚刚被创建,但尚未被调度运行。此时进程处于初始化阶段,操作系统正在为其分配资源。
    1. 就绪(Ready)
      进程已经准备好运行,但正在等待CPU时间片。就绪状态的进程被放入就绪队列中,等待调度器分配CPU资源。
    1. 运行(Running)
      进程正在CPU上执行。一个系统中可能有多个就绪状态的进程,但只有一个进程处于运行状态(单核CPU)。
    1. 阻塞(Blocked)
      进程因为等待某些事件(如I/O操作完成、信号量释放等)而暂时无法运行。阻塞状态的进程不会被调度器分配CPU时间,直到它等待的事件发生。
    1. 终止(Terminated)
      进程完成运行或因错误而终止。终止状态的进程将被操作系统回收资源,其状态信息将被清除。

僵尸进程

僵尸进程的产生原因
  • 子进程先于父进程结束:
    当子进程完成执行后,操作系统会保留其状态信息(如退出状态码、资源使用情况等),直到父进程通过wait()或waitpid()等系统调用读取这些信息。
    二、僵尸进程的问题
  • 资源占用:
    僵尸进程会占用系统资源,如进程表条目、文件描述符等。如果僵尸进程过多,可能会导致系统资源耗尽。
  • 无法被终止:
    僵尸进程不能被kill命令或任何其他信号终止,因为它们已经“死亡”,只是状态信息尚未被清理。
  • 影响系统性能:
    僵尸进程的存在可能会影响系统的性能和稳定性,尤其是在资源有限的系统上。

编程基础

1.创建进程

#include <unistd.h>

pid_t fork(void);

返回值:
在父进程中返回子进程的PID。
在子进程中返回0。
如果出错,返回-1。

2.wait()函数
父进程调用wait()等待子进程结束,避免子进程成为僵尸进程

3.示例

#include<iostream>
 #include<unistd.h> //unix标准文件
   int main()
   {
       using namespace std;
       pid_t pid;
      cout<<"parent have!"<<endl;
           pid = fork();
       if(pid == -1)//错误创建
       {
           perror("fork error");
             _exit(1);
       }
       else if(pid == 0)//子进程
       {

           cout<<"i am child,pid = "<<getpid()<<" my parent is:"<<getppid()<<endl;
       }
       else//父进程
       {
          
           cout<<"i am parent,pid = "<<getpid()<<" my parent is:"<<getppid()<<endl;
            // 等待子进程结束
           wait(nullptr);
       }
       cout<<"both have!"<<endl;
       return 0;
   }

信号

定义

信号是一种软件中断机制,用于通知进程某些事件的发生。信号可以由操作系统、其他进程或进程自身发送给目标进程。当信号到达时,目标进程可以选择忽略信号、捕获信号并执行自定义的处理函数,或者执行默认的处理动作。

信号的类型

信号通过一个正整数来标识,每个信号都有一个名称和一个默认的处理动作。以下是一些常见的信号及其默认行为:

信号名称信号编号默认行为描述
SIGINT2终止进程用户通过键盘中断(通常是Ctrl+C)发送给进程
SIGTERM15终止进程请求进程终止
SIGKILL9强制终止进程强制终止进程,不能被忽略或捕获
SIGSEGV11终止进程并生成核心转储进程访问非法内存时发送
SIGCHLD17/20忽略子进程结束时发送给父进程
SIGALRM14终止进程alarm系统调用设置的定时器到期时发送
SIGUSR130/10终止进程用户自定义信号1
SIGUSR231/12终止进程用户自定义信号2
SIGQUIT3终止进程并生成核心转储用户通过键盘(通常是Ctrl+\)发送给进程
SIGSTOP19/17暂停进程暂停进程
SIGCONT18/19继续进程继续暂停的进程
SIGTSTP20/18暂停进程用户通过键盘(通常是Ctrl+Z)发送给进程
信号的默认行为

每个信号都有一个默认的处理动作,这些动作由操作系统定义。常见的默认行为包括:
终止进程:

  • SIGINT、SIGTERM、SIGUSR1、SIGUSR2等信号的默认行为是终止进程。
    进程在接收到这些信号后会立即退出。
    强制终止进程:
  • SIGKILL信号的默认行为是强制终止进程,不能被忽略或捕获。
    进程在接收到SIGKILL信号后会立即退出,且不会执行任何清理操作。
    终止进程并生成核心转储:
  • SIGSEGV、SIGABRT等信号的默认行为是终止进程并生成核心转储文件。
    核心转储文件包含了进程的内存映像,可以用于调试。
    忽略信号:
  • SIGCHLD信号的默认行为是被忽略。
    父进程不会接收到子进程结束的信号,除非父进程显式地注册了信号处理函数。
    暂停进程:
  • SIGSTOP信号的默认行为是暂停进程。
    进程在接收到SIGSTOP信号后会暂停执行,直到接收到SIGCONT信号。
    继续进程:
  • SIGCONT信号的默认行为是继续暂停的进程。
    进程在接收到SIGCONT信号后会继续执行。
进程可以对信号进行以下几种处理:
  • 忽略信号:
    进程可以选择忽略某些信号,但有些信号(如SIGKILL和SIGSTOP)不能被忽略。
    例如,可以通过signal(SIGINT, SIG_IGN)忽略SIGINT信号。
  • 捕获信号:
    进程可以注册一个信号处理函数,当信号到达时,执行自定义的处理逻辑。
    例如,可以通过signal(SIGINT, handle_sigint)注册一个信号处理函数handle_sigint。
  • 执行默认动作:
    如果进程没有对信号进行特殊处理,操作系统会执行信号的默认动作。
    例如,如果进程没有捕获SIGINT信号,操作系统会终止进程。
  • 示例
#include <iostream>
#include <csignal>
#include <unistd.h>

// 信号处理函数
void handle_sigint(int sig) {
    std::cout << "Received SIGINT (Ctrl+C). Exiting gracefully." << std::endl;
    exit(0);
}

int main() {
    // 注册信号处理函数
    std::signal(SIGINT, handle_sigint);

    std::cout << "Process is running. Press Ctrl+C to send SIGINT." << std::endl;

    // 模拟长时间运行的进程
    while (true) {
        sleep(1);
    }

    return 0;
}
信号的发送
  • kill系统调用
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

pid是目标进程的PID,sig是要发送的信号编号。

信号的阻塞

进程可以通过sigprocmask系统调用阻塞某些信号,使它们暂时不会被处理。阻塞的信号会在适当的时候被处理,但不会立即中断进程的正常执行。

  • 信号掩码
    信号掩码是一个位掩码,用于表示哪些信号被阻塞。每个信号对应一个位,如果该位被设置为1,则表示该信号被阻塞。
#include <csignal>
#include <cerrno>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how:指定如何修改信号掩码,可以是以下值之一:
SIG_BLOCK:将set中的信号添加到当前信号掩码中。
SIG_UNBLOCK:从当前信号掩码中移除set中的信号。
SIG_SETMASK:将当前信号掩码设置为set。
set:指向要设置的信号集。
oldset:指向存储旧信号掩码的变量。

示例

#include <iostream>
#include <csignal>
#include <unistd.h>

int main() {
    // 创建信号集
    sigset_t set;
    sigemptyset(&set); // 初始化信号集
    sigaddset(&set, SIGINT); // 添加SIGINT信号到信号集

    // 阻塞SIGINT信号
    if (sigprocmask(SIG_BLOCK, &set, nullptr) == -1) {
        perror("sigprocmask");
        return 1;
    }

    std::cout << "SIGINT is blocked. Press Ctrl+C to test." << std::endl;

    // 模拟长时间运行的进程
    sleep(10);

    // 解除阻塞SIGINT信号
    if (sigprocmask(SIG_UNBLOCK, &set, nullptr) == -1) {
        perror("sigprocmask");
        return 1;
    }

    std::cout << "SIGINT is unblocked. Press Ctrl+C to test." << std::endl;

    // 模拟长时间运行的进程
    while (true) {
        sleep(1);
    }

    return 0;
}

进程间的通信(管道)

定义

管道是一种简单的进程间通信机制,用于在进程之间传递数据。管道可以分为两种类型:

  • 匿名管道(Anonymous Pipes):通常用于父子进程之间的通信。
  • 命名管道(Named Pipes,也称为FIFO):可以在不相关的进程之间通信。

匿名管道

特点

  • 单向通信:数据只能在一个方向上流动。
  • 仅限于相关进程:通常用于父子进程之间的通信。
  • 简单易用:通过pipe()系统调用创建。
    创建匿名管道
    #include <unistd.h> int pipe(int pipefd[2]);

pipefd[0]:管道的读端文件描述符。
pipefd[1]:管道的写端文件描述符。
返回值:成功返回0,失败返回-1。
示例:

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>

int main() {
    int parent_to_child[2];
    int child_to_parent[2];

    // 创建两个管道
    if (pipe(parent_to_child) == -1 || pipe(child_to_parent) == -1) {
        perror("pipe");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        close(parent_to_child[1]); // 关闭父进程到子进程的写端
        close(child_to_parent[0]); // 关闭子进程到父进程的读端

        char buffer[80];
        // 从父进程读取数据
        read(parent_to_child[0], buffer, sizeof(buffer));
        std::cout << "Child process received from parent: " << buffer << std::endl;

        // 向父进程发送数据
        const char *msg = "Hello from child\n";
        write(child_to_parent[1], msg, strlen(msg));

        close(parent_to_child[0]);
        close(child_to_parent[1]);
    } else {
        // 父进程
        close(parent_to_child[0]); // 关闭父进程到子进程的读端
        close(child_to_parent[1]); // 关闭子进程到父进程的写端

        const char *msg = "Hello from parent\n";
        // 向子进程发送数据
        write(parent_to_child[1], msg, strlen(msg));

        // 从子进程读取数据
        char buffer[80];
        read(child_to_parent[0], buffer, sizeof(buffer));
        std::cout << "Parent process received from child: " << buffer << std::endl;

        close(parent_to_child[1]);
        close(child_to_parent[0]);

        wait(nullptr); // 等待子进程结束
    }

    return 0;
}

命名管道

1.特点

  • 双向通信:虽然管道本身是单向的,但可以通过创建两个管道实现双向通信。
  • 不相关进程:可以在不相关的进程之间通信。
  • 基于文件系统:命名管道在文件系统中有一个特殊文件,可以通过文件名访问。
    2.创建
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
  • pathname:命名管道的路径名。
  • mode:文件权限,类似于open()中的权限。
    返回值:成功返回0,失败返回-1。
    3.使用
  • 写操作:使用open()打开管道文件,然后使用write()写入数据。
  • 读操作:使用open()打开管道文件,然后使用read()读取数据。

4 示例

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    const char *fifo_path1 = "/tmp/fifo1";
    const char *fifo_path2 = "/tmp/fifo2";

    // 创建命名管道
    if (mkfifo(fifo_path1, 0666) == -1 && errno != EEXIST) {
        perror("mkfifo");
        return 1;
    }
    if (mkfifo(fifo_path2, 0666) == -1 && errno != EEXIST) {
        perror("mkfifo");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子进程
        // 打开管道
        int fd1 = open(fifo_path1, O_RDONLY);
        if (fd1 == -1) {
            perror("open");
            return 1;
        }

        // 读取数据
        char buffer[80];
        read(fd1, buffer, sizeof(buffer));
        std::cout << "Child process received from parent: " << buffer << std::endl;

        close(fd1);

        // 打开管道
        int fd2 = open(fifo_path2, O_WRONLY);
        if (fd2 == -1) {
            perror("open");
            return 1;
        }

        // 写入数据
        const char *msg = "Hello from child\n";
        write(fd2, msg, strlen(msg));

        close(fd2);
    } else {
        // 父进程
        // 打开管道
        int fd1 = open(fifo_path1, O_WRONLY);
        if (fd1 == -1) {
            perror("open");
            return 1;
        }

        // 写入数据
        const char *msg = "Hello from parent\n";
        write(fd1, msg, strlen(msg));

        close(fd1);

        // 等待子进程的响应
        int fd2 = open(fifo_path2, O_RDONLY);
        if (fd2 == -1) {
            perror("open");
            return 1;
        }

        char buffer[80];
        read(fd2, buffer, sizeof(buffer));
        std::cout << "Parent process received from child: " << buffer << std::endl;

        close(fd2);

        wait(nullptr); // 等待子进程结束
    }

    return 0;
}

相关文章:

  • 常见框架漏洞(一)----Thinkphp(TP)
  • Android之卡片式滑动
  • 零基础上手Python数据分析 (9):DataFrame 数据读取与写入 - 让数据自由穿梭
  • 基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!
  • HarmonyOS-ArkUI Grip组件
  • Charles汉化步骤 charles中文版怎么用
  • 凝视型高光谱相机:钻石光谱分析研究与应用
  • PoE交换机如何助力智慧城市基础设施建设?
  • C# 如何检查给定的四个点是否形成一个正方形(How to check if given four points form a square)
  • docker ssh远程连接
  • uni app跨端开发遇到的问题
  • Linux搭建本地时间服务器及时间同步
  • mysql中show命令的使用
  • react-activation 实现页面保活记录
  • 前端模拟 websocket 请求小工具
  • mac vim命令快捷键
  • LeetCode热题100精讲——Top7:接雨水【双指针】
  • 树莓派5-GPIO和40针引脚
  • redis使用
  • 手动创建kkFileView4.4.0镜像
  • 网站优化工具/公众号排名优化软件
  • 一级a做爰片视频免费观看网站/网站seo报价
  • 大连手机自适应网站建设电话/互联网营销师考试题及答案
  • 美国新闻网站app/西安今天出大事
  • 来客seo/沈阳网络seo公司
  • 搜索引擎优化哪些方面/五年级上册优化设计答案