C开源项目-TinyHttp解读(上)

2023-10-26

项目简介

此项目可以自行在GitHub上进行下载,作者的ReadMe文档也写得是比较详细的。这里用到了较多的Unix编程,不会的可以一点一点去查。
(实名感谢我们家杰佬发给我的APUE,可以当字典查)

项目内容

其实就是实现了一个轻量级服务器的功能,同时源代码也包含了一个simpleclient的文件,用于生成一个用户端。这和计网课上的用Java写TCP/UDP很像,但C会显得更麻烦一些,但总认为基于Unix给人一种高大上的感觉。

代码分析

我这儿就直接按照作者在README中给出的阅读顺序来浏览了。
由于下边都是我没动过的源码,是存在一些问题的,比如我用Ubuntu21打开文件时就提示我说u_short已经不能用啦,全部改为unsigned short即可哈。

  1. main函数分析
int main(void)
{
    int server_sock = -1;
    u_short port = 4000;
    int client_sock = -1;
    struct sockaddr_in client_name;
    socklen_t  client_name_len = sizeof(client_name);
    pthread_t newthread;

    server_sock = startup(&port);
    printf("httpd running on port %d\n", port);

    while (1)
    {
        client_sock = accept(server_sock,
                (struct sockaddr *)&client_name,
                &client_name_len);
        if (client_sock == -1)
            error_die("accept");
        /* accept_request(&client_sock); */
        if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
            perror("pthread_create");
    }

    close(server_sock);

    return(0);
}

这里面直接来定义一些东西实际上是难以理解的,但总体来看还是可以根据英语,结合计算机网络的知识来进行解读。

大家都知道socket是套接字,我们的计算机网络通信机制用的就是socket编程,socket是留给我们开发人员的一个接口。这个接口之强大就在于其帮助我们屏蔽了计算机网络底层的内容,我们无需和那些繁琐的协议(虽然等会一些参数会用到)甚至是那些底层物理结构直接打交道。

先来看变量申明:

    int server_sock = -1;
    u_short port = 4000;
    int client_sock = -1;
    struct sockaddr_in client_name;
    socklen_t  client_name_len = sizeof(client_name);
    pthread_t newthread;

    server_sock = startup(&port);
    printf("httpd running on port %d\n", port);

可以看到我们分别为服务器和客户端都申请了一个套接字并且初始化的时候都为-1,等会我们会用了另外的值替换。
申明端口号4000,这个不难理解。
下述牵扯到地址格式的问题,可以翻阅APUE的16.3.2直接进行学习。
随后我们申明了一个结构体,sockaddr_in,这个可能有必要需查看一下他的定义。
请添加图片描述
首先官方就交代了,这是一个拿来描述Internet Socket Address的结构体。这就需要发挥计算机网络的知识了,如何来确定这么一个socket的地址呢?
它需要你的IP地址,端口号,协议族,同时此结构体的最后还声明了要针对结构体sockaddr来进行补齐‘0’操作。

那么这俩地址间的关系是什么呢,根据我们的“字典”,sockaddr_in是ipv4域的一个地址结构,sockaddr就是一个统一的可以传入套接字使用的通用地址结构!
所以,综上,应该可以明白为什么有一个padding填充值了吧!

多线程你那个pthread可以先不予理睬,因为作者已经说了这个是需要comment out的!所以我们在Linux环境下make的时候需要先注释掉!

最后就会发现我们调用了一个startup函数用以决定服务器的socket字了,其为int型的主要原因就是unix视万物为文件,这个套接字实际上就是一个文件描述符。

有了套接字就好办了,我们来分析剩下的内容:

 while (1)
    {
        client_sock = accept(server_sock,
                (struct sockaddr *)&client_name,
                &client_name_len);
        if (client_sock == -1)
            error_die("accept");
        /* accept_request(&client_sock); */
        if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
            perror("pthread_create");
    }

    close(server_sock);

不难看出这是一个死循环,原因也很简单,服务器需要一直处于打开状态监听客户端的请求并且与其建立连接。

这段代码中主要就是几个函数的理解。
1.error_die函数,这个不是什么内置api,是作者自己手写的一个函数,定义如下:

/* Print out an error message with perror() (for system errors; based
 * on value of errno, which indicates system call errors) and exit the
 * program indicating an error. */
/**********************************************************************/
void error_die(const char *sc)
{
    perror(sc);
    exit(1);
}

很清晰明了,就是打印出错误信息然后退出程序,就那么简单。

2.accept函数
在这里插入图片描述
首先还是看用途,我就用自己拙劣的英语翻译一下:
就是说等待到(服务器)套接字的一个连接。当一个连接请求到达时,会开辟一个新的套接字用于进行通信。所以这个函数最后就会返回一个新的套接字(描述符),也就是我们的客户端套接字。
然后就是参数设置问题,第一个就是我们的服务器套接字描述符,也就是server_sock,第二第三个参数就应该是客户端本身的地址和长度。

结束后我们就会进行一个判断,因为如果套接字仍旧是-1的,说明出错了!

哦对了,我们看README文件会发现需要把pthread多线程这一块注释掉的,我只是单纯把源文件拷贝过来,其实这个函数就无需分析了哈!
3.accept_request()函数。
这个函数有点长们我们等会再来细细看。

  1. startup()函数。
    在上面我们已经讲过,startup函数为服务器指定了一个套接字,我们就来看看这是如何来生成的吧!
/* This function starts the process of listening for web connections
 * on a specified port.  If the port is 0, then dynamically allocate a
 * port and modify the original port variable to reflect the actual
 * port.
 * Parameters: pointer to variable containing the port to connect on
 * Returns: the socket */
/**********************************************************************/
int startup(u_short *port)
{
    int httpd = 0;
    int on = 1;
    struct sockaddr_in name;

    httpd = socket(PF_INET, SOCK_STREAM, 0);
    if (httpd == -1)
        error_die("socket");
    memset(&name, 0, sizeof(name));
    name.sin_family = AF_INET;
    name.sin_port = htons(*port);
    name.sin_addr.s_addr = htonl(INADDR_ANY);
    if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)  
    {  
        error_die("setsockopt failed");
    }
    if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
        error_die("bind");
    if (*port == 0)  /* if dynamically allocating a port */
    {
        socklen_t namelen = sizeof(name);
        if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
            error_die("getsockname");
        *port = ntohs(name.sin_port);
    }
    if (listen(httpd, 5) < 0)
        error_die("listen");
    return(httpd);
}

作者的代码思路也很清晰了,就是说这个函数启动了监听某个特定端口的过程。
端口号如果为0,就动态地分配一个然后要修正原端口的信息以体现实际端口。

httpd就是充当了server_sock,它是由socket函数生成的,最后会返回此值。
接下来就可以来学习一下socket函数。
第一个参数是domain,其实就是协议族,我们在这里指定了PF_INET = AF_INET指定的是IPV4网域,还有一些其他的选项可以找另外的博客借鉴哈。
第二个参数是type,套接字类型,我们选择STREAM其实就是字节流传输,这种方式的特点就是我们说的TCP传输的特点。
第三个参数是协议类型,这个和第一个不大一样,指的是domain和type之间的protocol。设为0的话就会有一个默认值,我们这样的设置基本会默认为“TCP”。
最后如果此函数返回了-1说明失败了。

然后就是对我们上面认识过的ipv4地址的结构体进行设置。设置协议族,端口号,地址。这里涉及了htons和htonl函数都是和地址有关的,htons是将传入的参数返回“网络字节序”的16位整数,htonl是返回“网络字节序”的32位整数。
这里涉及的就是大小端表示法,而我们的网络字节序基本就是用大端法来处理。所谓大端法就是说低有效字节存放于高内存地址,给张计系(CSAPP)用的图:
在这里插入图片描述

其次就是setsockopt函数,这个函数用于设置socket的状态。如果只是想了解这个函数怎么设置的话可以看这篇,setsockopt参数设置这个函数自己也没仔细了解过就先不画蛇添足了。

接下来就是bind函数,用于关联套接字和地址。这个通常只是服务器需要的步骤,绑定一个ip地址+端口号,客户端并不需要如上步骤。原因很简单,服务器需要处于一个众所周知的位置,绑定就能很好的实现这一点,而客户端只需主机随机分配即可。
此函数的参数也并不困难,socket描述符 + 地址结构体(由ipv4的sockaddr_in进行转换得到) + socket长度。

随后就到了端口号为0时的情景,此时我们会动态分配一个端口号。
用到的是getsockname函数。

最后就让其处于listen(监听)状态,第二个参数的含义是可排队的最大连接个数。我们自己的电脑垃圾,大公司的服务器可接受的连接数那肯定是远远大于这个5的。

  1. accept_request()函数分析
/**********************************************************************/
/* A request has caused a call to accept() on the server port to
 * return.  Process the request appropriately.
 * Parameters: the socket connected to the client */
/**********************************************************************/
void accept_request(void *arg)
{
    int client = (intptr_t)arg;
    char buf[1024];
    size_t numchars;
    char method[255];
    char url[255];
    char path[512];
    size_t i, j;
    struct stat st;
    int cgi = 0;      /* becomes true if server decides this is a CGI
                       * program */
    char *query_string = NULL;

    numchars = get_line(client, buf, sizeof(buf));
    i = 0; j = 0;
    while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
    {
        method[i] = buf[i];
        i++;
    }
    j=i;
    method[i] = '\0';

    if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    {
        unimplemented(client);
        return;
    }

    if (strcasecmp(method, "POST") == 0)
        cgi = 1;

    i = 0;
    while (ISspace(buf[j]) && (j < numchars))
        j++;
    while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
    {
        url[i] = buf[j];
        i++; j++;
    }
    url[i] = '\0';

    if (strcasecmp(method, "GET") == 0)
    {
        query_string = url;
        while ((*query_string != '?') && (*query_string != '\0'))
            query_string++;
        if (*query_string == '?')
        {
            cgi = 1;
            *query_string = '\0';
            query_string++;
        }
    }

    sprintf(path, "htdocs%s", url);
    if (path[strlen(path) - 1] == '/')
        strcat(path, "index.html");
    if (stat(path, &st) == -1) {
        while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
            numchars = get_line(client, buf, sizeof(buf));
        not_found(client);
    }
    else
    {
        if ((st.st_mode & S_IFMT) == S_IFDIR)
            strcat(path, "/index.html");
        if ((st.st_mode & S_IXUSR) ||
                (st.st_mode & S_IXGRP) ||
                (st.st_mode & S_IXOTH)    )
            cgi = 1;
        if (!cgi)
            serve_file(client, path);
        else
            execute_cgi(client, path, method, query_string);
    }

    close(client);
}

此函数意图很明显,就是用于处理连接请求的。
那些变量可以通过推导获取其含义,直接对着关键函数进行分析即可。

1.get_line函数。

/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline,
 * carriage return, or a CRLF combination.  Terminates the string read
 * with a null character.  If no newline indicator is found before the
 * end of the buffer, the string is terminated with a null.  If any of
 * the above three line terminators is read, the last character of the
 * string will be a linefeed and the string will be terminated with a
 * null character.
 * Parameters: the socket descriptor
 *             the buffer to save the data in
 *             the size of the buffer
 * Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
int get_line(int sock, char *buf, int size)
{
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(sock, &c, 1, MSG_PEEK);
                /* DEBUG printf("%02X\n", c); */
                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    buf[i] = '\0';

    return(i);
}

注释里也说了,就是从socket套接字获取一行内容,这个一行可以是以【“换行\n”,可以是“回车\r”也可以是CRLF(回车换行)】结尾的。
这个函数其实主要就是做逻辑上的处理,但要完全阅读还得先看看recv究竟干了些啥。recv就是从传入的socket套接字处[客户端套接字,因为这个get_line函数是服务器端在调用的]取内容,存于缓冲区buffer处,大小为size,最后一个参数是读取的模式标志。此函数有一个整型返回值,若为0则说明已无可用数据或者连接已结束,-1代表出错,其他情况代表了真正读取到的字符数。

再回过头来看get_line的逻辑,循环体内,先读取一个字节的内容。
<=0说明失败,将c设为换行符下一轮就可以直接退出。
否则就对读取到的内容进行判定:
不是回车符就直接写入缓冲区,指针右移,这种情况如果读到的是换行符那么下一轮也同样直接就结束了
若是回车符的话,仍旧是接收一个字节的内容只不过此处用了标志MSG_PEEK,这样一来可以做到只是查看下一个要读取的数据内容而不是真正取走它!
接下来我们继续进行判断如果下一个又是\n或者说没有下一个了那么下一轮也是会直接退出的!

其实作者写那么多,大家好好理解一下,就是把我们的三种行结尾方式均进行了判定,其余情况才会在缓冲区进行写入,就那么简单!

tinyhttp项目阅读(上)小结

以上分析的内容并没有太多花里胡哨的技巧,没有什么高深莫测的算法,但需要编程人员熟悉Unix环境下的网络编程,需要扎实的C语言功底和一定的计算机网络的知识!

一篇写太多有点难以阅读,(下)晚上更完!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C开源项目-TinyHttp解读(上) 的相关文章

  • 按顺序发送大 UDP 数据包的最佳方法是什么

    我有一个 Android 应用程序 需要每 100 毫秒通过 UDP 协议发送数据 每个UDP数据包平均有15000字节 数据包以广播方式发送 下面的每 100 毫秒行就会运行一次循环 DatagramPacket sendPacket n
  • 如何用C语言创建UDP服务器?

    我正在尝试用 C 语言 在 Linux 下 编写一个 UDP 服务器 我知道在socket 我必须使用的功能SOCK DGRAM并不是SOCK STREAM if list s socket AF INET SOCK DGRAM IPPRO
  • 在 iOS 中跨应用程序(后台/前台或分屏多任务处理)共享 UDP 数据

    我正在编写一个研究应用程序 该应用程序利用通过 UDP 广播数据的特定传感器 有各种商业应用程序使用此传感器 我们希望能够同时运行我们的应用程序和现有应用程序 在过去 iOS 8 及更低版本 我们会在后台运行应用程序来记录数据 并在前台运行
  • memcached 使用 Django 监听 UDP

    Question 我无法获得memcached正在听UDP 上班 get set delete 与姜戈 我只让 memcached 监听UDP 11211 正如我在上一个问题 https stackoverflow com question
  • 如何使用 ZeroMQ 处理原始 UDP?

    我有一个客户 我无法更改其代码 但我想使用 重新 编写ZeroMQ插座 客户使用原始TCP和原始的UDP插座 我知道我可以使用ZMQ ROUTER RAW对于生的TCP插座 但是原始的怎么样 UDP数据流 ZeroMQ 中对 UDP 的支持
  • Android 上的 UDP 视频流

    我有一个 Android 项目 需要构建一个客户端应用程序来接收 UDP 或 RTP 单播视频流并播放它们 不幸的是 我似乎无法使其正常工作 并且已经广泛搜索了解决方案 我已经在 Xoom Android 3 2 和 Nexus S And
  • 具有多个接口的 Python UDP 套接字

    我正在 Windows XP 机器上用 python2 7 编写脚本 本机使用不同的网卡连接到多个网络 我遇到了一个问题 我已将 UDP 套接字绑定到特定接口 我知道您可以通过仅提供网卡现有的 IP 地址来在 Windows 中完成此操作
  • 错误的 UDP 校验和没有效果:为什么?

    我正在尝试测试 UDP 程序 如果它接收到 UDP 校验和错误的数据 会发生什么情况 奇怪的是 它似乎没有任何效果 并且有效负载被成功接收 至少在 OS X 上是通过环回接口成功接收的 下面是一个示例 其中使用以下方式发送数据SOCK RA
  • 用于接收 UDP 数据包的可变大小缓冲区

    我有一个 UDP 套接字 它将接收一些可能不同大小的数据包 并且我异步处理它 socket async receive from boost asio buffer buffer 65536 senderEndpoint handler 这
  • 通过 Internet 发送 UDP 数据包

    我正在尝试了解 P2P 去中心化网络的一些细节 我的问题如下 假设我有两台名为 comp1 和 comp2 的机器 现在 comp1 设置在我的家庭网络中的路由器后面 comp2 位于我的办公室中 也位于路由器后面 我是否可以像这样在 In
  • 接收来自 N 个客户端的响应,以回复通过 UDP 的广播请求

    我正在为特定类型的网络多媒体设备实现一种 IP 查找器 我想找出 LAN 中该类型的所有活动设备及其 IP 地址和其他详细信息 设备有自己的设备发现方式 其工作原理如下 客户端通过 UDP 通过 LAN 发送广播请求 目的端口号是固定的 作
  • 将 Docker 容器连接到网络接口/设备而不是 IP 地址

    经过仔细的研究 测试和摆弄 我只能找到通过从 IP 端口转发来将 Docker 容器连接到给定接口的方法 这可以通过添加来完成 p Host IP Host Port Container Port to a docker run命令 我有一
  • 当网络上的所有计算机都具有相同的公共IP地址时,如何向特定计算机发送UDP数据包? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 这就是问题 它非常简单 理解 我家里有 2 台电脑 它们都有相同的公共 IP 地址 例如 1 2 3 4 我在咖啡馆有一台计算机 不同的网络 因此它具
  • UDP sendto 上的 ECONNREFUSED 错误

    我在使用正在写入的应用程序时遇到一些无法解释的行为 使用 sendto 向多个端口发送 UDP 数据 所有端口均使用套接字 PF INET SOCK DGRAM 0 为了一组客户端读取进程的利益 这些 sendto 偶尔会不可预测地触发经济
  • Go:如何接收整个 UDP 数据报

    我的问题 使用 net Read 方法仅复制给定字节数组或切片大小的字节数 当然 我不想每次都分配最大 64 kB 的 UDP 数据报 有没有go如何确定数据报的大小 位于数据报头中 或再次读取直到数据报完全读取 Try 从UDP读取 ht
  • C# 广播是UDP消息,监听多个回复

    我正在尝试编写一些执行 UDP 广播的代码 然后侦听来自远程服务器的答复 说明它们存在 它用于识别子网上运行服务器应用程序的计算机 因此基本上会发出 谁在那儿 并听取所有答复 我在 Java 中有这个 工作完美 它将 DatagramPac
  • 如何在QT中发送和接收UDP数据包

    我正在 QT 中编写一个小型应用程序 它通过本地网络发送广播 UDP 数据包 并等待来自网络上的一个或多个设备的 UDP 响应数据包 创建套接字并发送广播数据包 udpSocketSend new QUdpSocket this udpSo
  • 什么是消息边界?

    什么是 消息边界 在以下情况下 TCP 和 UDP 之间的区别之一是 UDP 保留消息 边界 我理解之间的区别TCP and UDP 但我不确定的定义 消息边界 由于 UDP 在每个单独的数据包中包含目的地和端口信息 因此是否可以为消息提供
  • 对等网络应用程序的网络发现

    我希望有两个类 一个服务器类和一个客户端类 服务器类应该接收每个新客户端的 IP 地址和端口号并将它们存储在列表中 它应该为每个客户端提供已连接客户端及其 IP 地址的列表 然后 客户端可以使用 TCP 连接相互通信 问题是客户端不知道服务
  • 在 Perl 中如何接受多个 TCP 连接?

    我对 Linux 的 Perl 脚本有疑问 它的主要目的是成为 3 个应用程序之间的中间人 它应该做什么 它应该能够等待 UDP 文本 不带空格 udp port 当它收到 UDP 文本时 它应该将其转发到连接的 TCP 客户端 问题是我的

随机推荐

  • xml sax localName和qName的区别

    对于DefaultHandler类中方法的成员 String uri String localName String qName Attributes attributes 弄的不是很清楚 于是得到下面这片文章感觉讲的蛮清楚的 无节操的贴过
  • CSS动画实现的三种方式

    CSS动画 CSS动画就是元素从一种样式过渡到另一种样式的过程 常见的动画效果很多 比如 平移 缩放 旋转等 CSS实现动画的方式有以下几种 transition 实现渐变动画 transform 实现缩放 平移等效果 animation
  • 金币收集问题

    金币被放在1到n编号的格子中 一个人从第一个格子出发 有m张卡片 共4种 卡片上的数字1 4 表示可以走的步数 求到达终点获得的最大金币数 import java util public class Main public static v
  • linux内核设计与实现思想 – C风格的面向对象

    原文链接 linux内核学习 C风格的面向对象 linux内核大量使用面向对象的编码风格 然而linux内核是完全使用C写就 学习他们如何使用C模拟面向对象机制很有意思 这种做法很可能被人贬为扯淡 但是的确使用C模拟面向对象机制 使得程序员
  • 异常检测

    异常检测 MATLAB实现Bayes贝叶斯突时间序列变检测 目录 异常检测 MATLAB实现Bayes贝叶斯突时间序列变检测 基本描述 程序设计 参考资料 学习总结 致谢 基本描述 突变分为如下主要的几种 均值突变 最常见 方差突变 线性回
  • Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update

    进行数据库操作时 报错如下 org springframework orm hibernate5 HibernateOptimisticLockingFailureException Batch update returned unexpe
  • 虚函数表、函数地址、虚函数指针问题!

    一 虚函数 1 虚函数的写法 函数前 virtual 关键字 virtual fun cout lt lt lt
  • 代码覆盖率工具OpenCppCoverage在Windows上的使用

    OpenCppCoverage是用在Windows C 上的开源的代码覆盖率工具 源码地址为https github com OpenCppCoverage OpenCppCoverage 最新发布版本为0 9 9 0 License为GP
  • 深度学习基本环境搭建 ubuntu20.04LTS系统安装 nvidia显卡驱动 anaconda安装 cuda指定版本安装 cuda版本升级

    背景 电脑原来是windows 现在装为纯Ubuntu 安装显卡驱动 安装anaconda 安装pytorch 电脑型号 dell的一个工作站 显卡rtx3090 镜像下载 Enterprise Open Source and Linux
  • ElementUI+Vue 解决在使用el-dialog时,点击el-dialog外的其他区域会导致该对话框关闭。

    转载自 https blog csdn net weixin 42230550 article details 95201746 需求描述 今天 在做Element Vue项目时遇到一个需求 甲方要求在Dialog打开状态下 点击该Dial
  • YOLOv4网络详解

    0前言 在YOLOv4论文中 作者其实就是把当年所有的常用技术罗列了一遍 然后做了一堆消融实验 1 YOLOV4的网络改进部分 1 主干特征提取网络 DarkNet53 gt CSPDarkNet53 使用Mish激活函数 2 特征金字塔
  • java---为什么byte+byte=int

    byte也是基本数据类型范围是 128 127 但是做加法时会有这么一个问题 我们清晰的看到 byte byte byte报错了 这是为什么呢 编译器说他需要int类型 这是因为在java中 因为byte数量太小了 从硬件的角度讲 为较小的
  • uni-app使用时遇到的坑

    一 uni app开发规范 1 微信小程序request请求需要https 小程序端 在本地运行时 可以使用http 但是预览或者上传时 使用http无法请求 APP端 一般APP可以使用http访问 高版本的APP可能需要用https访问
  • 浪涌测试如何进行试验配置

    此文只是针对浪涌测试中的试验配置部分内容进行总结 如下表所示 试验对象 开路电压波形 注1 短路电流波形 输出阻抗 电源 线 线 1 2 50us 8 20us 2 18uF 线 地 2 10 9uF 非屏蔽不对称 注2 非差分通讯线 线
  • 和为s的数字

    题目描述 输入一个数组和一个数字s 在数组中查找两个数 使得它们的和正好是s 如果有多对数字的和等于s 输出任意一对即可 你可以认为每组输入中都至少含有一组满足条件的输出 样例 输入 1 2 3 4 sum 7 输出 3 4 分析 首先想到
  • Ubuntu系统安装分区

    一 U盘启动盘制作 1 U盘一个 U盘空间大小足够大 8G够用了 U盘里面的内容提前转存备份 2 UltraISO软碟通启动制作工具或Universal USB Installer 3 Ubuntu官网找到自己需要Ubuntu版本下载ISO
  • blfs:为lfs虚拟机增加桌面03

    编译安装Qt5 15 我比较好奇 当前只安装了twm X org提供的简单的窗口管理器 这个时候Qt的界面是怎么样的一个呈现 Qt5 15安装 required和recommanded必装 optional中涉及到runtime的建议安装
  • 树莓派4B Ubuntu 远程桌面 步骤

    文章目录 准备 更换国内源 更新软件列表和软件 安装SSH 安装ubuntu desktop xrdp方法 VNC方法 问题故障解决 花屏 蓝屏 黑屏 无法修正错误 因为您要求某些软件包保持现状 就是它们破坏了软件包间的依赖关系 参考 准备
  • 时间序列-预测-经典算法:Arimax【带额外输入的自回归综合移动平均】【多元变量预测】【ARIMA模型的一个扩展版本】

    标准的ARIMA 移动平均自回归模型 模型允许只根据预测变量的过去值进行预测 该模型假定一个变量的未来的值线性地取决于其过去的值 以及过去 随机 影响的值 ARIMAX模型是ARIMA模型的一个扩展版本 它还包括其他独立 预测 变量 该模型
  • C开源项目-TinyHttp解读(上)

    项目简介 此项目可以自行在GitHub上进行下载 作者的ReadMe文档也写得是比较详细的 这里用到了较多的Unix编程 不会的可以一点一点去查 实名感谢我们家杰佬发给我的APUE 可以当字典查 项目内容 其实就是实现了一个轻量级服务器的功