Linux之HTTP服务器的构建

news2024/11/16 18:08:28

在这里插入图片描述

欢迎诸位来阅读在下的博文~
在这里,在下会不定期发表一些浅薄的知识和经验,望诸位能与在下多多交流,共同努力!
江山如画,客心如若,欢迎到访,一展风采

文章目录

  • 参考环境
  • 参考书籍
  • 一、HTTP的工作原理
      • 1. 建立连接
      • 2. 发送请求
      • 3. 服务器处理请求
      • 4. 发送响应
      • 5. 断开连接
      • 6. 客户端处理响应
  • 二、客户端请求的消息结构
  • 三、服务器响应的消息结构
  • 四、HTTP状态码
  • 五、从零开始搭建HTTP服务器
    • 编译运行

参考环境

  1. VMware Workstation Pro
  2. Ubuntu20.04(运行服务器)

参考书籍

《Linux C/C++ 服务器开发实践》——朱文伟 李建英

一、HTTP的工作原理

HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的协议之一,它定义了客户端(通常是浏览器)和服务器之间交换数据的规则。浏览器作为HTTP客户端通过URL向HTTP服务器端,即Web服务器发送所有请求。常见的Web服务器有:Apache服务器、IIS服务器等。
其模型如下:
在这里插入图片描述

以下是 HTTP 的工作原理的简化概述:

1. 建立连接

  • 客户端发起请求:用户通过浏览器或其他客户端软件输入 URL 或点击链接,客户端软件解析 URL 并向服务器发起连接请求。
  • DNS解析:客户端首先通过 DNS(Domain Name System)将域名解析为服务器的 IP 地址。
  • TCP连接:客户端通过 TCP(Transmission Control Protocol)与服务器建立连接。HTTP 通常使用 TCP 的 80 端口,但是也可以改为8080或者其他端口。

2. 发送请求

  • 请求行:客户端发送一个 HTTP 请求,通常包括请求方法(如 GET、POST)、请求的 URL 和 HTTP 版本(如 HTTP/1.1 或 HTTP/2)。
  • 请求头:请求还包括一系列的请求头,如 Host(指定服务器的主机名)、User-Agent(标识客户端类型)、Accept(指定客户端可接受的响应类型)等。
  • 请求体(可选):如果请求方法(如 POST)需要发送数据,请求体将包含这些数据。

3. 服务器处理请求

  • 服务器接收请求:服务器接收到请求后,根据请求方法、URL 和其他信息处理请求。
  • 资源定位:服务器确定请求的资源位置,这可能是静态文件或需要动态生成的数据。
  • 处理请求:如果是动态请求,服务器可能需要执行数据库查询、业务逻辑处理等。

4. 发送响应

  • 状态行:服务器发送一个 HTTP 响应,包括 HTTP 版本、状态码(如 200 表示成功)和状态短语(如 “OK”)。
  • 响应头:响应还包括一系列的响应头,如 Content-Type(指定响应内容的类型)、Content-Length(指定响应内容的长度)等。
  • 响应体:这是服务器返回给客户端的实际数据,可以是 HTML 页面、图片、视频等。

5. 断开连接

  • 关闭连接:一旦响应被客户端接收,TCP 连接通常会被关闭。对于 HTTP/1.1,如果请求头中包含 Connection: keep-alive,则连接可能会被保持打开状态,以便后续请求重用。

6. 客户端处理响应

  • 解析响应:客户端软件解析响应内容,并根据内容类型进行相应的处理(如渲染 HTML 页面、显示图片等)。
    HTTP 是无状态的,这意味着每个请求都是独立的,服务器不会保存关于客户端的任何信息(除非使用 cookie 或其他机制)。这使得 HTTP 适用于简单的请求-响应模型,但也有一些局限性,比如在需要维护会话状态的应用中。

二、客户端请求的消息结构

客户端发送一个HTTP请求到服务器,改请求消息由请求行(状态行)、请求头部(首部行)、空行和请求数据(实体)四个部分组成,如图所示
在这里插入图片描述

注:sp 指空格,cr指回车符,lf指换行符

HTTP协议定义了8种请求方法,来表明对Request-URI指定资源的不同操作方式,具体如下:

  1. GET:请求获取由Request-URI标识的资源。它通常用于请求服务器发送一个文件或数据。
  2. POST:用于向服务器提交数据,通常用于提交表单数据或上传文件。POST请求可能会导致服务器上的状态改变或副作用。
  3. PUT:用于将请求中的数据存储在服务器上。它通常用于更新资源,但如果资源不存在,可能会创建一个新的资源。
  4. DELETE:请求服务器删除由Request-URI标识的资源。
  5. HEAD:与GET方法类似,但服务器在响应中只返回头部信息,不返回实体主体(即不返回实际内容)。
  6. OPTIONS:用于获取服务器支持的HTTP请求方法和其他选项信息。
  7. TRACE:用于沿着到目标资源的路径执行消息回环测试,主要用于诊断和调试。
  8. CONNECT:用于建立一个隧道,将连接转换为透明的TCP/IP通道,通常用于SSL加密的连接。

以下是这些方法的归纳:

  • GET:检索数据。
  • POST:提交数据。
  • PUT:更新或创建数据。
  • DELETE:删除数据。
  • HEAD:检查数据的存在性或状态。
  • OPTIONS:查询服务器能力。
  • TRACE:跟踪请求链。
  • CONNECT:建立隧道。

虽然HTTP的请求方式有8种,但是在实践中常用到的是GET和POST,其他方法可以通过这两种基本方式间接实现。

三、服务器响应的消息结构

HTTP响应由四个部分组成,分别是:状态行、消息报头(响应头)、空行和响应正文,如图:
在这里插入图片描述

在这里插入图片描述

四、HTTP状态码

当浏览者访问一个网页时,浏览器会向网页所在服务器发出请求。在浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头用以响应浏览器的请求。
HTTP状态码的英文时HTTP Status Code。下面时常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误

类型描述
1**信息,服务器收到请求,需要请求者继续执行操作
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

五、从零开始搭建HTTP服务器

现在有很多现成开源的HTTP服务器,比如非常流行的Apache,诸位若想详细了解,可以浏览下面博客一二:
在linux上架设Web服务器Apache(Ubuntu)
Linux系统的Apache2如何启动cgi模块(Ubuntu)

但是,我今天打算自己手动搭建一个,诸位若有兴趣,也可以一起来吧~

main.c

#include "httpserver.h"

int main(void)
{
    int server_sock = -1;
    u_short port = 8888;
    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*(*)(void*))accept_request, (void*)(intptr_t)client_sock) != 0)
        perror("pthread_create");
    }

    close(server_sock);

    return(0);
}

httpserver.h

#ifndef _HTTPSERVER_H_
#define _HTTPSERVER_H_
#include "myhead.h"

#define ISspace(x) isspace((int) (x))

#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"

//处理从套接字上监听到的一个HTTP请求,此函数很大部分体现服务器处理请求流程
void accept_request(void*);

//返回给客户端这是一个错误请求
void bad_request(int);

//读取服务器上某个文件,写道套接字中
void cat(int, FILE *);

//处理发生在执行cgi程序事出现的错误
void cannot_execute(int);

//打印错误信息,并退出
void error_die(const char *);

//运行cgi程序的处理,是主要的函数
void execute_cgi(int, const char *, const char *, const char *);

//读取套接字的一行,把回车符换行符等情况都统一为换行符结束
int get_line(int, char *, int);

//把HTTP响应头部写到套接字中
void headers(int, const char *);

//处理找不到请求文件的情况,即发送404
void not_found(int);

//读取文件并返回给客户端,里面调用cat
void serve_file(int, const char *);

//初始化http服务,包括建立套接字、绑定端口、进行监听等。
int startup(u_short *);

//返回浏览器,表明method不支持
void unimplemented(int);

#endif

httpserver.c

#include "myhead.h"
#include "httpserver.h"

int startup(u_short *port)
{
    int httpd = 0;
    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 (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);
}

void error_die(const char *sc)
{
    perror(sc);
    exit(1);
}

void accept_request(void *arg)
{
    int client = (intptr_t)arg;
    char buf[1024];
    int numchars;
    char method[255];
    char url[255];
    char path[512];
    size_t i, j;
    struct stat st;
    int cgi = 0;
    char *query_string = NULL;

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

    //只能识别GET和POST
    if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    {
        //返回浏览器,表明格式不符
        unimplemented(client);
        return;
    }

    //如果是POST,cgi置为1,即POST的时候开启cgi
    if (strcasecmp(method, "POST") == 0)
        cgi = 1;

    i = 0;
    //跳过空格
    while (ISspace(buf[j]) && (j < sizeof(buf)))
        j++;
    //从缓冲区中把URL读取出来
    while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
    {
        url[i] = buf[j];
        i++;
        j++;
    }
    url[i] = '\0';
    //处理GET请求
    if (strcasecmp(method, "GET") == 0)
    {
        query_string = url;
        //去找GET参数,即?后面部分
        while ((*query_string != '?') && (*query_string != '\0'))
            query_string++;
        //如果找到了的话,说明这个请求也需要调用脚本来处理
        //此时就把请求字符串单独抽取出来
        //GET方法特点,?后面为参数
        if (*query_string == '?')
        {
            cgi = 1;
            *query_string = '\0';
            query_string++;
        }
    }
    //保存有效的URL地址并加上请求地址的主页索引,默认目录是在htdocs下
    //格式化URL到path数组中
    sprintf(path, "/root/htdocs%s", url);

    //默认地址,解析到的路径如果是/,则自动加上index.html,代表默认访问该路径下的index.html网页
    if (path[strlen(path) - 1] == '/')
        strcat(path, "index.html");

    //访问请求的文件,如果文件不存在,直接返回并发送404,如果存在,则调用cgi程序处理
    //根据路径找到对应文件
    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
    {
        //如果文件存在但是个目录,则直接拼接index.html
        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;

        //如果不是cgi,直接把服务器文件返回,否则执行cgi
        if (!cgi)
            serve_file(client, path);
        else
            execute_cgi(client, path, method, query_string);
    }

    close(client);
}

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);
}

void unimplemented(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</TITLE></HEAD>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
}

void not_found(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "your request because the resource specified\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "is unavailable or nonexistent.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
}

void serve_file(int client, const char *filename)
{
    FILE *resource = NULL;
    int numchars = 1;
    char buf[1024];

    buf[0] = 'A';
    buf[1] = '\0';
    while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
        numchars = get_line(client, buf, sizeof(buf));

    resource = fopen(filename, "r");
    if (resource == NULL)
        not_found(client);
    else
    {
        headers(client, filename);
        cat(client, resource);
    }
    fclose(resource);
}

void execute_cgi(int client, const char *path,
                 const char *method, const char *query_string)
{
    char buf[1024];
    int cgi_output[2];
    int cgi_input[2];
    pid_t pid;
    int status;
    int i;
    char c;
    int numchars = 1;
    int content_length = -1;

    buf[0] = 'A';
    buf[1] = '\0';
    if (strcasecmp(method, "GET") == 0)
        while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
            numchars = get_line(client, buf, sizeof(buf));
    else /* POST */
    {
        numchars = get_line(client, buf, sizeof(buf));
        while ((numchars > 0) && strcmp("\n", buf))
        {
            buf[15] = '\0';
            if (strcasecmp(buf, "Content-Length:") == 0)
                content_length = atoi(&(buf[16]));
            numchars = get_line(client, buf, sizeof(buf));
        }
        if (content_length == -1)
        {
            bad_request(client);
            return;
        }
    }

    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);

    //建立无名管道,以帮助父子进程通信
    if (pipe(cgi_output) < 0)
    {
        cannot_execute(client);
        return;
    }
    if (pipe(cgi_input) < 0)
    {
        cannot_execute(client);
        return;
    }


    //  fork后管道都复制一份,都是一样的
    //子进程关闭两个无用的端口,避免浪费
    //   x<---------------------->1   output
    //   0<---------------------->x   input

    //父进程关闭2个无用的端口,避免浪费
    //   0<---------------------->x   output
    //   x<---------------------->1   input
    //此时父子进程可以通信了
    if ((pid = fork()) < 0)
    {
        cannot_execute(client);
        return;
    }
    if (pid == 0) /* child: CGI script */
    {
        char meth_env[255];
        char query_env[255];
        char length_env[255];

        dup2(cgi_output[1], 1);//将cgi_putput的写端文件描述符复制到1(即标准输出文件描述符),导致任何写入
                               //标准输出的操作都会发送到cig_output的写入端
        dup2(cgi_input[0], 0); //同理
        close(cgi_output[0]);
        close(cgi_input[1]);

        //设置cgi的环境变量
        sprintf(meth_env, "REQUEST_METHOD=%s", method);
        putenv(meth_env);
        if (strcasecmp(method, "GET") == 0)
        {
            sprintf(query_env, "QUERY_STRING=%s", query_string);
            putenv(query_env);
        }
        else
        { /* POST */
            sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
            putenv(length_env);
        }
        //使用execl执行cgi的脚本
        execl(path, path, NULL);
        exit(0);
    }
    else
    { /* parent */
        close(cgi_output[1]);
        close(cgi_input[0]);
        if (strcasecmp(method, "POST") == 0)
            for (i = 0; i < content_length; i++)
            {
                recv(client, &c, 1, 0);
                write(cgi_input[1], &c, 1);
            }
        while (read(cgi_output[0], &c, 1) > 0)
            send(client, &c, 1, 0);

        close(cgi_output[0]);
        close(cgi_input[1]);
        waitpid(pid, &status, 0);
    }
}

void headers(int client, const char *filename)
{
    char buf[1024];
    (void)filename; /* could use filename to determine file type */

    strcpy(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);
    strcpy(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    strcpy(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
}

void cat(int client, FILE *resource)
{
    char buf[1024];

    fgets(buf, sizeof(buf), resource);
    while (!feof(resource))
    {
        send(client, buf, strlen(buf), 0);
        fgets(buf, sizeof(buf), resource);
    }
}

void bad_request(int client)
{
 char buf[1024];

 sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
 send(client, buf, sizeof(buf), 0);
 sprintf(buf, "Content-type: text/html\r\n");
 send(client, buf, sizeof(buf), 0);
 sprintf(buf, "\r\n");
 send(client, buf, sizeof(buf), 0);
 sprintf(buf, "<P>Your browser sent a bad request, ");
 send(client, buf, sizeof(buf), 0);
 sprintf(buf, "such as a POST without a Content-Length.\r\n");
 send(client, buf, sizeof(buf), 0);
}

void cannot_execute(int client)
{
 char buf[1024];

 sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "Content-type: text/html\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "\r\n");
 send(client, buf, strlen(buf), 0);
 sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
 send(client, buf, strlen(buf), 0);
}

myhead.h

// myhead.h
#ifndef _MYHEAD_H
#define _MYHEAD_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/input.h>  //跟输入子系统模型有关的头文件
#include <dirent.h>
#include <stdbool.h>
#include <strings.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <poll.h>
#include <sys/epoll.h>
#include <ctype.h>
#endif

编译运行

使用:gcc httpserver.c main.c -o main -ipthread 编译出可执行文件
如果成功,可以使用./main运行该服务器程序。

但是,现在还缺少.html文件和.cgi文件。在下已提供一份样例,如下:

index.html

<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<p>Welcome to my HTTP webserver.</p>
<h1>Show CGI Result:</h1>
<form action="test.cgi" method="POST">
<input type="submit" value="Submit">
</form>
</body>
</html>

test.cpp(将该文件编译,然后将得到的可执行文件改名成test.cgi即可)

#include <iostream>  
using namespace std;  
       
int main()  
{  
	cout << "Content-Type: text/html\n\n";  //注意结尾是两个\n
	cout << "<html>\n";  
	cout << "<head>\n";  
	cout << "<title>Hello World - First CGI Program</title>\n";  
	cout << "</head>\n";  
	cout << "<body bgcolor=\"yellow\">\n";  
	cout << "<h2> <font color=\"#FF0000\">Hello World! This is my first CGI program</font></h2>\n";  
	cout << "</body>\n";  
	cout << "</html>\n";  
         
	return 0;  
}

获得上面两个文件后,将他们统一放在/root/htdocs/目录下(如果没有该目录,可自行创建),
当然,勿要忘记了更改目录和文件的权限:
sudo chmod +x /root/htdocs
(sudo chmod +x /root/htdocs/index.html)x
注意:不要修改.html的权限,因为在代码中当.html文件只读时,才处理
sudo chmod +x /root/htdocs/test.cgi

如此,运行main可执行文件,然后在主机的浏览器上输入:http://你的ip地址:8888/
结果如下:
在这里插入图片描述
点击按钮,会有如下返回:
在这里插入图片描述

当然,你也可以直接访问.cgi文件:
在这里插入图片描述

至此,结束~
在这里插入图片描述
望诸位不忘三连支持一下~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s查看容器的日志

查询运行容器命令&#xff1a;kubectl get po -n pro 如果想查看cams系统&#xff0c;那么执行命令&#xff1a; kubectl logs pro -f -n pro &#xff08;容器名称&#xff09;|more

【HarmonyOS】端云一体化初始化项目

简介 端云一体化开发是HarmonyOS对云端开发的支持、实现端云联动。云开发服务提供了云函数、云数据库、云存储等服务&#xff0c;可以使开发者专注于应用的业务逻辑开发&#xff0c;无需关注基础设施&#xff0c;例如&#xff1a;服务器、操作系统等问题。 因此&#xff0c;…

CentOS下分布式消息系统kafka的安装和使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前沿一、Windows主机&#xff08;G:\share&#xff09;和Linux虚拟机&#xff08;/mnt/hgfs/share&#xff09;之间文件共享1.添加Windows主机的路径2.linux安装VMw…

比特位的计算

给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;[0,1,1] 解释&#xff1a; 0 --> 0 1 --> …

我的创作纪念日【2048】

机缘 2048&#xff0c;是计算机二进制世界里很奇妙的数字&#xff0c;在CSDN上创作的第六年&#xff0c;记录从事本行业的知识学习与总结&#xff0c;好记性不如烂笔头&#xff0c;或许写的东西不如大佬的文章&#xff0c;那么有深度&#xff0c;但自己也是在坚持&#xff0c;…

计算机毕业设计选什么题目好?Java财会信息管理系统

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

Ciallo~(∠・ω・ )⌒☆第二十篇 入门mysql 数据库

要入门MySQL数据库&#xff0c;首先需要了解数据库的基本概念和原理。MySQL是一种广泛使用的开源关系型数据库管理系统&#xff0c;它能够处理大量的数据&#xff0c;并提供了多种功能。 一、创建数据库 连接到MySQL后&#xff0c;你可以使用SQL语句来创建数据库。例如&#x…

leetcode第141题:环形链表(C语言+引申问题全解)

解题思路&#xff1a; 定义一对快慢指针&#xff0c;slow指针每次走一步&#xff0c;fast指针每次走两步。当快指针入环时&#xff0c;慢指针只走了一半。 当slow指针入环时&#xff0c;fast指针已经在环内走了许多步。让他们两个继续走&#xff0c;直到相遇&#xff0c;可以证…

矩阵中的最大得分(Lc3148)——动态规划

给你一个由 正整数 组成、大小为 m x n 的矩阵 grid。你可以从矩阵中的任一单元格移动到另一个位于正下方或正右侧的任意单元格&#xff08;不必相邻&#xff09;。从值为 c1 的单元格移动到值为 c2 的单元格的得分为 c2 - c1 。 你可以从 任一 单元格开始&#xff0c;并且必须…

STM32G0系列配置Freertos所管理的最高中断优先级

最近在G070上部署Freertos&#xff0c;用cubemx生成的时候发现无法选择freertos所能管理的最高优先级和最低优先级&#xff0c;即configMAX_SYSCALL_INTERRUPT_PRIORITY和LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY。我们可以发现在F103RCT6上是可以选择这两个优先级限制的&#…

树莓派开发笔记01-树莓派的系统烧录以及初次开机配置

github主页&#xff1a;https://github.com/snqx-lqh gitee主页&#xff1a;https://gitee.com/snqx-lqh 本项目github地址&#xff1a;https://github.com/snqx-lqh/RaspberryPiLearningNotes 本项目gitee地址&#xff1a;https://gitee.com/snqx-lqh/RaspberryPiLearningNote…

详解golang内存管理

介绍 要搞明白 Go 语言的内存管理,就必须先理解操作系统以及机器硬件是如何管理内存的。因为 Go 语言的内部机制是建立在这个基础之上的,它的设计,本质上就是尽可能的会发挥操作系统层面的优势,而避开导致低效情况。 操作系统内存管理 其实现在计算机内存管理的方式都是…

Python---容器

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.容器概念 在Python中&#xff0c;容器是指可以存放多个元素的对象。常见的容器类型有列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;、集合&#xff08;Set&#xf…

solidwork经验总结2

新建<装配体 打开之后可以直接从文件夹将零件中拖入&#xff0c;也可以 怎样装配在一起&#xff1f; 点击配合。 假如我要把轴承装到这个孔里面来 首先&#xff0c;它们一定得是共线 然后点击这两个圆 产生同轴心 选择重合&#xff0c;可以两个贴紧 上面这个也可以按照需求…

ECEF地心地固坐标系与ENU站心坐标系互转

ENU站心坐标系 站心坐标系也叫做站点坐标系、东-北-天坐标系ENU&#xff0c;英文名称是local Cartesian coordinates coordinate system&#xff0c;主要是用于需了解以观察者为中心的其他物体运动规律。 站心直角坐标系 定义&#xff1a;以站心&#xff08;如GPS接收天线中…

植物生长时为什么会扭动?科学家解开令查尔斯·达尔文困惑的千古之谜

在一项新的研究中&#xff0c;来自美国和以色列的物理学家可能已经弄清了植物生长过程中的一种古怪行为–也是查尔斯-达尔文本人在其生命的最后几十年里所好奇的一个谜&#xff1a;对于许多人类来说&#xff0c;植物可能看起来静止不动&#xff0c;甚至有点无趣。但实际上&…

小米5c解除BL锁刷机root

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 解锁BL锁 1. 下载安装 miflash_unlock&#xff1a;https://miuiver.com/miunlock/&#xff0c;登录小米账号&#xff08;需要和解锁设备绑定的账号一致&#…

Java Web —— 第六天(Mybatis)

lombok Lombok是一个实用的Java类库&#xff0c;能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法&#xff0c;并可以自动化生成日志变量&#xff0c;简化java开发、提高效率 在pom.xml文件中引入依赖 <!--lombok--><dependency>…

Spring IoCDI(中)--IoC的进步

通过上文的讲解和学习, 我们已经知道了Spring IoC 和DI的基本操作, 接下来我们来系统的学习Spring IoC和DI 的操作. 前⾯我们提到IoC控制反转&#xff0c;就是将对象的控制权交给Spring的IOC容器&#xff0c;由IOC容器创建及管理对 象&#xff0c;也就是bean的存储。 1. Bean的…

Datawhale AI 夏令营第四期 大模型技术-微调 task3 数据增强与评分

前面我们介绍了baseline的思路及写作方案&#xff0c;这里我们尝试对数据做增强&#xff0c;但是需要聪明的你加入自己的努力完成更好的思路。 今天需要大家学习上手尝试数据增强&#xff0c;不过我会把增强的思路和相关知识告诉大家&#xff0c;让大家学习如何使用llm完成数据…