【boost网络库从青铜到王者】第二篇:asio网络编程中的socket的监听和连接

news2024/11/24 14:44:30

文章目录

  • 1、网络编程基本流程
  • 2、终端节点endpoint的创建
      • 2.1、客户端终端节点endpoint的创建
      • 2.2、服务器终端节点endpoint的创建
  • 3、服务器与客户端通信套接字socket的创建
  • 4、服务器监听套接字socket的创建
  • 5、绑定accpet监听套接字
  • 6、客户端连接指定的端点
  • 7、服务器接收连接
  • 8、ipv6协议和ipv4协议区别
  • 9、0.0.0.0地址和127.0.0.1地址区别

1、网络编程基本流程

网络编程的基本流程对于服务端是这样:

  • 服务器:

    • socket——创建socket对象。

    • bind——绑定本机ip+port

    • listen——监听来电,若在监听到来电,则建立起连接。

    • accept——再创建一个socket对象给其收发消息。原因是现实中服务端都是面对多个客户端,那么为了区分各个客户端,则每个客户端都需再分配一个socket对象进行收发消息。

    • readwrite——就是收发消息了。消息就是协议,这里通过protobuf来解析。

  • 客户端

    • socket——创建socket对象。

    • connect——根据服务端ip+port,发起连接请求。

    • write、read——建立连接后,就可发收消息了。

图文如下:
在这里插入图片描述
接下来按照上述流程,我们用boost::asio逐步介绍。

2、终端节点endpoint的创建

2.1、客户端终端节点endpoint的创建

所谓终端节点就是用来通信的端对端的节点,可以通过ip地址和端口构造,其的节点可以连接这个终端节点做通信。

如果我们是客户端,我们可以通过对端的ip端口构造一个endpoint,用这个endpoint和其通信。

#include"asio_demo.h"

int32_t BoostAsio::ClientEndPoint(std::string& raw_ip_address) {

	//step1:Assume that the client application has already 
	// obtained the IP-address and the protocol port number.
	
	//std::string raw_ip_address = "127.0.0.1";
	uint16_t port_num = 9273;

	// Used to store information about error that happens
	// while parsing the raw IP-address.
	boost::system::error_code ec;

	//Step 2. Using IP protocol version independent address
	// representation.
	boost::asio::ip::address ip_address = boost::asio::ip::address::from_string(raw_ip_address, ec);
	if (0 != ec.value()) {
		std::cout << "Failed to parse the Ip Adress,error_code = " << ec.value() << " . Message: " << ec.message();
		return ec.value();
	}

	// Step 3.
	boost::asio::ip::tcp::endpoint ep(ip_address, port_num);

	// Step 4. The endpoint is ready and can be used to specify a 
	// particular server in the network the client wants to 
	// communicate with.
}

这段代码是一个使用 Boost.Asio 库创建 TCP 客户端端点的示例。以下是代码中每个步骤的解释:

  • 包含语句: 代码包含了 “asio_demo.h” 头文件。这个头文件可能包含了 Boost.Asio 库的声明和必要的包含。

  • ClientEndPoint 函数: 这个函数用于使用 Boost.Asio 创建 TCP 客户端端点。这个函数用于使用 Boost.Asio 创建 TCP 客户端端点,并接受一个 IP 地址字符串作为输入参数。

  • 步骤 1: 客户端应用程序假设已经获取了原始 IP 地址(在本例中是 “127.0.0.1”)和端口号(9273),以便与服务器通信。

  • 步骤 2Boost.Asio 提供了从字符串表示转换为 IP 地址的方法。在这里,它使用 boost::asio::ip::address::from_string 函数将原始 IP 地址字符串转换为 ip::address 对象。error_code 参数用于检查在解析过程中是否发生了错误。

  • 步骤 3boost::asio::ip::tcp::endpoint 类表示一个端点(IP 地址和端口号),可以用于指定网络中的特定服务器。使用解析后的 IP 地址和端口号创建了 ep 对象。

  • 步骤 4: 此时,ep 对象表示客户端端点,可以用于连接指定的服务器上的 TCP。然而,代码并没有显示实际连接过程或与服务器的任何通信,因此它只是为将来使用而准备端点。

请注意,这段代码似乎是更大程序的一部分,所提供的片段并没有显示客户端实际如何与服务器交互或执行网络通信。Boost.Asio 库通常用于在 C++ 中进行异步网络编程,允许开发人员创建高效和可扩展的网络应用程序。

2.2、服务器终端节点endpoint的创建

如果是服务端,则只需根据本地地址绑定就可以生成endpoint

int32_t BoostAsio::ServerEndPoint(uint16_t& port_num) {
	// Step 1. Here we assume that the server application has
	//already obtained the protocol port number.

	//uint16_t port_num = 9273;

	// Step 2. Create special object of asio::ip::address class
	// that specifies all IP-addresses available on the host. Note
	// that here we assume that server works over IPv6 protocol.

	boost::asio::ip::address ip_address = boost::asio::ip::address_v6::any();

	//Step 3.
	boost::asio::ip::tcp::endpoint ep(ip_address, port_num);

	// Step 4. The endpoint is created and can be used to 
   // specify the IP addresses and a port number on which 
   // the server application wants to listen for incoming 
   // connections.

	return 0;
}

这段代码是一个使用 Boost.Asio 库创建 TCP 服务器端点的示例。以下是代码中每个步骤的解释:

  • ServerEndPoint 函数: 这个函数用于使用 Boost.Asio 创建 TCP 服务器端点,并且传入一个端口号作为参数。

  • 步骤 1: 服务器应用程序已经获得了一个协议端口号,并将这个端口号作为参数传递给函数。

  • 步骤 2: 使用 Boost.Asioip::address_v6::any() 函数创建了一个特殊的 ip::address 对象,表示服务器将监听主机上的所有可用 IPv6 地址。这意味着服务器将监听所有可用的网络接口。

  • 步骤 3: 使用步骤 2 中创建的 IP 地址和传入的端口号,创建了一个 boost::asio::ip::tcp::endpoint 对象 ep,表示服务器端点。

  • 步骤 4: 此时,ep 对象表示一个可以用于监听传入连接请求的 TCP 端点,其 IP 地址为所有可用的 IPv6 地址,而端口号由函数参数 port_num 决定。

最后,函数返回一个值,这里是 0,表示函数执行成功。

总之,这段代码允许你通过传入一个端口号来创建一个服务器端点,然后服务器可以用这个端点在特定的 IP 地址和端口上监听传入的连接请求。

3、服务器与客户端通信套接字socket的创建

服务器和客户端所用的通信的socket都是一样的。创建socket分为4步:

  • 创建上下文io_context
  • 选择协议。
  • 生成通信socket
  • 打开通信socket
int32_t BoostAsio::CreateTcpSocket() {
	// Step 1. An instance of 'io_service' class is required by
	// socket constructor. 
	boost::asio::io_context context;	

	// Step 2. Creating an object of 'tcp' class representing
	// a TCP protocol with IPv4 as underlying protocol.
	boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v4();

	// Step 3. Instantiating an active TCP socket object.
	boost::asio::ip::tcp::socket sock(context);

	// Used to store information about error that happens
	// while opening the socket.
	boost::system::error_code ec;

	// Step 4. Opening the socket.
	sock.open(protocol, ec);
	if (0 != ec.value()) {
		//Failed to open the socket
		std::cout << "Failed to open the socket,error_code = " << ec.value() << ". Message: " << ec.message();
		return ec.value();
	}

	return 0;
}

这段代码是使用 Boost.Asio 库创建一个 TCP 套接字的过程,让我们逐步解释:

  • io_context 创建: 首先,通过创建 boost::asio::io_context 类的实例 context,用于管理异步 I/O 操作。

  • 协议选择: 选择使用 IPv4 的 TCP 协议,这里使用 boost::asio::ip::tcp::v4() 创建 tcp 对象来表示。

  • 创建套接字: 创建了一个 boost::asio::ip::tcp::socket 对象 sock,它代表一个 TCP 套接字。

  • 错误处理对象: 创建一个 boost::system::error_code 对象 ec,用于存储可能在打开套接字时发生的错误信息。

  • 打开套接字: 使用 sock.open(protocol, ec) 打开套接字。如果套接字打开过程中发生错误,会将错误码存储在 ec 中,然后检查 ec 是否非零,如果非零则表示打开套接字失败,输出错误信息。

  • 返回结果: 最后返回一个整数值,表示操作的结果。如果返回值为 0,表示套接字创建并打开成功。

这段代码演示了如何使用 Boost.Asio 库来创建并打开一个 TCP 套接字,并在过程中处理错误情况。这对于进行网络通信的程序非常有用,因为可以通过异步操作来管理网络连接和数据传输。

4、服务器监听套接字socket的创建

服务端,我们还需要生成一个acceptorsocket,用来监听接收新来自客户端的连接。

  • 创建上下文iocontext
  • 选择协议
  • 生成监听socket
  • 打开监听socket
int32_t BoostAsio::CreateAcceptSocket() {
	// Step 1. An instance of 'io_service' class is required by
	// socket constructor. 
	boost::asio::io_context context;

	// Step 2. Creating an object of 'tcp' class representing
    // a TCP protocol with IPv6 as underlying protocol.

	boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v6();

	// Step 3. Instantiating an acceptor socket object.
	boost::asio::ip::tcp::acceptor accept(context);

	// Used to store information about error that happens
	// while opening the acceptor socket.
	boost::system::error_code ec;

	// Step 4. Opening the acceptor socket.
	accept.open(protocol, ec);
	if (0 != ec.value()) {
		std::cout << "Failed to open the accept socket!" << " Error code = " << ec.value() << " . Message: " << ec.message();
		return ec.value();
	}

	return 0;
}

这段代码是使用 Boost.Asio 库创建一个监听套接字**(acceptor socket)**的过程,让我们逐步解释:

  • io_context 创建: 通过创建 boost::asio::io_context 类的实例 context,用于管理异步 I/O 操作。

  • 协议选择: 选择使用 IPv6TCP 协议,这里使用 boost::asio::ip::tcp::v6() 创建 tcp 对象来表示。

  • 创建接收套接字: 创建了一个 boost::asio::ip::tcp::acceptor 对象 accept它代表一个接收来自客户端连接监听套接字。

  • 错误处理对象: 创建一个 boost::system::error_code 对象 ec,用于存储可能在打开监听套接字时发生的错误信息。

  • 打开接收套接字: 使用 accept.open(protocol, ec) 打开监听套接字。如果在打开过程中发生错误,会将错误码存储在 ec 中,然后检查 ec 是否非零,如果非零则表示打开监听套接字失败,输出错误信息。

  • 返回结果: 最后返回一个整数值,表示操作的结果。如果返回值为 0,表示监听套接字创建并打开成功。

这段代码演示了如何使用 Boost.Asio 库来创建并打开一个监听套接字,用于接受传入的连接请求。监听套接字通常用于服务器端,用于等待客户端的连接。

5、绑定accpet监听套接字

对于accept类型的socket,服务器要将其绑定到指定的断点,所有连接这个端点的连接都可以被接收到。

int32_t BoostAsio::BindAcceptSocket(uint16_t& port_num) {
	// Step 1. Here we assume that the server application has
	// already obtained the protocol port number.

	// Step 2. Creating an endpoint.
	boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port_num);

	// Used by 'accept' class constructor.
	boost::asio::io_context context;

	// Step 3. Creating and opening an acceptor socket.
	boost::asio::ip::tcp::acceptor accept(context, ep.protocol());

	boost::system::error_code ec;

	// Step 4. Binding the acceptor socket.
	accept.bind(ep, ec);

	// Handling errors if any.
	if (0 != ec.value()) {
		std::cout << "Failed to bind the accept socket!" << "Error code : " << ec.value() << ". Message:" << ec.message();
		return ec.value();
	}
	return 0;
}

这段代码演示了使用 Boost.Asio 库创建创建并绑定一个监听套接字 (acceptor socket) 的过程,让服务器可以监听指定端口号上的连接请求。下面是代码的解释:

  • 创建端点: 通过创建 boost::asio::ip::tcp::endpoint 对象 ep,指定了一个 IP 地址为 boost::asio::ip::address_v4::any(),表示监听所有可用的 IPv4 地址。port_num 则是提前获取到的端口号。

  • io_context 创建: 创建了 boost::asio::io_context 类的实例 context,用于管理异步 I/O 操作。

  • 创建和打开监听套接字: 使用 boost::asio::ip::tcp::acceptor 对象的构造函数创建了一个监听套接字 accept,并指定了 ep.protocol()(即 IPv4)。

  • 错误处理对象: 创建一个 boost::system::error_code 对象 ec,用于存储可能在绑定监听套接字时发生的错误信息。

  • 绑定监听套接字: 使用 accept.bind(ep, ec) 绑定监听套接字到指定的 IP 地址和端口。如果在绑定过程中发生错误,会将错误码存储在 ec 中,然后检查 ec 是否非零,如果非零则表示绑定监听套接字失败,输出错误信息。

  • 返回结果: 最后返回一个整数值,表示操作的结果。如果返回值为 0,表示监听套接字绑定成功。

这段代码展示了如何使用 Boost.Asio 库创建一个绑定到指定 IP 地址和端口的监听套接字,用于等待客户端的连接请求。

6、客户端连接指定的端点

作为客户端可以连接服务器指定的端点**(ip+端口)**进行连接。

int32_t BoostAsio::ClientConnectServer(std::string& raw_ip_address, uint16_t& port_num) {
	// Step 1. Assume that the client application has already
	// obtained the IP address and protocol port number of the
	// target server.

	try {
		// Step 2. Creating an endpoint designating 
		// a target server application.
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		boost::asio::io_context context;

		// Step 3. Creating and opening a socket.
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		// Step 4. Connecting a socket.
		socket.connect(ep);

		// At this point socket 'sock' is connected to 
	   // the server application and can be used
	   // to send data to or receive data from it.

	}
	// Overloads of asio::ip::address::from_string() and 
	 // asio::ip::tcp::socket::connect() used here throw
	// exceptions in case of error condition.
	catch (boost::system::system_error& e) {
		std::cout << "Error occured! Error code = " << e.code() << " .Message:" << e.what();
		return e.code().value();
	}
	return 0;
}

这段代码实现了一个客户端连接服务器的过程。下面是代码的解释:

  • 创建端点: 通过使用 boost::asio::ip::address::from_string(raw_ip_address) 将提供的 IP 地址字符串解析为 IP 地址对象,并使用 port_num 作为端口号,创建了一个 boost::asio::ip::tcp::endpoint 对象 ep,该对象指定了要连接的服务器的终端。

  • io_context 创建: 创建了 boost::asio::io_context 类的实例 context,用于管理异步 I/O 操作。

  • 创建和打开套接字: 使用 boost::asio::ip::tcp::socket 对象的构造函数创建了一个客户端套接字 socket,并指定了 ep.protocol(),即服务器的协议类型。

  • 连接套接字: 使用 socket.connect(ep) 方法来连接服务器。这一步骤将客户端的套接字与服务器的套接字建立连接,从此客户端可以通过该套接字与服务器进行通信。

  • 错误处理: 使用 try-catch 语句来捕获可能发生的异常。如果连接过程中发生错误,会抛出 boost::system::system_error 异常。在 catch 块中,会输出错误信息,包括错误码和错误消息。

  • 返回结果: 最后返回一个整数值,表示操作的结果。如果返回值为 0,表示连接成功。

这段代码用于在客户端创建套接字并连接到服务器,以便与服务器进行数据交换。

7、服务器接收连接

当有客户端连接时,服务器需要接收连接:

int32_t ServerAcceptClientConnect(uint16_t& port_num) {
	// The size of the queue containing the pending connection
	// requests.
	//连接队列
	const int BACKLOG_SIZE = 30;

	// Step 1. Here we assume that the server application has
	// already obtained the protocol port number.

	// Step 2. Creating a server endpoint.
	boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port_num);

	boost::asio::io_context context;

	try {
		// Step 3. Instantiating and opening an acceptor socket.
		boost::asio::ip::tcp::acceptor accept(context, ep.protocol());

		// Step 4. Binding the acceptor socket to the 
		// server endpint.
		accept.bind(ep);

		// Step 5. Starting to listen for incoming connection
		// requests.
		accept.listen(BACKLOG_SIZE);

		// Step 6. Creating an active socket.
		boost::asio::ip::tcp::socket socket(context);

		// Step 7. Processing the next connection request and 
		// connecting the active socket to the client.
		accept.accept(socket);

		// At this point 'sock' socket is connected to 
		//the client application and can be used to send data to
		// or receive data from it.
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured! Error code = " << e.code() << ". Message: " << e.what();
		return e.code().value();
	}
	return 0;
}

这段代码实现了服务器接受客户端连接的过程。以下是代码的解释:

  • 连接队列大小: 定义了连接队列中待处理的连接请求的最大数量,通过 const int BACKLOG_SIZE = 30; 定义。

  • 创建服务器端点: 使用 boost::asio::ip::tcp::endpoint 对象创建一个服务器端点对象 ep,使用 boost::asio::ip::address_v4::any() 表示服务器可以绑定到任何可用的 IPv4 地址,同时使用提供的 port_num 作为端口号。

  • io_context 创建: 创建了 boost::asio::io_context 类的实例 context,用于管理异步 I/O 操作。

  • 实例化和打开接受套接字: 使用 boost::asio::ip::tcp::acceptor 构造函数创建一个接受套接字 accept,并指定协议类型。

  • 绑定接受套接字: 使用 accept.bind(ep) 方法将接受套接字绑定到服务器端点。

  • 开始监听连接请求: 使用 accept.listen(BACKLOG_SIZE) 方法开始监听传入的连接请求,并指定连接队列的大小

  • 创建活动套接字并处理连接请求: 创建一个活动套接字 socket,并使用 accept.accept(socket) 方法来处理下一个连接请求,并将活动套接字与客户端连接,进行协议传输。

  • 错误处理: 使用 try-catch 语句来捕获可能发生的异常。如果发生错误,会抛出 boost::system::system_error 异常。在 catch 块中,会输出错误信息,包括错误码和错误消息。

  • 返回结果: 最后返回一个整数值,表示操作的结果。如果返回值为 0,表示连接成功。

这段代码用于在服务器端创建接受套接字,绑定端点并开始监听连接请求,然后处理下一个连接请求并与客户端建立连接,以便进行数据交换。

8、ipv6协议和ipv4协议区别

IPv4(Internet Protocol version 4)IPv6(Internet Protocol version 6) 是互联网通信中使用的两种不同版本的IP协议,它们有以下主要区别:

  • 地址长度:

    • IPv4使用32位二进制地址,通常以点分十进制表示(例如:192.168.1.1)。
    • IPv6使用128位二进制地址,通常以冒号分隔的十六进制表示(例如:2001:0db8:85a3:0000:0000:8a2e:0370:7334)。
  • 地址数量:

    • IPv4提供了大约42亿个唯一地址,但随着互联网的发展,IPv4地址耗尽成为了问题,需要使用NAT等技术来实现地址复用。
    • IPv6提供了远远超过IPv4的地址数量,理论上可以支持约340十万亿亿亿亿个唯一地址,因此可以解决IPv4地址耗尽问题。
  • 地址表示:

    • IPv4地址以32位二进制表示,使用四个8位字节表示,每个字节使用十进制表示。
    • IPv6地址以128位二进制表示,使用八个16位字节表示,每个字节使用十六进制表示。
  • 地址类型:

    • IPv4的地址分为公有地址(用于公共互联网)和私有地址(用于局域网内部)。
    • IPv6的地址一般都是全球唯一的,不再使用私有地址的概念,因为地址数量足够满足所有需求。
  • 协议特性:

    • IPv6引入了一些新的特性,如移动IPv6支持、自动地址配置、IPSec等,以提升网络的效率、安全性和功能。
  • 包头大小:

    • IPv6包头相对于IPv4包头有一些增加,主要是因为引入了更多的特性,但这也使得IPv6的路由和分片等操作更加高效。
  • 部署和过渡:

    • 由于IPv4已经被广泛使用,IPv6的过渡是逐步进行的,许多网络同时支持IPv4IPv6,以保障兼容性。
    • 一些服务提供商、网站和设备已经开始支持IPv6,但完全的IPv6广泛部署仍在进行中。

总体来说,IPv6是为了解决IPv4地址耗尽问题而设计的,它提供了更大的地址空间和更多的功能,逐渐在全球范围内推广和部署。

9、0.0.0.0地址和127.0.0.1地址区别

  • 0.0.0.0地址:

    • 0.0.0.0: 这个 IP 地址表示“所有可用的 IP 地址”或“任何主机”。当一个服务器监听在这个 IP 地址上,它将能够接受来自网络上所有可用 IP 地址的连接。这在服务器需要在多个网络接口上监听时很有用,因为它能够接受来自所有接口的连接请求。这通常用于公共服务器,如 Web 服务器,以便能够处理来自不同来源的连接。
  • 127.0.0.1:

    • 127.0.0.1: 这是称为“回环地址”的特殊 IP 地址。它是本地主机上的回环接口,也就是说,它指向自己。当一个程序连接到 127.0.0.1 地址时,它实际上连接到本地计算机上的自身,这对于测试网络通信或本地服务非常有用。因此,127.0.0.1 被称为“本地回环地址”或“本地主机”。

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

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

相关文章

H5 和小程序的区别

什么是小程序&#xff1f; 从“微信之父” 张小龙的定义里&#xff0c;我们可以了解到&#xff0c;小程序其实就是内嵌在微信&#xff0c;不需要安装和卸载的一种新应用形态。它具备的两个强属性&#xff1a;提高效率&#xff0c;用完即走&#xff01;因此小程序的设计以轻便、…

vue element 多图片组合预览

定义组件&#xff1a;preview-image <template><div><div class"imgbox"><divclass"preview-img":class"boxClass"v-if"Imageslist 3 ||Imageslist 5 ||Imageslist 7 ||Imageslist 8 ||Imageslist > 9"&…

SQLyog中导入CSV文件入库到MySQL中

1.在数据库中新建一个表&#xff0c;设置列名&#xff08;与待导入文件一致&#xff09;&#xff0c;字段可以多出几个都可以 2.右键表名&#xff0c;导入- - >导入使用本地加载的CSV数据 选择使用加载本地CVS数据 3.指定好转义字符&#xff0c;将终止设置为,号(英文状态下…

微型导轨在包棉机中的作用

随着工业革命的开展&#xff0c;各种人工智能设备的迅猛发展&#xff0c;为了适应高速发展的工业自动化&#xff0c;越来越多的工业企业开始采用微型导轨&#xff0c;尤其是在包棉机中的应用。 包棉机是一种用于加工棉花的机械设备&#xff0c;它的主要功能是将原始棉花经过清洁…

zabbix监控tomcat

一、zabbix监控Tomcat1.1 zbx-agent配置1.1.1 关闭防火墙&#xff0c;将安装 Tomcat 所需软件包传到/opt目录下1.1.2 安装JDK1.1.3 设置JDK环境变量1.1.4 安装启动Tomcat1.1.5 配置 JMX 1.2 zbx-server配置1.2.1 安装zabbix&#xff08;省略&#xff0c;可看上一篇博客&#xf…

【校招VIP】前端JS语言考点之px rem等单位

考点介绍&#xff1a; rem vm等问题是前端面试里的高频题型。但是不少同学并不能很清楚的说明为什么在有px单位之后&#xff0c;还需要rem单位&#xff1f;往往会往不对的自适应方向回答。 作为基础性问题&#xff0c;只要回答不出来&#xff0c;面试就通过不了&#xff0c;需要…

在vue中使用swiper轮播图(搭配watch和$nextTick())

在组件中使用轮播图展示图片信息&#xff1a; 1.下载swiper,5版本为稳定版本 cnpm install swiper5 2.在组件中引入swiper包和对应样式&#xff0c;若多组件使用swiper&#xff0c;可以把swiper引入到main.js入口文件中&#xff1a; import swiper/css/swiper.css //引入swipe…

树莓派3B CSI摄像头配置

1.硬件连接 1、找到 CSI 接口(树莓派3B的CSI接口在HDMI接口和音频口中间)&#xff0c;需要拉起 CSI 接口挡板,如下&#xff1a; 2、将摄像头排线插入CSI接口。记住&#xff0c;有蓝色胶带的一面应该面向音频口或者网卡方向&#xff0c; 确认方向并插紧排线&#xff0c;将挡板…

基于Spring Boot的高校图书馆管理系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的高校图书馆管理系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java sp…

springBoot 集中配置管理

springBoot 集中配置管理 项目配置如果上线项目&#xff0c;运维或者开发者可以直接和jar包同目录下创建文件&#xff0c;然后更改属性 项目配置 创建文件&#xff0c;调整配置如果上线项目&#xff0c;运维或者开发者可以直接和jar包同目录下创建文件&#xff0c;然后更改 属…

海格里斯HEGERLS四向穿梭车仓储解决方案在电子商务行业中的应用

随着现代物流&#xff0c;尤其是智能化物流的飞速发展&#xff0c;河北沃克金属制品有限公司看到了智能物流领域背后的巨大价值和市场空间&#xff0c;深知物流与供应链对企业发展的重要性。于是&#xff0c;引进了先进的高科技智能技术—HEGERLS四向穿梭车技术&#xff0c;并迅…

echarts-convert.js使用

echarts-convert.js demo 点击下载 1、本地安装phantom.js插件 点击下载 2、更改文件路径 &#xff08;D:\phantomjs-2.1.1-windows\bin&#xff09;改为本地项目文件路径 3、打开cmd命令行&#xff0c;并格式化语言 运行以下命令 将命令行语言改为中文简体 chcp 65001…

超详细,自动化测试实战-获取配置文件信息(实例源码)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 配置文件的类型 …

03.Show and Tell

目录 前言泛读摘要IntroductionRelated Work小结 精读模型基于LSTM的句子生成器TrainingInference 实验评价标准数据集训练细节分数结果生成结果多样性讨论排名结果人工评价结果表征分析 结论 代码 前言 本课程来自深度之眼《多模态》训练营&#xff0c;部分截图来自课程视频。…

新题速看!2023阿里、腾讯、字节都在问的SQL数据库笔试题及答案都给你整理好啦!

♥ 前 言 2021到了最后一个月份&#xff0c;年后肯定有蛮多小伙伴需要跳槽换工作&#xff0c;但对于年限稍短的软件测试工程师&#xff0c;难免会需要进行笔试&#xff0c;而在笔试中&#xff0c;基本都会碰到一道关于数据库的大题&#xff0c;今天这篇文章呢&#xff0c;就…

保存位置不同,多个文件如何统一命名并给文件编号

在日常工作中&#xff0c;我们会经常碰到文件或文件夹需要整理归类下&#xff0c;或文件&#xff08;夹&#xff09;需要统一名称再编号下再保存&#xff0c;也方便日后用到文件时可以快速找到文件。大家有没有碰到文件多个&#xff0c;但是文件的保存位置不一样&#xff0c;并…

聚观早报 | 近2亿“救命款”每日优鲜不卖菜了;小鹏G6难过交付关

【聚观365】8月14日消息 近2亿元“救命款”每日优鲜不卖菜了 小鹏爆款G6难过交付关 AIGC在数字营销领域应用程度 iPhone SE 4设计基于iPhone 14 Modulal寻求A轮融资挑战英伟达 近2亿元“救命款”每日优鲜不卖菜了 从一家基于前置仓模式的蔬菜水果等本地生活服务商&#x…

多个项目使用的node版本不一致?vscode dev container + docker 真香

一、接手的项目多了&#xff0c;什么node版本都有~ 遇到这种情况&#xff0c;多数情况会使用 nvm 进行 node 版本管理&#xff0c;具体使用方法可戳nvm的安装与使用。但若要并行开发两个不同环境下的项目&#xff0c;不停切换node版本&#xff0c;也难免有些繁琐。此时&#xf…

基于HTML+CSS+Echarts大屏数据可视化集合共99套

基于HTMLCSSEcharts大屏数据可视化集合共99套 一、介绍二、展示1.大数据展示系统2.物流订单系统3.物流信息系统4.办税渠道监控平台5.车辆综合管控平台 三、其他系统实现四、获取源码 一、介绍 基于HTML/CSS/Echarts的会议展览、业务监控、风险预警、数据分析展示等多种展示需求…

共识算法初探

共识机制的背景 加密货币都是去中心化的&#xff0c;去中心化的基础就是P2P节点众多&#xff0c;那么如何吸引用户加入网络成为节点&#xff0c;有那些激励机制&#xff1f;同时&#xff0c;开发的重点是让多个节点维护一个数据库&#xff0c;那么如何决定哪个节点写入&#x…