libevent高并发网络编程 - 04_libevent实现http服务器

news2025/1/12 13:31:11

文章目录

    • 1 evhttp简介
    • 2 相关的API
      • evhttp_new()
      • evhttp_free()
      • evhttp_bind_socket()
      • evhttp_set_gencb()
      • evhttp_set_cb()
      • evhttp_request_get_uri()
      • evhttp_request_get_command()
      • evhttp_request_get_input_headers()
      • evhttp_request_get_input_buffer()
      • evhttp_request_get_output_headers()
      • evhttp_add_header()
      • evhttp_request_get_output_buffer()
      • evhttp_send_reply()
    • http服务器例子
      • http服务器代码
      • index.html文件
      • 运行效果

1 evhttp简介

在libevent中,HTTP的实现主要是通过evhttp模块来完成的。evhttp提供了一个高层次的HTTP服务器接口,可以处理HTTP请求并发送HTTP响应。

在源码中,libevent的HTTP协议处理主要是通过evhttp模块来完成的。当客户端发起一个HTTP请求时,libevent将该请求解析为struct evhttp_request结构体表示,并调用用户设置的请求处理函数进行处理

struct evhttp_request结构体定义了HTTP请求的各个字段,如请求行、请求头、请求正文等。例如,以下是struct evhttp_request结构体的部分定义:

struct evhttp_request {
    int major;  // 主版本号
    int minor;  // 次版本号
    enum evhttp_cmd_type type;  	// 请求方法(GET、POST等)
    char *uri;  					// 请求URI
    struct evkeyvalq *input_headers;// 请求头
    struct evbuffer *input_buffer;  // 请求正文
};

enum evhttp_cmd_type {
	EVHTTP_REQ_GET     = 1 << 0,
	EVHTTP_REQ_POST    = 1 << 1,
	EVHTTP_REQ_HEAD    = 1 << 2,
	EVHTTP_REQ_PUT     = 1 << 3,
	EVHTTP_REQ_DELETE  = 1 << 4,
.....
};

当接收到HTTP请求数据时,libevent将会使用一个状态机来逐步解析HTTP请求。在此过程中,它将从输入流中读取一些数据并按照HTTP协议格式解析这些数据,以获得请求的不同部分。具体来说,HTTP请求包括请求行、请求头和请求正文三个部分,每个部分都有其特定的格式。

将HTTP请求解析为struct evhttp_request结构体,libevent就会调用用户设置的请求处理函数来处理该请求。对于GET请求等不包含请求正文的请求,可以直接在请求处理函数中进行处理;对于POST请求等包含请求正文的请求,则需要从struct evhttp_request结构体中获取请求正文数据并进行处理。

总之,libevent使用状态机来逐步解析HTTP请求,并将请求解析为struct evhttp_request结构体表示。这种设计使得libevent能够高效地处理HTTP请求,同时也方便了开发者对HTTP协议进行扩展和定制。

2 相关的API

evhttp_new()

evhttp_new 用于创建和初始化一个 evhttp 结构体,以便用于 HTTP 服务器。 evhttp 结构体表示一个 HTTP 服务器,它可以在一个或多个套接字上监听传入的 HTTP 请求,并将这些请求分派给适当的请求处理程序。

struct evhttp * evhttp_new(struct event_base *base)
    
base:即指向 `event_base` 结构体的指针,用于处理 HTTP 服务器的事件

evhttp_free()

evhttp_free() 用于释放 evhttp 结构体及其相关资源。调用该函数后,将销毁与 evhttp 相关联的所有信息,包括绑定的套接字以及任何未完成的请求。

void evhttp_free(struct evhttp* http)

evhttp_bind_socket()

evhttp_bind_socket用于将HTTP服务器绑定到指定的IP地址和端口号。它接受一个指向evhttp结构体的指针,一个字符串表示IP地址,以及一个16位整数表示端口号。

int evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port)

参数:
    http:指向要绑定的evhttp对象的指针。
    address:要绑定的IP地址。可以是一个IPv4或IPv6地址,也可以是一个域名。如果为NULL,则默认绑定到所有可用地址。
    port:要绑定的端口号。

evhttp_set_gencb()

evhttp_set_gencb() 是 libevent 库中的一个函数,用于设置通用回调函数,该函数将在 HTTP 请求到达时被调用。

void
evhttp_set_gencb(struct evhttp *http,
   				 void (*cb)(struct evhttp_request *, void *), 
                 void *cbarg)
    
	第一个参数是指向 evhttp 结构体的指针,表示要设置回调函数的 HTTP 服务器;
	第二个参数是指向回调函数的指针,该回调函数的原型为 void (*cb)(struct evhttp_request *, void *),其中第一个参数是指向当前 HTTP 请求的指针,第二个参数是传递给 evhttp_set_gencb() 函数的第三个参数;
    第三个参数是传递给回调函数的上下文参数 cbarg,它可以是任何数据类型的指针;

当 HTTP 请求到达并且没有与之匹配的特定 URI 或处理程序时,将调用设置的通用回调函数。注意,如果已经为某个 URI 注册了特定的处理程序,则不会调用通用回调函数来处理该 URI 的请求。

evhttp_set_cb()

evhttp_set_cb() 用于为 HTTP 服务器注册特定 URI 的请求处理程序回调函数。

evhttp_set_cb(struct evhttp *http, 
			  const char *uri,
    		  void (*cb)(struct evhttp_request *, void *), 
    		  void *cbarg)
	第一个参数是指向 evhttp 结构体的指针,表示要设置回调函数的 HTTP 服务器;
	第二个参数是字符串类型的 URI,表示要注册回调函数的 URI;
	第三个参数是指向回调函数的指针,该回调函数的原型为 void (*cb)(struct evhttp_request *, void *),其中第一个参数是指向当前 HTTP 请求的指针,第二个参数是传递给 evhttp_set_cb() 函数的第四个参数;
	四个参数是传递给回调函数的上下文参数 cbarg,它可以是任何数据类型的指针。

当 HTTP 请求到达并且与所注册的 URI 匹配时,将调用设置的回调函数。注意,如果同时为相同的 URI 注册了多个回调函数,则只会调用最后一个注册的回调函数。

evhttp_request_get_uri()

evhttp_request_get_uri() 用于获取 HTTP 请求的 URI(Uniform Resource Identifier)。

const char *
evhttp_request_get_uri(const struct evhttp_request *req) {
	if (req->uri == NULL)
		event_debug(("%s: request %p has no uri\n", __func__, (void *)req));
	return (req->uri);	//返回struct evhttp_reques中的URI
}

该函数只有一个参数,即指向 `evhttp_request` 结构体的指针,表示要获取 URI 的 HTTP 请求

函数返回一个字符串类型的指针,指向 HTTP 请求的 URI。URI 是客户端在请求服务器时发送的字符串,该字符串通常包含一个主机名、路径和查询字符串。例如,对于 “http://example.com/foo?bar=baz” 这样的请求,URI 将是 “/foo?bar=baz”。

需要注意的是,返回的指针指向的内存由 evhttp_request 结构体管理,因此不需要手动释放该指针指向的内存。

evhttp_request_get_command()

evhttp_request_get_command() 用于获取 HTTP 请求的方法类型。

enum evhttp_cmd_type
evhttp_request_get_command(const struct evhttp_request *req) {
	return (req->type);	//返回struct evhttp_reques中请求方法(GET、POST等)
}

函数返回一个枚举类型 evhttp_cmd_type 值,表示 HTTP 请求使用的方法类型。枚举类型包括以下值:
    EVHTTP_REQ_GET: 使用 GET 方法。
    EVHTTP_REQ_POST: 使用 POST 方法。
    EVHTTP_REQ_HEAD: 使用 HEAD 方法。
    EVHTTP_REQ_PUT: 使用 PUT 方法。
    EVHTTP_REQ_DELETE: 使用 DELETE 方法。
    EVHTTP_REQ_OPTIONS: 使用 OPTIONS 方法。
    EVHTTP_REQ_TRACE: 使用 TRACE 方法。
    EVHTTP_REQ_CONNECT: 使用 CONNECT 方法。
    EVHTTP_REQ_PATCH: 使用 PATCH 方法。

evhttp_request_get_input_headers()

evhttp_request_get_input_headers() 用于获取 HTTP 请求中的输入头部。输入头部是从客户端发送到服务器的数据块,其中包含一些元信息,例如请求的内容类型、长度或来源等。

struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req)
{
	return (req->input_headers);	// 返回struct evhttp_reques中请求头
}

函数返回一个指向 evkeyvalq 结构体的指针,该结构体代表了 HTTP 请求的输入头部。 该结构体是一个键值对列表,其中每个键都是头部的名称,每个值都是头部的值。

evhttp_request_get_input_buffer()

evhttp_request_get_input_buffer() 用于获取 HTTP 请求的输入缓冲区。输入缓冲区是从客户端发送到服务器的数据块,其中包含了 HTTP 请求体以及可能的其他数据。

struct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req)
{
	return (req->input_buffer);	// 返回struct evhttp_reques中请求正文
}

函数返回一个指向 evbuffer 结构体的指针,该结构体代表了 HTTP 请求的输入缓冲区。

evhttp_request_get_output_headers()

evhttp_request_get_output_headers() 用于获取 HTTP 请求的输出头部。输出头部是从服务器发送到客户端的元信息块,其中包含了一些关于响应的信息,例如响应状态码、内容类型或其他自定义头部等。

struct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req)
{
	return (req->output_headers);
}

函数返回一个指向 evkeyvalq 结构体的指针,该结构体代表了 HTTP 请求的输出头部。 该结构体是一个键值对列表,其中每个键都是头部的名称,每个值都是头部的值。

evhttp_add_header()

evhttp_add_header()用于向 evkeyvalq 结构体表示的头部列表添加一个新的键值对

int	evhttp_add_header(struct evkeyvalq *headers,
    				  const char *key, 
                      const char *value)
    
第一个参数是指向 evkeyvalq 结构体的指针,表示要添加头部的头部列表;
第二个参数是字符串类型的键名,表示要添加的头部的名称;
第三个参数是字符串类型的键值,表示要添加的头部的值。

需要注意的是,如果某个键已经存在于头部列表中,则会将现有键值对的值替换为新的值。如果要添加多个具有相同名称的头部,请使用逗号分隔它们的值。例如,可以使用"Accept-Encoding: gzip, deflate"来同时添加两个Accept-Encoding头部。

evhttp_request_get_output_buffer()

evhttp_request_get_output_buffer() 用于获取 HTTP 请求的输出缓冲区。输出缓冲区是服务器发送到客户端的数据块,其中包含了 HTTP 响应体以及可能的其他数据。

struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req)
{
	return (req->output_buffer);
}

函数返回一个指向 evbuffer 结构体的指针,该结构体代表了 HTTP 请求的输出缓冲区。可以使用 evbuffer_add() 函数将内容添加到缓冲区中。

evhttp_send_reply()

evhttp_send_reply() 用于向客户端发送 HTTP 响应。该函数使用指定的状态码、原因短语和响应正文来构造 HTTP 响应。

void
evhttp_send_reply(struct evhttp_request *req, 
                 int code, 
                 const char *reason,
    			 struct evbuffer *databuf)
    
	第一个参数是指向 evhttp_request 结构体的指针,表示要发送响应的 HTTP 请求;
    第二个参数是整数类型的状态码,表示响应的状态码;
    第三个参数是字符串类型的原因短语,表示状态码的原因短语;
    第四个参数是指向 evbuffer 结构体的指针,表示响应正文的内容。

需要注意的是,在调用 evhttp_send_reply() 函数之前,必须确保已经设置了响应状态码和必要的响应头部,并且已经将所有响应数据写入到输出缓冲区中。

http服务器例子

通过浏览器发送http请求,并解析url和http头部,并响应浏览器对文件和图片的请求。

http服务器代码

#include <event2/event.h>
#include <event2/listener.h>
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#include <event2/buffer.h>
#include <string.h>
#ifndef _WIN32
#include <signal.h>
#endif
#include <iostream>
#include <string>
using namespace std;

#define WEBROOT "." 
#define DEFAULTINDEX "index.html"

void http_cb(struct evhttp_request *request, void *arg)
{
	cout << "http_cb" << endl;
	//1 获取浏览器的请求信息
	//uri 
	const char *uri = evhttp_request_get_uri(request);
	cout << "uri:" << uri << endl;

	//请求类型 GET POST
	string cmdtype;
	switch (evhttp_request_get_command(request))
	{
	case EVHTTP_REQ_GET:
		cmdtype = "GET";
		break;
	case EVHTTP_REQ_POST:
		cmdtype = "POST";
		break;
	}
	cout << "cmdtype:" << cmdtype << endl;

	// 消息报头
	evkeyvalq *headers = evhttp_request_get_input_headers(request);
	cout << "====== headers ======" << endl;
	for (evkeyval *p = headers->tqh_first; p != NULL; p = p->next.tqe_next)
	{
		cout << p->key << ":" << p->value << endl;
	}

	// 请求正文 (GET为空,POST有表单信息  )
	evbuffer *inbuf = evhttp_request_get_input_buffer(request);
	char buf[1024] = { 0 };
	cout << "======= Input data ======" << endl;
	while (evbuffer_get_length(inbuf))
	{
		int n = evbuffer_remove(inbuf, buf, sizeof(buf) - 1);
		if (n > 0)
		{
			buf[n] = '\0';
			cout << buf << endl;
		}
	}

	//2 回复浏览器
	//状态行 消息报头 响应正文 HTTP_NOTFOUND HTTP_INTERNAL
	string filepath = WEBROOT;
	filepath += uri;
	if (strcmp(uri, "/") == 0)
	{
		//默认加入首页文件
		filepath += DEFAULTINDEX;
	}

	//消息报头
	evkeyvalq *outhead = evhttp_request_get_output_headers(request);
	//  要支持 图片 js css 下载zip文件
	//  获取文件的后缀
	// ./index.html
	int pos = filepath.rfind('.');
	string postfix = filepath.substr(pos + 1, filepath.size() - (pos + 1));
	if (postfix == "jpg" || postfix == "gif" || postfix == "png")
	{
		string tmp = "image/" + postfix;
		evhttp_add_header(outhead, "Content-Type", tmp.c_str());
	}
	else if (postfix == "zip")
	{
		evhttp_add_header(outhead, "Content-Type", "application/zip");
	}
	else if (postfix == "html")
	{
		evhttp_add_header(outhead, "Content-Type", "text/html;charset=UTF8");
		//evhttp_add_header(outhead, "Content-Type", "text/html");
	}
	else if (postfix == "css")
	{
		evhttp_add_header(outhead, "Content-Type", "text/css");
	}

	//读取html文件返回正文
	FILE *fp = fopen(filepath.c_str(), "rb");
	if (!fp)
	{
		evhttp_send_reply(request, HTTP_NOTFOUND, "", 0);
		return;
	}
	evbuffer *outbuf = evhttp_request_get_output_buffer(request);
	for (;;)
	{
		int len = fread(buf, 1, sizeof(buf), fp);
		if (len <= 0)break;
		evbuffer_add(outbuf, buf, len);
	}
	fclose(fp);
	evhttp_send_reply(request, HTTP_OK, "", outbuf);
}

int main(int argc,char *argv[])
{
    
#ifdef _WIN32 
	//初始化socket库
	WSADATA wsa;
	WSAStartup(MAKEWORD(2,2),&wsa);
#else
	//忽略管道信号,发送数据给已关闭的socket
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		return 1;
#endif

	std::cout << "test server!\n"; 
	//创建libevent的上下文
	event_base * base = event_base_new();
	if (base)
	{
		cout << "event_base_new success!" << endl;
	}

	// http  服务器
	//1	创建evhttp上下文
	evhttp *evh = evhttp_new(base);

	//2  绑定端口和IP
	if (evhttp_bind_socket(evh, "0.0.0.0", 8080) != 0)
	{
		cout << "evhttp_bind_socket failed!" << endl;
	}

	//3   设定回调函数
	evhttp_set_gencb(evh, http_cb, 0);

	//事件分发处理
	if(base)
		event_base_dispatch(base);
	if(base)
		event_base_free(base);
	if(evh)
		evhttp_free(evh);

	return 0;
}

index.html文件

<HTML>
<HEAD>
<meta charset= "UTF8" />
<TITLE>test http server</TITLE>
</HEAD>
<BODY>
    <h1>test http server</h1>
    1234567890
      测试中文  
    <img src="test.jpg">
    <form method="post">
        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="submit" name="submit"/>
    </form>
</BODY>
</HTML>

运行效果

请添加图片描述

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

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

相关文章

MagicalCoder系列教程(二):如何下载本地安装启动项目?

目录 下载启动 1 windows下载 2 linux下载 3 安装启动方式&#xff1a; 3.1 Windows免安装 3.2 其他操作系统&#xff08;Mac等其他操作系统&#xff09; 3.3 启动安装失败 MagicalCoder可视化开发平台&#xff1a;轻松搭建业务系统&#xff0c;为企业创造更多价值&…

【故障排查】【杂项】ingress + Jenkins 上传hpi文件(8MB)报错413

1、ingress Jenkins 上传hpi文件&#xff08;8MB&#xff09;报错 413 Request Entity Too Large 处理方法&#xff1a;ingress文件加上annotations ingress-nginx 实际上为nginx&#xff0c;所以在此调整。apiVersion: extensions/v1beta1 kind: Ingress metadata:name: jen…

Dreaming of Freedom

题目&#xff1a; 题意解析&#xff1a; n 个程序员要在 m 个算法里选出最受欢迎的算法&#xff0c;每轮投票每个程序员都会在剩下的算法中选择一个。 在第一轮投票前&#xff0c;m 种算法都可以选择&#xff1b;每轮投票后&#xff0c;只保留有最多票数的算法&#xff1b;只…

蓝桥杯提单day2【题解】

题单链接 蓝桥杯题单day2【题目】_奔跑的星黛露的博客-CSDN博客 题解 蓝肽子序列 链接 https://www.lanqiao.cn/problems/1030/learning/?page1&first_category_id1&sortstudents_count&second_category_id3&tags%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92,%E…

VRRP高级特性——管理VRRP

目录 管理VRRP备份组与业务VRRP备份组 管理VRRP备份组的两种实现方式 配置管理备份组 当在设备上配置了多个VRRP备份组时&#xff0c;为了减少设备间交互大量的VRRP协议报文&#xff0c;可以将其中一个VRRP备份组配置为管理VRRP备份组&#xff08;mVRRP&#xff09;&#xf…

WordPress PHP版本:2023年用于WordPress的最佳PHP版本

不同的 WordPress 版本可以与多个 PHP 版本一起使用。然而&#xff0c;WordPress社区官方为每个 WP 版本推荐了特定的 PHP 要求。选择最好的 WordPress PHP 版本对于确保网站安全且没有错误至关重要。 本文从 WordPress 的推荐、最低和最新 PHP 版本的角度讨论了 PHP 和 WordP…

SpringBoot——如何获取配置文件中的配置信息

SpringBoot——如何获取配置文件中的配置信息 简单介绍&#xff1a; 之前我们看到了我们是如何对SpringBoot以及SpringBoot中不同技术进行配置的&#xff0c;我们舍弃了之前在Spring中的对不同的技术使用不同的配置信息的方式&#xff0c;使用将所有技术的配置写在了一个配置…

Mac(苹果)安装nvm进行node版本控制

Mac&#xff08;苹果&#xff09;安装nvm进行node版本控制 错误演示nvm安装下载命令更改网络环境配置环境 相信我一般的初学者问题看这一片就够了 错误演示 如上图所示&#xff0c;node的版本&#xff0c;与当前项目中的一些框架或者组件不支持时一般就会报这个错&#xff0c;…

一天吃透SpringCloud面试八股文

1、什么是Spring Cloud &#xff1f; Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序&#xff0c;提供与外部系统的集成。Spring cloud Task&#xff0c;一个生命周期短暂的微服务框架&#xff0c;用于快速构建执行有限数据处理的应用程序。 Sprin…

Portraiture4.1.0最新版PS/LR磨皮美白滤镜插件

Portraiture这款插件磨皮效率超高 是99%摄影师的必备插件 一秒磨皮&#xff0c;无卡顿&#xff0c;效果好 最新版Portraiture 2023磨皮效果提升很大 之前发布了Portraitrue4.0.3版 现已更新!本期带来最新版&#xff1a; 磨皮插件Portraiture 4.1.0版!portraiture最新版本…

LITCTF2023 部分WP

WEB 我Flag呢&#xff1f; 查看源码就有flag了 彩蛋 导弹迷踪 Follow me and hack me 然后彩蛋www.zip解压即可 PHP是世界上最好的语言&#xff01;&#xff01; 直接可以命令执行,当时想了好久 Vim yyds 扫目录,有swp文件 作业管理系统 任意文件上传,传个马访问就…

十二、目标检测

文章目录 1、 目标定位2、特征点检测3、卷积网络的滑动窗口4、交并比(Intersection over Union)5、非最大值抑制(Non Max Suppression)6、anchor boxTHE END 1、 目标定位 \qquad 目标检测是图像识别中的更加复杂的应用情景&#xff0c;图像识别只需要输出某一幅图像的种类即可…

React面试题汇总1

1.React的严格模式如何使用&#xff0c;有什么用处&#xff1f; React中StrictMode严格模式_react.strictmode_前端精髓的博客-CSDN博客当我们使用 npx create-react-app my-app 创建一个项目的时候。项目中有一段如下所示的代码&#xff1a;ReactDOM.render( <React.Stric…

LeetCode_Day2 | 有意思的数组滑动窗口及螺旋矩阵

LeetCode_数组 977.有序数组的平方1.题目描述2.暴力法3. 双指针法 209.长度最小的子数组1.题目描述2.暴力法3.滑动窗口(双指针法) 59.螺旋矩阵1.题目描述2. 螺旋矩阵解法 977.有序数组的平方 1.题目描述 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字…

解决IDEA中集成Tomcat日志出现的乱码问题

解决IDEA中集成Tomcat日志出现的乱码问题 下面的方式可以解决的是解决的是idea中出现的控制台&#xff0c;Localhost日志以及Catalina日志乱码的问题 1.出现的乱码问题 你是不是也是遇到了这种的乱码的情况呢&#xff1f; 2.解决方法 1.打开conf文件夹。 2.设置输出的日志的编…

【小沐学Python】Python实现在线英语翻译功能

文章目录 1、简介2、在线翻译接口2.1 Google Translate API2.2 Microsoft Translator API2.2.1 开发简介2.2.2 开发费用2.2.3 开发API 2.3 百度翻译开放平台 API2.3.1 开发简介2.3.2 开发费用2.3.3 开发API 2.4 Tencent AI 开放平台的翻译 API2.4.1 开发简介2.4.2 开发API 2.5 …

人工智能基础部分16-神经网络与GPU加速训练

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能基础部分15-神经网络与GPU加速训练&#xff0c;在深度学习领域&#xff0c;神经网络已经成为了一种流行的、表现优秀的技术。然而&#xff0c;随着神经网络的规模越来越大&#xff0c;训练神经网络所需的时…

Kafka Connect JNDI注入漏洞复现(CVE-2023-25194)

漏洞原理 Apache Kafka Connect中存在JNDI注入漏洞&#xff0c;当攻击者可访问Kafka Connect Worker&#xff0c;且可以创建或修改连接器时&#xff0c;通过设置sasl.jaas.config属性为com.sun.security.auth.module.JndiLoginModule&#xff0c;进而可导致JNDI注入&#xff0c…

数字设计小思 - 谈谈非理想时钟的时钟偏差

写在前面 本系列整理数字系统设计的相关知识体系架构&#xff0c;为了方便后续自己查阅与求职准备。在FPGA和ASIC设计中&#xff0c;时钟信号的好坏很大程度上影响了整个系统的稳定性&#xff0c;本文主要介绍了数字设计中的非理想时钟的偏差来源与影响。 &#xff08;本文长…

数据结构-排序-(直接插入、折半插入、希尔排序、冒泡、快速排序)

目录 一、直接插入排序 二、折半插入排序 三、希尔排序 四、冒泡排序 五、快速排序 *效率分析 一、直接插入排序 思想&#xff1a;每次将一个待排序的记录按其关键字大小插入到前面已经排好序中&#xff0c;直到全部记录插入完毕 保证稳定性 空间复杂度&#xff1a;O(1…