c++实现httpd服务器

news2024/11/15 17:21:39

文章目录

  • 网络的初始化
    • 端口的分配
    • 协议的使用
      • WSAStartup函数
        • 参数1:`WORD wVersionRequested`
        • 参数2:LPWSADATA lpWSAData
        • 返回值 int
        • 代码实现
    • 创建套接字
      • int af表示套接字的类型(网络套接字 文件套接字)
      • int type数据流 数据报
      • int protocol 通信协议 TCP & UDP
      • 返回值
      • 代码
      • 优化
    • 设置端口套接字可复用特性(端口复用)
    • 绑定套接字和网络地址
    • 动态分配一个端口
    • 创建监听队列
    • 创建 客户端的访问
    • 使用多线程进行用户的访问
      • 使用CreateThread函数创建线程
  • Http请求的流程
    • 第一行报文详细说明
    • 响应报文的格式
    • POST请求报文的格式
  • 报文的分析

网络的初始化

这是网络通信需要包含的头文件以及库文件

#include <WinSock2.h>
#pragma comment (lib,"WS2_32.lib")

附加一个 需要利用里面的一些定义

#include <Windows.h>

定义如下

在这里插入图片描述
在这里插入图片描述

我们进行严格的端口定义是需要定义为 WORD 即 unsigned short

端口的分配

为了方便端口的调配 ,我们讲端口的分配设置为动态 其中的InterInit函数为网络初始化函数

WORD port = 0;
   int server_socker = InterInit(&port);

然后打印出信息

	printf("httpd服务已经启动,正在监听 %d 端口..", port);

总的代码是这样的

#include <stdio.h>
#include <Windows.h>
#include <WinSock2.h>
#pragma comment (lib,"WS2_32.lib")

int InterInit(PWORD port);


int InterInit(PWORD port)//实现网络的初始化
{
	 
	return;
}


int main(void)
{

	WORD port = 0;
	int server_socker = InterInit(&port);
	printf("httpd服务已经启动,正在监听 %d 端口..", port);
	return 0;
}

协议的使用

在这里插入图片描述

官方文档

我们需要用到这个函数进行网路的初始化

WSAStartup函数

int WSAAPI WSAStartup(
  WORD      wVersionRequested,
  LPWSADATA lpWSAData
);

WSAStartup

  • W:windows
  • S:socket
  • A:Asynchronous 异步
    • 同步:阻塞、卡死状态
    • 异步:多个工作同时进行
  • Startup:启动

参数1:WORD wVersionRequested

调用者可以使用的Windows套接字规范的最高版本。 高位字节指定次要版本号; 低位字节指定主要版本号。

在这里插入图片描述

#define MAKEWORD(a, b)      ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
#define LOWORD(l)           ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l)           ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define LOBYTE(w)           ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w)           ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))

参数2:LPWSADATA lpWSAData

指向WSADATA数据结构的指针,该数据结构将接收Windows套接字实现的详细信息。

在这里插入图片描述

typedef struct WSAData {
        WORD                    wVersion;//我们要使用的版本
        WORD                    wHighVersion;//系统能提供给我们的版本
#ifdef _WIN64
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
#else
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];//当前库的秒数信息,2.0是第2版的意思
        unsigned short          iMaxSockets;//返回可用的socket数量,2版本只有就没用了
        unsigned short          iMaxUdpDg;//UDP数据报信息大小,2版本只有就没用了
        char FAR *              lpVendorInfo;//供应商特定的信息,2版本只有就没用了
#endif
} WSADATA;

返回值 int

如果成功,则WSAStartup函数将返回0。 否则,它将返回下面列出的错误代码之一。
WSAStartup函数直接在该函数的返回值中返回扩展错误代码。 不需要调用WSAGetLastError函数,并且不应使用该调用。

Error codeMeaning
WSASYSNOTREADY基础网络子系统尚未准备好进行网络通信。
WSAVERNOTSUPPORTED此特定的Windows套接字实现未提供所请求的Windows套接字支持的版本。
WSAEINPROGRESSWindows Sockets 1.1的阻止操作正在进行中。
WSAEPROCLIMWindows套接字实现所支持的任务数已达到限制。
WSAEFAULTlpWSAData 参数不是有效的指针

代码实现

int InterInit(PWORD port)//实现网络的初始化
{
	 
	WSADATA data;//初始化数据
	//网络通信初始化
	int ret = WSAStartup(MAKEWORD(1, 1), &data);

	if (ret)
	{
		switch (ret)
		{
		case WSASYSNOTREADY:
			printf("请检查网络库");
			break;
		case WSAVERNOTSUPPORTED:
			printf("请更新网络库");
			break;
		case WSAEINPROGRESS:
			printf("请重新启动");
			break;
		case WSAEPROCLIM:
			printf("请尝试关闭不必要软件,以为当前网络运行提供充足的资源");
			break;
		}
		return -1;
	}
	//返回值:套接字	
	return;
}

创建套接字

在这里插入图片描述

SOCKET WSAAPI socket(
  int af, //地址族规范。 地址族的可能值在Winsock2.h头文件中定义。
  int type,//新套接字的类型规范。
  int protocol//要使用的协议。
);

int af表示套接字的类型(网络套接字 文件套接字)

#define PF_UNSPEC       AF_UNSPEC
#define PF_UNIX         AF_UNIX
#define PF_INET         AF_INET
#define PF_IMPLINK      AF_IMPLINK
#define PF_PUP          AF_PUP
#define PF_CHAOS        AF_CHAOS
#define PF_NS           AF_NS
#define PF_IPX          AF_IPX
#define PF_ISO          AF_ISO
#define PF_OSI          AF_OSI
#define PF_ECMA         AF_ECMA
#define PF_DATAKIT      AF_DATAKIT
#define PF_CCITT        AF_CCITT
#define PF_SNA          AF_SNA
#define PF_DECnet       AF_DECnet
#define PF_DLI          AF_DLI
#define PF_LAT          AF_LAT
#define PF_HYLINK       AF_HYLINK
#define PF_APPLETALK    AF_APPLETALK
#define PF_VOICEVIEW    AF_VOICEVIEW
#define PF_FIREFOX      AF_FIREFOX
#define PF_UNKNOWN1     AF_UNKNOWN1
#define PF_BAN          AF_BAN
#define PF_ATM          AF_ATM
#define PF_INET6        AF_INET6
AfMeaning
AF_UNSPEC 0地址族未指定。
AF_INET 2Internet协议版本4(IPv4)地址族。
AF_IPX 6IPX / SPX地址族。 仅当安装了NWLink IPX / SPX NetBIOS兼容传输协议时,才支持此地址系列。WindowsVista和更高版本不支持此地址系列。
AF_APPLETALK 16AppleTalk地址族。 仅当安装了AppleTalk协议时才支持此地址系列。WindowsVista和更高版本不支持此地址系列。
AF_INET6 23Internet协议版本6(IPv6)地址族。
AF_IRDA 26红外数据协会(IrDA)地址族。仅当计算机安装了红外端口和驱动程序时,才支持此地址族。
AF_NETBIOS 17NetBIOS地址族。 仅当安装了用于NetBIOS的Windows套接字提供程序时,才支持此地址系列。在32位版本的Windows上支持用于NetBIOS的Windows套接字提供程序。 默认情况下,此提供程序安装在32位版本的Windows上.NetBIOS的Windows套接字提供程序在64位版本的Windows(包括Windows 7,Windows Server 2008,Windows Vista,Windows Server 2003或Windows XP)上不受支持。 用于NetBIOS的Windows套接字提供程序仅支持将* type 参数设置为* SOCK_DGRAM **的套接字。用于NetBIOS的Windows套接字提供程序与[NetBIOS]不直接相关(https://docs.microsoft.com/en -us / previous-versions / windows / desktop / netbios / portal)编程界面。 Windows Vista,Windows Server 2008和更高版本不支持NetBIOS编程接口。
AF_BTH 32蓝牙地址系列。如果计算机安装了蓝牙适配器和驱动程序,则Windows XP SP2或更高版本支持此地址系列。

int type数据流 数据报

#define SOCK_STREAM     1               /* stream socket */
#define SOCK_DGRAM      2               /* datagram socket */
#define SOCK_RAW        3               /* raw-protocol interface */
#define SOCK_RDM        4               /* reliably-delivered message */
#define SOCK_SEQPACKET  5       

在这里插入图片描述

在Windows套接字1.1中,唯一可能的套接字类型是SOCK_DGRAM和SOCK_STREAM

int protocol 通信协议 TCP & UDP

#define IPPROTO_IP              0               /* dummy for IP */
#define IPPROTO_ICMP            1               /* control message protocol */
#define IPPROTO_IGMP            2               /* group management protocol */
#define IPPROTO_GGP             3               /* gateway^2 (deprecated) */
#define IPPROTO_TCP             6               /* tcp */
#define IPPROTO_PUP             12              /* pup */
#define IPPROTO_UDP             17              /* user datagram protocol */
#define IPPROTO_IDP             22              /* xns idp */
#define IPPROTO_ND              77              /* UNOFFICIAL net disk proto */

在这里插入图片描述

返回值

如果没有发生错误,socket将返回一个引用新socket的描述符。否则,将返回INVALID_SOCKET 的值,并且可以通过调用WSAGetLastError检索特定的错误代码

在这里插入图片描述

代码


int server_socket =  socket(PF_INET,SOCK_STREAM,	IPPROTO_TCP);
	
	if (server_socker == -1) {
		printf("socket function failed with error = %d\n", server_socker);
		switch (server_socker)
		{
		case WSANOTINITIALISED:
		
			printf("	A successful WSAStartup call must occur before using this function.");
			break;
			
		case WSAENETDOWN:
			printf("The network subsystem or the associated service provider has failed.");
			break;
			
		case WSAEAFNOSUPPORT:
			printf("The specified address family is not supported. For example, an application tried to create a socket for the AF_IRDA address family but an infrared adapter and device driver is not installed on the local computer.");
			break;
			
		case WSAEINPROGRESS:
			printf("A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.");
			break;
		case WSAEMFILE:
			printf("	No more socket descriptors are available.");
			break;
		case WSAEINVAL:
			printf("An invalid argument was supplied. This error is returned if the af parameter is set to AF_UNSPEC and the type and protocol parameter are unspecified.");
			break;
		case WSAEINVALIDPROVIDER:
			printf("	The service provider returned a version other than 2.2.");
			break;
		case WSAEINVALIDPROCTABLE:
			printf("The service provider returned an invalid or incomplete procedure table to the WSPStartup.");
			break;
		case WSAENOBUFS:
			printf("No buffer space is available. The socket cannot be created.");
			break;
		case WSAEPROTONOSUPPORT:
			printf("The specified protocol is not supported.");
			break;
		case WSAEPROTOTYPE:
			printf("The specified protocol is the wrong type for this socket.");
			break;
		case WSAEPROVIDERFAILEDINIT:
			printf("The service provider failed to initialize. This error is returned if a layered service provider (LSP) or namespace provider was improperly installed or the provider fails to operate correctly.				");
			break;
		case WSAESOCKTNOSUPPORT:
			printf("The specified socket type is not supported in this address family.");
			break;

		default:
			break;
		}

	}

优化

函数说明

perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 设备 (stderr) 。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量error 的值来决定要输出的字符串。在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了error的值。perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出。

void error_die(const char* str) {
	perror(str);
}

在这里插入图片描述

设置端口套接字可复用特性(端口复用)

在这里插入图片描述

在这里插入图片描述

int PASCAL FAR setsockopt (
                           _In_ SOCKET s,
                           _In_ int level,
                           _In_ int optname,
                           _In_reads_bytes_opt_(optlen) const char FAR * optval,
                           _In_ int optlen);
		//设置端口套接字可复用特性
	int opt = 1;
	ret = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,(const char*) & opt, sizeof(opt));
	if (ret == -1) {
		error_die("setsockopt");
	}

绑定套接字和网络地址

		//配置服务器的网络地址
		struct sockaddr_in server_addr;
		memset(&server_addr, 0, sizeof(server_addr));
		server_addr.sin_family = PF_INET;
		server_addr.sin_port = htons(*port);
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //IP 服务器 INADDR_ANY 表示任意网络地址可接入

		//绑定套接字
		if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {

			error_die("bind");
		}

`

动态分配一个端口

	//动态分配一个端口
		int nameLen = sizeof(server_addr);
		if (*port == 0) {
			if (getsockname(server_socket, (struct sockaddr*)&server_addr, &nameLen) < 0)
			{
				error_die("getsockname");
			}

			*port = server_addr.sin_port;
			
		}

创建监听队列

//创建监听队列
		if (listen(server_socket, 5) < 0) {
			error_die("listen");//监听队列报错
            
		}

创建 客户端的访问

//客户端 访问
	struct sockaddr_in client_addr;
	int client_addr_len = sizeof(client_addr);

	while (1) {
		//
		
		int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
		if (client_sock == -1) {
			error_die("accept"); //打印错误信息并结束
		}
	 
	}

使用多线程进行用户的访问

DWORD dwThreadID = 0;
		HANDLE handleFirst = CreateThread(NULL, 0, accept_request, (void*)client_sock, 0, &dwThreadID);

DWORD WINAPI accept_request(LPVOID arg) {
	return 0;
}

使用CreateThread函数创建线程

线程是进程中的一个实体,是被系统独立调度和分派的基本单位。一个进程可以拥有多个线程,但是一个线程必须有一个进程。线程自己不拥有系统资源,只有运行所必须的一些数据结构,但它可以与同属于一个进程的其它线程共享进程所拥有的全部资源,同一个进程中的多个线程可以并发执行。

在C/C++中可以通过CreateThread函数在进程中创建线程,函数的具体格式如下:

HANDLE CreateThread(
                   LPSECURITY_ATTRIBUTES lpThreadAttributes,
                   DWORD dwStackSize,
                   LPTHREAD_START_ROUTINE lpStartAddress,
                   LPVOID lpParameter,
                   DWORD dwCreationFlags,
                   LPDWORD lpThreadID
                  );

参数的含义如下:

lpThreadAttrivutes:指向SECURITY_ATTRIBUTES的指针,用于定义新线程的安全属性,一般设置成NULL;

dwStackSize:分配以字节数表示的线程堆栈的大小,默认值是0;

lpStartAddress:指向一个线程函数地址。每个线程都有自己的线程函数,线程函数是线程具体的执行代码;

lpParameter:传递给线程函数的参数;

dwCreationFlags:表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;

lpThreadID:返回新创建的线程的ID编号;

如果函数调用成功,则返回新线程的句柄,调用WaitForSingleObject函数等待所创建线程的运行结束。函数的格式如下:

DWORD WaitForSingleObject(
                          HANDLE hHandle,
                          DWORD dwMilliseconds
                         );

参数的含义如下:

hHandle:指定对象或时间的句柄;

dwMilliseconds:等待时间,以毫秒为单位,当超过等待时间时,此函数返回。如果参数设置为0,则该函数立即返回;如果设置成INFINITE,则该函数直到有信号才返回。

一般情况下需要创建多个线程来提高程序的执行效率,但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对一个内存地址进行写入操作,由于CPU时间调度的问题,写入的数据会被多次覆盖,所以要使线程同步。

就是说,当有一个线程对文件进行操作时,其它线程只能等待。可以通过临界区对象实现线程同步。临界区对象是定义在数据段中的一个CRITICAL_SECTION结构,Windows内部使用这个结构记录一些信息,确保同一时间只有一个线程访问改数据段中的数据。
使用临界区的步骤如下:

(1)初始化一个CRITICAL_SECTION结构;在使用临界区对象之前,需要定义全局CRITICAL_SECTION变量,在调用CreateThread函数前调用InitializeCriticalSection函数初始化临界区对象;

(2)申请进入一个临界区;在线程函数中要对保护的数据进行操作前,可以通过调用EnterCriticalSection函数申请进入临界区。由于同一时间内只能有一个线程进入临界区,所以在申请的时候如果有一个线程已经进入临界区,则该函数就会一直等到那个线程执行完临界区代码;

(3)离开临界区;当执行完临界区代码后,需要调用LeaveCriticalSection函数离开临界区;

(4)删除临界区;当不需要临界区时调用DeleteCriticalSection函数将临界区对象删除;

Http请求的流程

浏览器发起新的访问时,将向服务器端发送一个请求报文。例如,在浏览器地址输入 127.0.0.1:8000 回车后,服务器端收到的完整报文如下:

GET / HTTP/1.1\n
Host: 127.0.0.1:8000\n
Connection: keep-alive\n
Cache-Control: max-age=0\n
Upgrade-Insecure-Requests: 1\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\n
Sec-Fetch-Site: none\n
Sec-Fetch-Mode: navigate\n
Sec-Fetch-User: ?1\n
Sec-Fetch-Dest: document\n
Accept-Encoding: gzip, deflate, br\n
Accept-Language: zh-CN,zh;q=0.9\n
\n

在这里插入图片描述

请求报文由4四个部分组成:请求行、请求头部行、空行、请求数据。具体格式如下:

在这里插入图片描述

第一行报文详细说明

报文的第一行
报文的第一行是:GET / HTTP/1.1\n

GET表示请求方法(另外有POST方法)

1.1表示http的版本

GET和HTTP之间的"/“表示请求的资源。浏览器发起请求后的,第一次发送的请求报文中,这个位置都是”/",/表示服务器端的资源目录,这里表示不指定特定的资源。

当服务器把网页文件(例如:index.html)发送给浏览器后,浏览器收到这个网页文件后,如果网页文件中含有图片,那么浏览器会自动再发起一个http请求报文,此时请求报文的第一行数据,就类似:
GET /images/head.png HTTP/1.1\n

响应报文的格式

服务器发送数据给浏览器时,发送的响应报文,由4个部分组成:
状态行、消息头部、空行和响应正文。格式如下:

在这里插入图片描述

常用的关键字有:

在这里插入图片描述

在这里插入图片描述

POST请求报文的格式

浏览器发送的POST报文的格式,和GET报文格式其实是一致的,只是多了最后一部分内容“请求数据”,实例如下

POST /color.cgi HTTP/1.1\n
Host: 127.0.0.1:8000\n
Connection: keep-alive\n
Content-Length: 9\n
Cache-Control: max-age=0\n
Upgrade-Insecure-Requests: 1\n
Origin: http://127.0.0.1:8000\n
Content-Type: application/x-www-form-urlencoded\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\n
Sec-Fetch-Site: same-origin\n 
Sec-Fetch-Mode: navigate\n
Sec-Fetch-User: ?1\n
Sec-Fetch-Dest: document\n
Referer: http://127.0.0.1:8000/\n
Accept-Encoding: gzip, deflate, br\n
Accept-Language: zh-CN,zh;q=0.9\n
\n
color = red

最后一行“color=red”就是网页提交的数据

报文的分析

参考文献

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

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

相关文章

网络空间安全数学基础考试要点

网络空间安全数学基础 阶的计算不要求那个公式&#xff0c;但是Order几次方要求 考试会考原根 Legendre必考 多项式计算必考 扩域多项式计算 同态不考 域元素表示 本元多项式不考 1.整除 3 ≡ \equiv ≡ 4 mod 7不对吧3 ≡ \equiv ≡ 3 mod 74 ≡ \equiv ≡ 4 &#xff08;m…

DITA技巧:给文字加颜色

- 1 - 场景 在文档中&#xff0c;我们有时候会在文字中使用颜色。 比如&#xff1a; 在文档中&#xff0c;使用在文字上加颜色来代表一定意义。使用MS Word编写文档的时候&#xff0c;直接在文字上加颜色就可以了。转换成DITA以后&#xff0c;大家会发现在XML编辑器的工具栏…

css实现大屏效果的背景div

实现大屏效果的背景div, 效果如下: html <div class"box">1111111</div>css .box {width: 200px;height: 80px;background: linear-gradient(270deg, #00cda2, #00cda2) 0 0 no-repeat,linear-gradient(180deg, #00cda2, #00cda2) 0 0 no-repeat,line…

JUC高并发编程-初篇(后续发布高阶篇)

JUC高并发编程 1.JUC概述 1.1 什么是JUC JUC就是java.util.concurrent工具包的简称。这是一个处理线程的工具包&#xff0c;JDK1.5开始出现的。 1.2 线程和进程概念 进程&#xff1a;指在系统中正在运行的一个应用程序&#xff1b;程序一旦运行就是进程&#xff1b;进程—…

数据结构--队列的基本概念

数据结构–队列的基本概念 队列的定义 队列其实是一种受限制的线性表 队列(Queue)&#xff1a;是 只允许在一端进行插入或删除操作 \color{red}只允许在一端进行插入或删除操作 只允许在一端进行插入或删除操作的线性表 重要术语: 队头、队尾、空队列 队列的特点: 先进先出 \…

表格检测识别技术面临的挑战和发展趋势

第四章 表格检测识别技术面临的挑战和发展趋势 现在表格区域检测的准确率已经很高了。但检测和识别是相辅相成的&#xff0c;单独的检测不够完善。如何利用检测和结构识别的结果互相提高效果&#xff0c;是未来的研究方向和重点。 由于表格应用场景较为广泛&#xff0c;表格形…

【MySQL学习笔记】(三)操作表(结构)

表 1 创建表2 查看表结构3 修改表4 删除表 注&#xff1a;本篇文章操作的是表的结构&#xff0c;并不是表的内容。 属于笔记&#xff08;一&#xff09;中的SQL分类中的DDL 1 创建表 语法&#xff1a; CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 d…

线程同步器:CountDownLatch、CyclicBarrier、Semaphore

CountDownLatch 日常开发中经常遇到一个线程需要等待一些线程都结束后才能继续向下运行的场景&#xff0c;在CountDownLatch出现之前通常使用join方法来实现&#xff0c;但join方法不够灵活&#xff0c;所以开发了CountDownLatch。场景&#xff1a;一个等其他多个线程&#xf…

消息中间件进阶学习

文章目录 1、RabbitMQ1.1、如何保证消息不丢失&#xff1f;小总结面试快速答法 1.2、消息的重复消费问题面试快速答法 1.3、死信交换机小总结面试快速答法 1.4、消息堆积怎么解决小总结面试快速答法 1.5、集群小总结面试快速答法 2、Kafka2.1、Kafka是如何保证消息不丢失小总结…

Linux中Docker详细安装说明

1.准备环境 说明&#xff1a;准备Linux系统centos7版本(以上) 2.切换管理模式 说明&#xff1a;输入一下命令&#xff0c;然后回车&#xff0c;输入密码。 su – 3.更新yum 说明&#xff1a;为了保证doker能够给顺利安装&#xff0c;那么更新一下&#xff1b;如果没有也可以…

单相智能电量多用户远程预付费控系统优化的设计及应用

摘要&#xff1a;由于现有系统仅对电表数据进行读取操作&#xff0c;存在成本较高和耗时较长的问题&#xff0c;为此对单相智能多用户远程预付费控系统优化设计进行研究。选择电能表子系统作为优化对象&#xff0c;选取78KO527A微控制器作为电能表子系统的控制核心&#xff0c;…

文献阅读:中国物理海洋学研究70 年-发展历程、学术成就概览

摘要 本文概略评述新中国成立70 年来物理海洋学各分支研究领域的发展历程和若干学术成就。中国物理海洋学研究起步于海浪、潮汐、近海环流与水团&#xff0c;以及以风暴潮为主的海洋气象灾害的研究。随着国力的增强&#xff0c;研究领域不断拓展&#xff0c;涌现了大量具有广泛…

Linux踢掉远程登录用户

Linux踢掉远程登录用户 安装psmisc yum install -y psmisc查看远程登录用户 who得到以下结果 [rootcentos7 ~]# w10:58:13 up 0 min, 2 users, load average: 0.12, 0.03, 0.01 USER TTY FROM LOGIN IDLE JCPU PCPU WHAT lhz pts/0 19…

mysql——数据库设计

前言 之前我们已经了解了 mysql 的基本增删改查mysql 从入门到放弃——基本约束以及语法 现在我们系统的进行一遍数据库的设计 直接进入主题 来个例子&#xff1a;下面我们将围绕这个例子来进行数据库的设计 我们就来简单的模拟 大学教务处的选课 系统 中的 选课功能 注意…

十大排序算法(Java实现)

文章目录 零、总览 / 前言一、冒泡排序1.算法描述2.代码&复杂度 二、选择排序1.算法描述2.代码&复杂度 三、插入排序1.算法描述2.代码&复杂度分析 四、希尔排序1.算法步骤2.代码&复杂度分析 五、归并排序1.算法描述2.代码&复杂度分析 六、快速排序1.算法描…

《强化学习的数学原理》思维导图,供初学者参考

对应课程&#xff1a; 【强化学习的数学原理】课程&#xff1a;从零开始到透彻理解&#xff08;完结&#xff09;_哔哩哔哩_bilibili

Linux 系统下 CMake 示 例

CMake 是一个开源的跨平台工具&#xff0c;可以构建、测试和打包软件。 它具有如下特性&#xff1a; 自动搜索可能需要的程序、库和头文件的能力&#xff1b;独立的构建目录&#xff08;如build&#xff09;&#xff0c;可以安全清理&#xff1b;支持复杂的自定义命令&#xf…

一文了解什么什么是加密货币及其工作原理

加密货币是基于区块链技术并由密码学保护的去中心化数字货币。要理解加密货币&#xff0c;首先需要理解三个术语——区块链、去中心化和密码学。 一、加密货币如何运作 简而言之&#xff0c;加密货币中的区块链是一种数字分类账&#xff0c;其访问权限分布在授权用户之间。该分…

hello算法学习笔记之排序

概述&#xff1a;排序算法 在排序算法中&#xff0c;数据类型可以是整数、浮点数、字符或字符串等&#xff1b;顺序的判断规则可根据需求设定&#xff0c;如数字大小、字符 ASCII 码顺序或自定义规则。 评价维度&#xff1a; 运行效率、就地性、稳定性、自适应性&#xff08…

21.RocketMQ源码之NameServer的路由管理和架构设计

highlight: arduino-light NameServer 路由管理 Broker消息服务器在启动的时向所有NameServer注册。 消息生产者Producer在发送消息之前先从NameServer获取Broker服务器地址列表然后根据负载均衡算法从列表中选择一台服务器进行发送。 NameServer与每台Broker保持长连接&#x…