Webserve(4): HTTP解析

// 写HTTP响应
bool http_conn::write()
{int temp = 0;if ( bytes_to_send == 0 ) {// 将要发送的字节为0,这一次响应结束。modfd( m_epollfd, m_sockfd, EPOLLIN ); init();return true;}while(1) {// 分散写temp = writev(m_sockfd, m_iv, m_iv_count);if ( temp <= -1 ) {// 如果TCP写缓冲没有空间,则等待下一轮EPOLLOUT事件,虽然在此期间,// 服务器无法立即接收到同一客户的下一个请求,但可以保证连接的完整性。if( errno == EAGAIN ) {modfd( m_epollfd, m_sockfd, EPOLLOUT );return true;}unmap();return false;}bytes_have_send += temp;bytes_to_send -= temp;if (bytes_have_send >= m_iv[0].iov_len){m_iv[0].iov_len = 0;m_iv[1].iov_base = m_file_address + (bytes_have_send - m_write_idx);m_iv[1].iov_len = bytes_to_send;}else{m_iv[0].iov_base = m_write_buf + bytes_have_send;m_iv[0].iov_len = m_iv[0].iov_len - temp;}if (bytes_to_send <= 0){// 没有数据要发送了unmap();modfd(m_epollfd, m_sockfd, EPOLLIN);if (m_linger){init();return true;}else{return false;}}}}

这段代码实现了写入 HTTP 响应的功能。让我们逐行解释它的工作原理:

  1. if (bytes_to_send == 0):检查是否还有待发送的字节。如果没有,则说明本次响应已经完成,重新初始化连接并等待下一个请求。

  2. while(1):进入一个无限循环,以确保所有要发送的数据都能够被写入。

  3. temp = writev(m_sockfd, m_iv, m_iv_count);:调用 writev 函数向套接字写入数据。m_iviovec 结构的数组,用于描述要写入的数据块。m_iv_count 表示 m_iv 数组中的元素数量。writev 函数会尽可能地将所有数据一次性写入套接字。

  4. if (temp <= -1):检查 writev 函数的返回值,如果出现错误,则根据错误类型进行处理。

    • 如果错误为 EAGAIN,表示套接字写缓冲区已满,无法继续写入数据,需要等待下一次 EPOLLOUT 事件再次写入。因此,修改套接字的事件监听为 EPOLLOUT,然后返回 true,表示本次写入未完成,需要等待下一次写入机会。

    • 其他错误情况,可能是连接出现了问题,需要取消内存映射,并返回 false

  5. bytes_have_send += temp;:更新已经发送的字节数。

  6. bytes_to_send -= temp;:更新待发送的字节数。

  7. if (bytes_have_send >= m_iv[0].iov_len):检查是否已经发送完 m_iv[0] 描述的数据块。如果是,则更新 m_iv[1] 描述的数据块为下一块要发送的数据。

  8. if (bytes_to_send <= 0):检查是否所有数据均已发送。如果是,则取消内存映射,修改事件监听为 EPOLLIN,并根据是否需要保持连接来重新初始化连接或者关闭连接。

  9. 循环会继续写入剩余的数据,直到所有数据均已发送完成或者发生错误。

这段代码实现了有效的分散写操作,可以高效地将 HTTP 响应数据写入套接字。

这段代码逻辑用于更新 iovec 结构数组 m_iv 中描述的数据块,以便继续发送数据。让我们逐行解释:

  1. if (bytes_have_send >= m_iv[0].iov_len):这个条件判断语句检查是否已经发送完了 m_iv[0] 描述的数据块中的内容。如果 bytes_have_send 大于等于 m_iv[0].iov_len,表示 m_iv[0] 描述的数据块中的内容已经全部发送完成。

  2. m_iv[0].iov_len = 0;:将 m_iv[0] 描述的数据块的长度设置为零,表示该数据块中的内容已经全部发送完成,下一次写入将不再包含这部分内容

  3. m_iv[1].iov_base = m_file_address + (bytes_have_send - m_write_idx);:更新 m_iv[1] 描述的数据块的起始地址。m_file_address 是之前使用 mmap 函数映射的文件内存起始地址,bytes_have_send - m_write_idx 表示已经发送的字节数减去 m_write_idx,得到当前需要发送的数据在文件内存中的起始位置

  4. m_iv[1].iov_len = bytes_to_send;:更新 m_iv[1] 描述的数据块的长度,使其等于待发送的字节数 bytes_to_send

  5. else:如果 if 条件不满足,则执行 else 分支,表示 m_iv[0] 描述的数据块中的内容尚未全部发送完成。在这种情况下,需要继续发送 m_iv[0] 描述的数据块的剩余内容

  6. m_iv[0].iov_base = m_write_buf + bytes_have_send;:更新 m_iv[0] 描述的数据块的起始地址,使其指向剩余待发送的缓冲区中的数据

  7. m_iv[0].iov_len = m_iv[0].iov_len - temp;:更新 m_iv[0] 描述的数据块的长度,减去已经发送的字节数 temp,以便指向剩余待发送的数据块。

这段代码的目的是确保 writev 函数能够连续地将所有的数据块一次性发送出去,提高发送效率。

// 往写缓冲中写入待发送的数据
bool http_conn::add_response( const char* format, ... ) {if( m_write_idx >= WRITE_BUFFER_SIZE ) {return false;}va_list arg_list;va_start( arg_list, format );int len = vsnprintf( m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list );if( len >= ( WRITE_BUFFER_SIZE - 1 - m_write_idx ) ) {return false;}m_write_idx += len;va_end( arg_list );return true;
}

这段代码是 http_conn 类的 add_response 方法的实现,用于将待发送的数据写入写缓冲区中。让我们逐行解释这段代码的功能:

  1. if (m_write_idx >= WRITE_BUFFER_SIZE)检查当前写缓冲区中的数据是否已经超过了缓冲区的大小。如果超过了,说明没有足够的空间来写入新的数据,返回 false 表示写入失败。

  2. va_list arg_list; va_start(arg_list, format);:声明一个 va_list 类型的变量 arg_list,并使用 va_start 宏初始化该变量。这个宏的作用是初始化 arg_list,使其指向可变参数列表中的第一个参数。

  3. int len = vsnprintf(m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list);:使用 vsnprintf 函数将可变参数格式化为字符串,并将结果写入写缓冲区中。这个函数类似于 sprintf,但是它使用了可变参数列表来支持不定数量的参数。WRITE_BUFFER_SIZE - 1 - m_write_idx 是限制写入长度的上限,以确保不会溢出写缓冲区。

  4. if (len >= (WRITE_BUFFER_SIZE - 1 - m_write_idx)):检查 vsnprintf 函数的返回值 len 是否大于等于剩余空间的大小。如果是,说明写入的数据超出了剩余空间,因此写入失败,返回 false

  5. m_write_idx += len;:更新写缓冲区的索引,将其向后移动 len 个位置,以便下次写入数据时从正确的位置开始。

  6. va_end(arg_list);:结束可变参数列表的遍历。

  7. return true;:写入数据成功,返回 true

这段代码允许使用类似于 printf 的格式化字符串,将数据格式化并写入到写缓冲区中。如果写入失败,可能是由于缓冲区已满或者格式化错误导致的。

这段代码使用了C语言中的可变参数功能,通过 va_list, va_start, 和 vsnprintf 函数来处理和格式化一个不确定数量的参数。下面是对这些关键部分的详细解释:

  1. va_list arg_list;
    va_list一个用于处理可变参数列表的类型arg_list 是一个变量,用于访问函数的可变参数部分。在这里,它将被用来访问传给 add_response 函数的所有可变参数

  2. va_start(arg_list, format);
    va_start 是一个宏,用于初始化 arg_list 变量,以便它可以用于访问可变参数列表va_start 的第一个参数是之前声明的 va_list 变量,第二个参数是可变参数列表之前的最后一个固定参数,这里是 format。这个调用之后,arg_list 将指向函数的第一个可变参数。

  3. int len = vsnprintf(m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list);
    这一行是整个操作中最核心的部分,它执行多个任务:

    • vsnprintf 是一个标准C函数,功能是将可变参数按照 format 指定的格式格式化为字符串,并将结果存储到指定的缓冲区。这里,缓冲区是 m_write_buf + m_write_idx,即写缓冲区当前的写入位置
    • WRITE_BUFFER_SIZE - 1 - m_write_idx 指定了目标缓冲区的最大长度,以防止写入超出缓冲区大小导致的溢出。这里减去1是为了留出位置给字符串的终结符 \0
    • format格式字符串,指定了后续可变参数应该如何被格式化。
    • arg_list 是之前通过 va_start 初始化的可变参数列表。
    • 函数返回写入字符的数量(不包括终结符\0),并将这个数量赋值给 len 变量。如果格式化后的字符串长度超过了缓冲区的限制,vsnprintf 会截断输出但仍会返回完整的字符串长度(如果不截断会有的长度)

通过这个过程,add_response 函数能够将格式化的字符串安全地追加到写缓冲区中,同时避免缓冲区溢出,这是网络编程中处理字符串时的一种常见且重要的做法。

va_end(arg_list); 是用来结束可变参数列表的使用的宏。在使用 va_start 初始化了 va_list 类型的变量以后,你应当在函数返回之前对其使用 va_end,以进行必要的清理。

虽然在许多实现中 va_end 实际上可能什么也不做,调用它是好的编程实践,也是标准C中的要求。这样做可以确保代码的可移植性,因为某些平台或编译器可能需要 va_end 来执行实际的操作,比如释放分配的内存或重置状态。

简而言之,va_end 的作用是结束对可变参数列表的访问,帮助确保资源得到正确管理和释放。在这个上下文中,它标志着 va_list 类型的 arg_list 的使用周期结束:

bool http_conn::add_status_line( int status, const char* title ) {return add_response( "%s %d %s\r\n", "HTTP/1.1", status, title );
}bool http_conn::add_headers(int content_len) {add_content_length(content_len);add_content_type();add_linger();add_blank_line();
}bool http_conn::add_content_length(int content_len) {return add_response( "Content-Length: %d\r\n", content_len );
}bool http_conn::add_linger()
{return add_response( "Connection: %s\r\n", ( m_linger == true ) ? "keep-alive" : "close" );
}bool http_conn::add_blank_line()
{return add_response( "%s", "\r\n" );
}

这段代码是在构建HTTP响应消息的不同部分,用于在服务器端向客户端发送HTTP响应。每个方法都负责生成响应的一部分,按照HTTP协议格式组织信息。下面是每个方法的详细说明:

add_status_line(int status, const char* title)

此方法用于添加HTTP响应的状态行。状态行是HTTP响应消息的第一行,包含HTTP版本号、状态码和原因短语。例如,“HTTP/1.1 200 OK”表示一个成功的请求。

  • "HTTP/1.1" 表示HTTP版本。
  • status 是一个整数,表示HTTP状态码,如200、404等。
  • title 是状态码的文本描述,如"OK"或"Not Found"。
  • 该方法通过调用add_response函数,将格式化的状态行字符串添加到响应缓冲区中。

add_headers(int content_len)

此方法用于添加HTTP响应头。它依次调用其他几个方法来添加特定的头部信息,包括内容长度、内容类型、连接类型和一个空行,标志着头部结束,接下来是消息体。

  • add_content_length(content_len) 添加Content-Length头,指示响应体的长度。
  • add_content_type() 该方法在代码片段中未显示,但其作用应是添加Content-Type头,指明响应体的媒体类型。
  • add_linger() 添加Connection头,根据m_linger成员变量的值决定是保持连接还是关闭连接。
  • add_blank_line() 添加一个空行,表示头部信息结束,按照HTTP协议,头部和正文之间必须有一个空行。

add_content_length(int content_len)

这个方法添加Content-Length响应头,该头部字段告诉客户端响应体的字节长度。这是一个重要的头部字段,特别是在响应体不为空时,因为它允许客户端正确地读取和解析接收到的数据。

add_linger()

这个方法添加Connection响应头,其值基于m_linger的布尔值。如果m_linger为true,头部值为"keep-alive",指示连接应该保持打开状态,以便客户端可以复用连接发送更多的请求。如果为false,则值为"close",指示处理完当前请求后应关闭连接。

add_blank_line()

这个方法向响应中添加一个空行,这在HTTP响应中是必需的,用来分隔头部和正文。在HTTP协议中,头部信息和正文内容之间必须有一个空行(即\r\n),这个方法正是用来添加这个分隔符的。

总体而言,这些方法共同构成了HTTP响应消息的创建和发送过程的一部分,确保了响应格式符合HTTP/1.1协议的规范。

bool http_conn::add_content( const char* content )
{return add_response( "%s", content );
}bool http_conn::add_content_type() {return add_response("Content-Type:%s\r\n", "text/html");
}

这两个方法都是用于构建HTTP响应的一部分,用来向响应中添加特定的内容和头部信息

add_content(const char* content)

此方法用于向HTTP响应正文中添加具体的内容。这里的content参数是一个字符串,代表了要发送给客户端的数据。这个方法通过调用add_response函数,并使用"%s"格式化字符串,将content直接添加到响应缓冲区中。这个方法通常用于添加HTML页面内容、API的JSON响应等。

add_content_type()

此方法用于添加Content-Type头部到HTTP响应中。Content-Type是一个HTTP头部字段,用于指示资源的MIME类型,客户端可以通过这个信息来解析和处理接收到的数据。在这个方法中,它固定地设置了Content-Type的值为"text/html",表示发送的内容是HTML文本。这是Web服务器常见的做法,因为大多数响应内容都是HTML格式的页面。如果服务器需要发送其他类型的内容,如JSON、图片或视频等,就需要相应地改变Content-Type的值

这两个方法的共同点是都通过add_response函数向响应缓冲区添加内容。add_response负责将格式化后的字符串追加到缓冲区,同时确保不会超过缓冲区的大小限制。通过这种方式,服务器能够构建包含状态行、响应头和响应体的完整HTTP响应消息。

bool http_conn::process_write(HTTP_CODE ret) {switch (ret){case INTERNAL_ERROR:add_status_line( 500, error_500_title );add_headers( strlen( error_500_form ) );if ( ! add_content( error_500_form ) ) {return false;}break;case BAD_REQUEST:add_status_line( 400, error_400_title );add_headers( strlen( error_400_form ) );if ( ! add_content( error_400_form ) ) {return false;}break;case NO_RESOURCE:add_status_line( 404, error_404_title );add_headers( strlen( error_404_form ) );if ( ! add_content( error_404_form ) ) {return false;}break;case FORBIDDEN_REQUEST:add_status_line( 403, error_403_title );add_headers(strlen( error_403_form));if ( ! add_content( error_403_form ) ) {return false;}break;case FILE_REQUEST:add_status_line(200, ok_200_title );add_headers(m_file_stat.st_size);m_iv[ 0 ].iov_base = m_write_buf;m_iv[ 0 ].iov_len = m_write_idx;m_iv[ 1 ].iov_base = m_file_address;m_iv[ 1 ].iov_len = m_file_stat.st_size;m_iv_count = 2;bytes_to_send = m_write_idx + m_file_stat.st_size;return true;default:return false;}m_iv[ 0 ].iov_base = m_write_buf;m_iv[ 0 ].iov_len = m_write_idx;m_iv_count = 1;bytes_to_send = m_write_idx;return true;
}

process_write 函数的目的是根据处理请求的结果(由ret参数的HTTP_CODE类型表示)准备HTTP响应。这个函数执行几项任务:

  1. 设置响应行和头部:根据HTTP_CODE的值,它会设置适当的状态行(例如,"HTTP/1.1 404 Not Found")和响应的头部。这包括指定内容长度和其他必要的头部,如ConnectionContent-Type

  2. 添加响应内容:对于错误状态(如404未找到、400错误请求等),它会向客户端添加描述错误的HTML正文。这是通过调用add_content实现的,该函数将错误描述的HTML追加到响应缓冲区中。

  3. 处理文件请求:如果请求是获取文件(FILE_REQUEST),它会准备表示成功的头部(200 OK)并设置两个iovec结构,以使用writev高效发送文件。第一个iovec指向存储在缓冲区(m_write_buf)中的HTTP头部,第二个iovec指向内存映射的文件内容(m_file_address)。

  4. 发送准备:最后,它会计算总共要发送的字节数,并根据情况设置m_iv数组(用于writev调用)和bytes_to_send。对于文件请求,bytes_to_send会包括头部和文件内容的总大小;对于错误响应,只包括响应头和错误页面的大小

// 由线程池中的工作线程调用,这是处理HTTP请求的入口函数
void http_conn::process() {// 解析HTTP请求HTTP_CODE read_ret = process_read();if ( read_ret == NO_REQUEST ) {modfd( m_epollfd, m_sockfd, EPOLLIN );return;}// 生成响应bool write_ret = process_write( read_ret );if ( !write_ret ) {close_conn();}modfd( m_epollfd, m_sockfd, EPOLLOUT);
}

这段代码是一个HTTP连接处理流程的概述,展示了在一个典型的基于事件驱动的服务器模型中,如何处理一个HTTP请求。具体来说,这段代码可能是在一个基于epoll的服务器中使用线程池处理HTTP请求的一部分。下面是对这个过程的详细解释:

  1. 解析HTTP请求:首先调用process_read()函数来解析客户端发送的HTTP请求这个函数的目的是读取并分析客户端发送的数据,然后确定这些数据是否构成了一个完整的HTTP请求process_read()函数返回一个HTTP_CODE枚举类型的值,表示请求解析的结果。

  2. 检查请求状态:如果process_read()返回NO_REQUEST,这表示当前读取的数据不足以构成一个完整的HTTP请求,需要继续读取客户端数据。此时,使用modfd()函数重新设置socket为EPOLLIN事件(即可读事件),然后返回,等待更多数据到来。

  3. 生成响应:如果process_read()成功解析出一个完整的HTTP请求,它会返回一个代表请求状态的HTTP_CODE值,而不是NO_REQUEST。随后,process_write(read_ret)函数会被调用,以根据解析的请求生成相应的HTTP响应process_write()函数根据process_read()的返回值来准备HTTP响应数据,并返回一个布尔值,表示响应是否成功生成。

  4. 处理生成响应失败的情况:如果process_write()返回false,表明在准备响应时遇到了问题,可能是资源不足或内部错误。此时,会调用close_conn()来关闭这个连接

  5. 设置为可写事件:无论process_write()是否成功,最后都会调用modfd(m_epollfd, m_sockfd, EPOLLOUT),将这个socket的事件设置为EPOLLOUT(即可写事件)。这是因为无论如何都需要向客户端发送数据(即HTTP响应),哪怕是一个错误消息。设置为EPOLLOUT后,当socket的发送缓冲区可写时,epoll会通知应用程序,应用程序随后可以写入HTTP响应数据。

这个流程展示了处理HTTP请求的典型步骤:读取请求、解析请求、生成响应,并根据请求的处理结果调整socket的事件注册,以便继续进行数据的读取或写入。

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

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

相关文章

如何在华为云服务器部署安防监控EasyCVR平台?

随着视频技术的快速发展&#xff0c;安防视频汇聚平台EasyCVR可支持的协议也在不断拓展&#xff0c;平台兼容多类型的协议接入&#xff0c;包括&#xff1a;国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议与SDK&#xff0c;如&#xff1a;海康ehome、海康sdk、大…

YOLOv7创新改进:SPPF创新涨点篇 | SPPELAN:SPP创新结合ELAN ,效果优于SPP、SPPF| YOLOv9

💡💡💡本文独家改进:新颖SPPF创新涨点改进,SPP创新结合ELAN,来自于YOLOv9,助力YOLOv7,将SPPELAN代替原始的SPPF 💡💡💡在多个私有数据集和公开数据集VisDrone2019、PASCAL VOC实现涨点 收录 YOLOv7原创自研 https://blog.csdn.net/m0_63774211/category…

C语言常见关键字:一文打尽

关键字 1. 前言2. 什么是关键字3. extern-声明外部符号4. auto-自动5. typedef-类型重定义&#xff08;类型重命名&#xff09;6. register-寄存器6.1 存储器6.2 register关键字的作用 7. static-静态7.1 static修饰局部变量7.1.1 代码对比7.1.2 原理分析 7.2 static修饰全局变…

Java中常见延时队列的实现方案总结

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

41. 【Linux教程】日志文件系统

前小节介绍了早期的 Linux 文件系统&#xff0c;本小节介绍 Linux 日志文件系统&#xff0c;日志文件系统是给 Linux 操作系统增加了一些安全相关能力&#xff0c;日志文件系统和早期的文件系统相比&#xff0c;它并不是先把数据写到中间存储设备&#xff0c;然后再写到存储设备…

2024/3/6打卡最短编辑距离---线性DP

题目&#xff1a; 给定两个字符串 A 和 B&#xff0c;现在要将 A 经过若干操作变为 B&#xff0c;可进行的操作有&#xff1a; 删除–将字符串 A 中的某个字符删除。插入–在字符串 A 的某个位置插入某个字符。替换–将字符串 A 中的某个字符替换为另一个字符。 现在请你求出&a…

springboot基于java的中医院门诊挂号诊断就诊系统ssm+jsp

主要研究内容&#xff1a; 医院门诊挂号系统分为护士&#xff0c;医生&#xff0c;药房&#xff0c;收费&#xff0c;管理员等权限。 护士&#xff1a;挂号、退号、查询病人。挂号——就诊科室(发热门诊、骨科、妇科等等)&#xff0c;就诊医生数据库获取&#xff0c;挂号类型—…

测试遍历1e5,1e8数组耗时

1e8大概0.38秒&#xff0c;即380ms 1e5耗时1ms左右&#xff1a; 代码使用方式来自&#xff1a;clock - C Reference (cplusplus.com)

Java 与 JavaScript 区别

Java 与 JavaScript 区别 制造商不同 Java 是 SUN Microsystems 公司推出的新一代面向对象的程序设计语言。javascript 是 Netscape 公司的产品&#xff0c;其目的是为了扩展 Netscape Navigator 功能&#xff0c;而开发的一种可以嵌入 Web 页面中的基于对象和事件驱动的解释…

python_读取txt文件绘制多条曲线III

先把文件中指定列&#xff0c;去重提取出来&#xff0c;然后根据指定列去匹配数据&#xff0c;最后完成多条数据的绘图&#xff1b; import matplotlib.pyplot as plt import re from datetime import datetime from pylab import mplmpl.rcParams["font.sans-serif"…

【python 1】----Pytest基础知识

定义 用于编写和执行Python测试全功能测试框架&#xff08;工具&#xff09;&#xff0c;是一个第三方库 安装 pip insatll pytest 安装pytest --version 校验 pytest的组成构成 不写调用语句也可以执行函数内容 在用例运行语句里面&#xff1a; -s:指的是开启与终端的…

vue3 setup函数与setup语法糖之间的区别

普通setup函数构建的组件 import {ref} from vueexport default {setup(){const countref(0)const handleUpdate()>{count.value}return{count,handleUpdate}}} </script>使用setup语法糖构建的组件 <script setup>import {ref} from vueconst countref(0)con…

devc++小游戏3.8.5

导航&#xff1a; Dev-c跑酷小游戏 1.0.0 devc跑酷小游戏1.2.5 devc跑酷游戏1.2.6 devc跑酷游戏2.0.0 devc跑酷游戏2.0.1 devc跑酷游戏2.4.0 devc跑酷小游戏3.5.0 更新内容 重磅回归&#xff0c;存档搞定&#xff01;&#xff01;&#xff01; 每一关需要前一关已…

C#日志记录:实现应用程序的监控与调试

日志记录是软件开发中不可或缺的功能&#xff0c;它能帮助开发者在应用程序运行时记录重要信息&#xff0c;便于调试和监控。本文将详细介绍C#中的常用日志记录功能以及常用的日志库&#xff0c;包括日志级别控制、日志输出格式、自定义日志目标、结构化日志和异步日志记录。同…

【Linux】常见指令1(ls指令、pwd指令、cd指令、touch指令、mkdir指令、rmdir指令、man指令、cp指令、mv指令、cat指令)

目录 01.ls指令与ll指令 02.pwd指令 03.cd指令 04.touch指令 05.mkdir指令 06.rmdir指令&&rm指令 07.man指令 08.cp指令 09.mv指令 10.cat指令 01.ls指令与ll指令 ls指令&#xff1a; 原型&#xff1a;list directory contents 语法&#xff1a;ls[选项][目录…

nodejs安装教程(及过程中的易错)

nodejs&#xff1a;Nodejs 是基于 Chrome 的 V8 引擎开发的一个 C 程序&#xff0c;目的是提供一个 JS 的运行环境。 npm&#xff1a;npm 是 Node Package Manager 的缩写&#xff0c;意思是 Node 的包管理系统&#xff0c;是最大的软件包仓库 下载nodejs 首先我们需要在node…

数学算法笔记

1、平方差 [蓝桥杯 2023 省 A] 平方差 - 洛谷 考虑将公式化简&#xff0c;然后看x是由什么性质的数组成&#xff0c;该题中&#xff0c;从x奇偶性质入手&#xff0c;判断x可能的组成情况。 题解&#xff1a;Welcome - Luogu Spilopelia

抖店无货源怎么做?最新玩法分享,看这篇就懂了!

我是电商珠珠 抖店一直热度很高&#xff0c;都在讲的无货源玩法&#xff0c;对于新手来说很陌生。 今天呢&#xff0c;我就来详细的讲一下抖店无货源玩法。 第一步&#xff0c;入驻 入驻的时候需要准备一张个体的营业执照&#xff0c;以及个人的身份证、银行卡。 资金的话…

C# SwinV2 Stable Diffusion 提示词反推 Onnx Demo

目录 介绍 效果 模型信息 项目 代码 下载 C# SwinV2 Stable Diffusion 提示词反推 Onnx Demo 介绍 模型出处github地址&#xff1a;https://github.com/SmilingWolf/SW-CV-ModelZoo 模型下载地址&#xff1a;https://huggingface.co/SmilingWolf/wd-v1-4-swinv2-tagg…

python-数组元素积的符号-LEETCODE

方法一&#xff1a;笨蛋方法&#xff0c;直接相乘 def arraySign(nums): sum1 for i in range(len(nums)): sumnums[i]*sum if sum>0: return 1 if sum<0: return -1 if sum0: return 0 方法二&#xff1a;判断是…