学懂C++(三十八):深入详解C++网络编程:套接字(Socket)开发技术
目录
一、概述与基础概念
1.1 套接字(Socket)概念
1.2 底层原理与网络协议
1.2.1 网络协议
1.2.2 套接字工作原理
二、C++套接字编程核心技术
2.1 套接字编程的基本步骤
2.2 套接字编程详细实现
2.2.1 创建套接字
2.2.2 绑定地址
2.2.3 监听和接受连接(服务端)
2.2.4 客户端连接
2.2.5 发送和接收数据
2.2.6 关闭套接字
2.3 经典实例:TCP客户端和服务器
2.3.1 TCP服务器
2.3.2 TCP客户端
运行结果
三、深入理解与高级应用
3.1 异步I/O与事件驱动编程
3.2 高效并发编程
3.3 使用Boost.Asio进行异步编程
完整代码示例:使用Boost.Asio进行异步TCP服务器
BoostServer.cpp
四、核心技术及高级话题
4.1 同步I/O与异步I/O的选择
4.2 多线程与事件驱动模型
4.3 高效网络编程的关键技术
4.4 使用C++11/14/17/20新特性优化网络编程
五、实例:高效异步HTTP服务器
5.1 异步HTTP服务器
运行结果
六、拓展思考
1、套接字(Socket)是什么?它包括哪些核心内容?
套接字的定义
套接字的核心内容
1. 套接字类型
2. 地址族
3. 套接字的基本操作
套接字的高级内容
2、套接字 socket是基于哪个网络协议的?是TCP还是Http?
总结
网络编程是现代软件开发的重要领域,广泛应用于客户端-服务器应用、分布式系统和互联网应用开发中。C++作为一门强大且高效的编程语言,在进行低层次、高性能网络编程时有显著优势。本文将深入剖析C++网络编程中的套接字(Socket)开发技术,详细讲解其概念、底层原理、网络协议知识、本质及需要掌握的核心点、实现方式等,同时结合经典实例进行解析。
一、概述与基础概念
1.1 套接字(Socket)概念
概念:套接字(Socket)是网络编程中用于描述网络连接的端点,是操作系统提供的用于网络通信的基础抽象。它可以看作是网络中的“文件”,通过对套接字的操作实现数据的发送和接收。
核心点:
- 类型:主要包括流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM),分别对应TCP和UDP协议。
- 地址:由IP地址和端口号组成,用于标识网络中的设备和应用。
1.2 底层原理与网络协议
1.2.1 网络协议
IP地址:标识网络中的主机,分为IPv4(如192.168.1.1)和IPv6(如2001:db8::1)地址。
端口号:标识主机上的应用程序,范围为0到65535。常见的端口号如HTTP的80端口、HTTPS的443端口等。
TCP/IP协议族:互联网的基础协议族,包含传输层的TCP和UDP、网络层的IP等。
- TCP(传输控制协议):提供可靠的字节流服务,包括连接建立、数据传输、连接终止等过程。TCP通过三次握手建立连接,四次挥手终止连接。
- UDP(用户数据报协议):提供不可靠的消息传递服务,数据报可能无序到达或丢失。UDP适用于实时性要求较高的应用,如视频传输、在线游戏等。
1.2.2 套接字工作原理
套接字是一种抽象层,使得应用程序能够通过操作系统提供的API进行网络通信。它包含以下基本操作:
- 创建:通过系统调用创建套接字。
- 绑定:将套接字绑定到特定的IP地址和端口。
- 监听:在服务端,套接字监听来自客户端的连接请求。
- 连接:在客户端,套接字连接到服务端的指定地址和端口。
- 发送/接收:通过套接字发送和接收数据。
- 关闭:关闭套接字,释放系统资源。
二、C++套接字编程核心技术
2.1 套接字编程的基本步骤
- 创建套接字
- 绑定地址
- 监听(服务端)
- 接受连接(服务端)
- 连接(客户端)
- 发送和接收数据
- 关闭套接字
2.2 套接字编程详细实现
2.2.1 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {perror("socket creation failed");exit(EXIT_FAILURE);
}
解析:
socket(AF_INET, SOCK_STREAM, 0)
:创建一个TCP套接字。AF_INET
表示使用IPv4,SOCK_STREAM
表示使用TCP协议。- 返回值:成功时返回套接字文件描述符,失败时返回-1。
2.2.2 绑定地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);
}
解析:
struct sockaddr_in
:定义IP地址和端口,sin_family
设为AF_INET
,sin_addr.s_addr
设为INADDR_ANY
表示接受任何IP地址,sin_port
设为端口(通过htons
函数转换为网络字节序)。bind
:将地址绑定到套接字。
2.2.3 监听和接受连接(服务端)
if (listen(sockfd, 5) < 0) {perror("listen failed");close(sockfd);exit(EXIT_FAILURE);
}struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int newsockfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (newsockfd < 0) {perror("accept failed");close(sockfd);exit(EXIT_FAILURE);
}
解析:
listen
:将套接字置于监听模式,准备接受连接。第二个参数为连接队列的最大长度。accept
:接受客户端连接,返回新的套接字用于通信。client_addr
保存客户端地址信息。
2.2.4 客户端连接
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {perror("invalid address");exit(EXIT_FAILURE);
}if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("connect failed");close(sockfd);exit(EXIT_FAILURE);
}
解析:
inet_pton
:将字符串形式的IP地址转换为struct in_addr
。connect
:将套接字连接到指定的服务器地址和端口。
2.2.5 发送和接收数据
const char* message = "Hello, Server!";
send(sockfd, message, strlen(message), 0);char buffer[1024];
int n = recv(sockfd, buffer, sizeof(buffer), 0);
buffer[n] = '\0';
printf("Received: %s\n", buffer);
解析:
send
:发送数据,第四个参数为标志位,通常为0。recv
:接收数据,返回接收的字节数。
2.2.6 关闭套接字
close(sockfd);
2.3 经典实例:TCP客户端和服务器
2.3.1 TCP服务器
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>#define PORT 8080int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};const char* hello = "Hello from server";// 创建套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 强制绑定端口if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {perror("setsockopt");close(server_fd);exit(EXIT_FAILURE);}// 绑定地址address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 监听连接if (listen(server_fd, 3) < 0) {perror("listen");close(server_fd);exit(EXIT_FAILURE);}// 接受客户端连接if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");close(server_fd);exit(EXIT_FAILURE);}// 读取客户端数据int valread = read(new_socket, buffer, 1024);printf("%s\n", buffer);// 发送数据到客户端send(new_socket, hello, strlen(hello), 0);printf("Hello message sent\n");// 关闭套接字close(new_socket);close(server_fd);return 0;
}
解析:
- 创建套接字:
socket(AF_INET, SOCK_STREAM, 0)
- 绑定地址:
bind
- 监听连接:
listen
- 接受连接:
accept
- 读取数据:
read
- 发送数据:
send
- 关闭套接字:
close
2.3.2 TCP客户端
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>#define PORT 8080int main() {int sock = 0, valread;struct sockaddr_in serv_addr;const char* hello = "Hello from client";char buffer[1024] = {0};// 创建套接字if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket creation error");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);// 将IP地址转换成二进制形式if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {perror("Invalid address/ Address not supported");return -1;}// 连接服务器if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {perror("Connection Failed");return -1;}// 发送数据到服务器send(sock, hello, strlen(hello), 0);printf("Hello message sent\n");// 读取服务器响应valread = read(sock, buffer, 1024);printf("%s\n", buffer);// 关闭套接字close(sock);return 0;
}
解析:
- 创建套接字:
socket(AF_INET, SOCK_STREAM, 0)
- 连接服务器:
connect
- 发送数据:
send
- 读取数据:
read
- 关闭套接字:
close
运行结果
-
启动服务器:
./server
输出:
Hello from client Hello message sent
-
启动客户端:
./client
输出:
Hello message sent Hello from server
三、深入理解与高级应用
3.1 异步I/O与事件驱动编程
异步I/O:允许程序在等待I/O操作完成时继续执行其他任务,提高程序的并发性能。实现方式包括:
- 多线程:每个I/O操作分配一个线程处理。
- 事件驱动:使用事件循环处理I/O事件,例如
select
、poll
、epoll
等。
3.2 高效并发编程
多线程网络编程:
- 线程同步:避免多个线程同时访问共享资源,使用互斥锁、条件变量等。
- 线程池:复用线程,减少线程创建和销毁的开销,提高性能。
实例:使用线程池处理多个客户端连接。
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>#define PORT 8080std::mutex mtx; // 互斥锁用于保护共享资源
std::condition_variable cv; // 条件变量用于线程间同步
std::queue<int> clients; // 用于存储待处理的客户端套接字// 处理客户端连接的函数
void handle_client(int client_sock) {char buffer[1024];int n = recv(client_sock, buffer, sizeof(buffer), 0); // 接收客户端消息if (n > 0) {buffer[n] = '\0'; // 添加字符串终止符std::cout << "Received: " << buffer << std::endl;const char* response = "Message received";send(client_sock, response, strlen(response), 0); // 发送响应给客户端}close(client_sock); // 关闭客户端连接
}// 工作线程函数,用于处理客户端连接
void worker() {while (true) {int client_sock;{std::unique_lock<std::mutex> lock(mtx); // 获取锁cv.wait(lock, [] { return !clients.empty(); }); // 等待条件变量通知client_sock = clients.front(); // 从队列中取出客户端套接字clients.pop(); // 移除队列中的套接字}handle_client(client_sock); // 处理客户端连接}
}int main() {int server_sock = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字if (server_sock < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET; // 使用IPv4server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有可用的网络接口server_addr.sin_port = htons(PORT); // 绑定端口if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("bind failed");close(server_sock);exit(EXIT_FAILURE);}if (listen(server_sock, 5) < 0) { // 设置监听队列的最大长度为5perror("listen failed");close(server_sock);exit(EXIT_FAILURE);}// 创建多个工作线程std::vector<std::thread> workers;for (int i = 0; i < 4; ++i) {workers.emplace_back(worker); // 启动工作线程}while (true) {struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_len); // 接受客户端连接if (client_sock < 0) {perror("accept failed");continue;}{std::lock_guard<std::mutex> lock(mtx); // 获取锁clients.push(client_sock); // 将客户端套接字添加到队列中}cv.notify_one(); // 通知一个工作线程}for (auto& t : workers) {t.join(); // 等待所有工作线程结束}close(server_sock); // 关闭服务器套接字return 0;
}
3.3 使用Boost.Asio进行异步编程
Boost.Asio 是一个强大且灵活的库,提供了跨平台的异步I/O操作,支持多平台。我们继续讨论如何使用Boost.Asio实现一个高效的异步TCP服务器。
完整代码示例:使用Boost.Asio进行异步TCP服务器
BoostServer.cpp
#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <utility>using boost::asio::ip::tcp;// 会话类,用于管理单个客户端连接
class Session : public std::enable_shared_from_this<Session> {
public:explicit Session(tcp::socket socket): socket_(std::move(socket)) {}// 开始会话void start() {do_read(); // 开始读取数据}private:// 异步读取数据void do_read() {auto self(shared_from_this());socket_.async_read_some(boost::asio::buffer(data_, max_length),[this, self](boost::system::error_code ec, std::size_t length) {if (!ec) {std::cout << "Received: " << data_ << std::endl;do_write(length); // 读取完成后写入数据}});}// 异步写入数据void do_write(std::size_t length) {auto self(shared_from_this());boost::asio::async_write(socket_, boost::asio::buffer(data_, length),[this, self](boost::system::error_code ec, std::size_t /*length*/) {if (!ec) {do_read(); // 写入完成后继续读取数据}});}tcp::socket socket_; // 套接字enum { max_length = 1024 };char data_[max_length]; // 数据缓冲区
};// 服务器类,用于管理客户端连接
class Server {
public:Server(boost::asio::io_context& io_context, short port): acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {do_accept(); // 开始接受连接}private:// 异步接受连接void do_accept() {acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket) {if (!ec) {std::make_shared<Session>(std::move(socket))->start();}do_accept(); // 继续接受下一次连接});}tcp::acceptor acceptor_; // 接受器,用于监听连接
};int main(int argc, char* argv[]) {try {if (argc != 2) {std::cerr << "Usage: BoostServer <port>\n";return 1;}boost::asio::io_context io_context; // IO上下文Server s(io_context, std::atoi(argv[1])); // 创建服务器io_context.run(); // 运行IO上下文} catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";}return 0;
}
解析:
- Session类:管理单个客户端连接。
- do_read:异步读取数据,使用
async_read_some
方法。 - do_write:异步写入数据,使用
async_write
方法。
- do_read:异步读取数据,使用
- Server类:管理所有客户端连接。
- do_accept:异步接受新的连接,使用
async_accept
方法。
- do_accept:异步接受新的连接,使用
- main函数:创建
io_context
和Server
实例,调用io_context.run()
运行事件循环。
四、核心技术及高级话题
4.1 同步I/O与异步I/O的选择
同步I/O:
- 简单直接,易于编程和调试。
- 适合处理少量并发连接。
异步I/O:
- 提高并发性能,适合高并发场景。
- 编程复杂度较高,需要处理回调、状态管理等。
4.2 多线程与事件驱动模型
多线程模型:
- 每个连接分配一个线程处理。
- 线程同步开销大,线程数量有限制。
事件驱动模型:
- 使用事件循环处理I/O事件。
- 无需大量线程,仅需少量线程处理所有连接。
4.3 高效网络编程的关键技术
- I/O复用:如
select
、poll
、epoll
,实现单线程高效处理多个连接。 - 零拷贝:减少内存拷贝,提高数据传输效率。
- 内存池:复用内存,减少内存分配和释放开销。
- 负载均衡:分配连接到不同服务器,均衡负载,提高系统吞吐量。
4.4 使用C++11/14/17/20新特性优化网络编程
- 智能指针:如
std::shared_ptr
、std::unique_ptr
,管理动态内存,避免内存泄漏。 - lambda表达式:简化回调函数编写。
std::thread
:标准多线程库,简化线程创建和管理。std::async
:异步任务执行,简化异步编程。
五、实例:高效异步HTTP服务器
5.1 异步HTTP服务器
使用Boost.Asio实现一个高效的异步HTTP服务器:
#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <utility>using boost::asio::ip::tcp;class HttpSession : public std::enable_shared_from_this<HttpSession> {
public:explicit HttpSession(tcp::socket socket): socket_(std::move(socket)) {}void start() {do_read();}private:void do_read() {auto self(shared_from_this());socket_.async_read_some(boost::asio::buffer(buffer_),[this, self](boost::system::error_code ec, std::size_t length) {if (!ec) {handle_request(length);}});}void handle_request(std::size_t length) {std::string data(buffer_.data(), length);std::cout << "Request: " << data << std::endl;std::string response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, world!";auto self(shared_from_this());boost::asio::async_write(socket_, boost::asio::buffer(response),[this, self](boost::system::error_code ec, std::size_t /*length*/) {if (!ec) {socket_.shutdown(tcp::socket::shutdown_both, ec);}});}tcp::socket socket_;std::array<char, 8192> buffer_;
};class HttpServer {
public:HttpServer(boost::asio::io_context& io_context, short port): acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {do_accept();}private:void do_accept() {acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket) {if (!ec) {std::make_shared<HttpSession>(std::move(socket))->start();}do_accept();});}tcp::acceptor acceptor_;
};int main(int argc, char* argv[]) {try {if (argc != 2) {std::cerr << "Usage: HttpServer <port>\n";return 1;}boost::asio::io_context io_context;HttpServer server(io_context, std::atoi(argv[1]));io_context.run();} catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";}return 0;
}
解析:
- HttpSession类:管理单个HTTP会话。
- do_read:异步读取HTTP请求。
- handle_request:处理HTTP请求,生成响应。
- do_write:异步发送HTTP响应。
- HttpServer类:管理所有HTTP会话。
- do_accept:异步接受新的HTTP连接。
- main函数:创建
io_context
和HttpServer
实例,调用io_context.run()
运行事件循环。
运行结果
-
启动HTTP服务器:
./HttpServer 8080
-
使用浏览器或
curl
访问服务器:curl http://localhost:8080
输出:
Hello, world!
六、拓展思考
1、套接字(Socket)是什么?它包括哪些核心内容?
套接字(Socket)是计算机网络编程中一个重要的概念,它提供了一种通信的抽象,使得程序可以通过网络进行数据传输。套接字在应用层和传输层之间起到了桥梁作用,使得不同主机上的应用程序能够进行通信。换句话说,套接字是网络通信的端点,负责处理网络协议栈中的底层细节。
套接字的定义
套接字是一个网络编程接口,允许程序发送和接收数据,通过IP地址和端口号唯一标识网络中的通信端点。套接字可以基于多种传输协议进行操作,最常见的有TCP(传输控制协议)和UDP(用户数据报协议)。
套接字的核心内容
套接字编程涉及多个核心概念和操作步骤,下面详细介绍这些内容:
1. 套接字类型
- SOCK_STREAM:使用TCP协议,提供可靠的面向连接的通信。
- SOCK_DGRAM:使用UDP协议,提供不可靠的无连接的通信。
- SOCK_RAW:使用原始套接字,允许直接访问底层协议,如IP协议,通常用于网络测试和低层次的网络编程。
2. 地址族
- AF_INET:IPv4地址族。
- AF_INET6:IPv6地址族。
- AF_UNIX:本地通信的Unix套接字。
3. 套接字的基本操作
- 创建套接字:使用
socket()
系统调用创建一个套接字。 - 绑定地址:使用
bind()
将套接字绑定到本地地址和端口。 - 监听(仅用于TCP服务器):使用
listen()
将套接字置于监听模式,等待客户端连接。 - 接受连接(仅用于TCP服务器):使用
accept()
接受客户端连接,返回新的套接字用于通信。 - 连接(仅用于TCP客户端):使用
connect()
将套接字连接到服务器地址。 - 发送数据:使用
send()
或sendto()
发送数据。 - 接收数据:使用
recv()
或recvfrom()
接收数据。 - 关闭套接字:使用
close()
关闭套接字,释放资源。
套接字的高级内容
- 非阻塞模式:套接字可以设置为非阻塞模式,使得I/O操作不会阻塞程序执行。
- 多线程/多进程:使用多线程或多进程处理多个客户端连接,提高并发性能。
- 异步I/O:使用异步I/O操作,如
select
、poll
、epoll
或Boost.Asio,实现高效的事件驱动编程。 - 安全套接字:使用SSL/TLS加密通信,确保数据传输的安全性。
概括总结
套接字(Socket)是网络编程中关键的抽象和接口,提供了通过网络进行数据传输的基本功能。理解和掌握套接字的核心内容和操作步骤是进行网络编程的基础。通过深入学习和实践,开发者可以灵活地使用套接字进行各种网络应用的开发,从简单的客户端-服务器通信到复杂的分布式系统。
2、套接字 socket是基于哪个网络协议的?是TCP还是Http?
套接字(Socket)是一种网络编程的抽象,提供了一种通用的接口,使得程序能够灵活地使用不同的网络协议进行通信。套接字并不局限于某一种协议,可以基于TCP(SOCK_STREAM)或UDP(SOCK_DGRAM)等多种协议进行操作。HTTP协议是一种应用层协议,通常运行在TCP套接字之上。通过掌握套接字编程,可以实现各种网络应用,从简单的客户端-服务器通信到复杂的分布式系统。
总结
本文深入探讨了C++网络编程中的套接字(Socket)开发技术,详细讲解了其概念、底层原理、网络协议知识、本质及需要掌握的核心点,并通过经典实例进行解析。我们介绍了同步与异步I/O、多线程与事件驱动模型、高效网络编程的关键技术,最后详细展示了如何使用Boost.Asio实现一个高效的异步HTTP服务器。通过这些技术和示例代码,高级C++开发者可以深入理解和掌握网络编程的关键技术,为开发高效、可扩展的网络应用打下坚实的基础。希望这些代码和注释能够帮助你更好地理解和应用C++网络编程。
相关文章:
学懂C++(三十八):深入详解C++网络编程:套接字(Socket)开发技术
目录 一、概述与基础概念 1.1 套接字(Socket)概念 1.2 底层原理与网络协议 1.2.1 网络协议 1.2.2 套接字工作原理 二、C套接字编程核心技术 2.1 套接字编程的基本步骤 2.2 套接字编程详细实现 2.2.1 创建套接字 2.2.2 绑定地址 2.2.3 监听和接…...

SpringBoot-配置加载顺序
目录 前言 样例 内部配置加载顺序 样例 小结 前言 我之前写的配置文件,都是放在resources文件夹,根据当前目录下,优先级的高低,判断谁先被加载。但实际开发中,我们写的配置文件并不是,都放…...

第八周:机器学习笔记
第八周机器学习笔记 摘要Abstract机器学习1. 鱼和熊掌和可兼得的机器学习1.1 Deep network v.s. Fat network 2. 为什么用来验证集结果还是不好? Pytorch学习1. 卷积层代码实战2. 最大池化层代码实战3. 非线性激活层代码实战 总结 摘要 本周学习对李宏毅机器学习视…...

音乐怎么剪切掉一部分?5个方法,轻松学会音频分割!(2024全新)
音乐怎么剪切掉一部分?音频文件是娱乐和创作的重要基础。音频在我们日常生活中发挥着重要作用,从音乐播放列表到有趣的视频,它无处不在。无论是音乐爱好者还是内容创作者,我们常常需要对音频文件进行剪切和编辑。想象一下…...

洛谷 CF295D Greg and Caves
题目来源于:洛谷 题目本质:动态规划dp,枚举 解题思路:将整个洞分成两半,一半递增,一半递减。我们分别 DP 求值,最后合并。状态转移方程为:dpi,jk2∑j(j−k1)dpi−1,k1。枚举极…...
【图像处理】在图像处理算法开发中,有哪些常见的主观评价指标和客观评价指标?
主观评价指标 在图像处理算法开发中,主观评价指标依赖于观察者的个人感受和判断,通常用于评估图像的视觉质量。以下是一些常见的主观评价指标: 平均意见分数 (Mean Opinion Score, MOS):通过收集多个评价者的评分并计算平均值来评…...

从零开始学cv-6:图像的灰度变换
文章目录 一,简介:二、图像的线性变换三、分段线性变换四,非线性变换4.1 对数变换4.2 Gamma变换 五,效果: 一,简介: 图像灰度变换涉及对图像中每个像素的灰度值执行数学运算,进而调整图像的视觉…...

使用Apache POI和POI-OOXML实现word模板文档自动填充功能
最近接到一个新的需求,用户创建好模板文件保存到模板库,然后使用在线文档编辑器打开模板时,将系统数据填充到模板文件并生成新的word文件,然后在线编辑,研究使用Apache POI和POI-OOXML实现了这个功能。 Maven依赖 <…...

【HarmonyOS NEXT星河版开发学习】综合测试案例-各平台评论部分
目录 前言 功能展示 整体页面布局 最新和最热 写评论 点赞功能 界面构建 初始数据的准备 列表项部分的渲染 底部区域 index部分 知识点概述 List组件 List组件简介 ListItem组件详解 ListItemGroup组件介绍 ForEach循环渲染 列表分割线设置 列表排列方向设…...

垂直行业数字化表现抢眼 亚信科技全年利润展望乐观
大数据产业创新服务媒体 ——聚焦数据 改变商业 2024年8月14日,亚信科技控股有限公司(股票代码:01675.HK)公布了公司截至2024年6月30日的中期业绩。 财报数据显示,2024年上半年,亚信科技的营业收入为人民币…...

EmguCV学习笔记 VB.Net 4.1 颜色变换
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客 教程C#版本请访问:EmguCV学习笔记 C# 目录-CSD…...

【MySQL进阶之路】表结构的操作
目录 创建表 查看表 查看数据库有哪些表 查看表结构 查看表的详细信息 修改表 表的重命名 添加一列 修改某一列的属性 删除某一列 对列进行重命名 删除表 个人主页:东洛的克莱斯韦克-CSDN博客 【MySQL进阶之路】MySQL基础——从零认识MySQL-CSDN博客 创…...

3分钟搞定PDF转PPT!你一定要知道的3款转换神器!
在数字办公成为主流的当下,我们每天会收到各类基于数字化方式存储的办公文档,如PDF、PPT、Word、Excel文档等。 日常处理这些文档时,经常需要在不同格式的文档之间进行切换和转换,其中将PDF转换为PPT就是一个非常高频的需求&…...
【EasyExcel】导出excel-设置动态表头并导出数据
需求背景: 导出excel的设置某些表头动态导出(可以根据筛选条件或一些属性的数据量),方便导出后用户查看想看的信息。 一、技术选型: easyExcel的原生数据处理 二、方案设计: 根据EasyExcel支持的表头List<List<String>…...

深入探索 Elasticsearch 8:新特性与核心原理剖析(上)
深入探索 Elasticsearch 8:新特性与核心原理剖析 目录 一、引言 (二)版本 8 的重要意义 二、Elasticsearch 8 的新特性 三、Elasticsearch 的核心原理 一、引言 (一)Elasticsearch 简介 在大数据处理和搜索领域…...

瑜伽馆预约小程序,在线预约,提高商业价值
随着大众生活质量的提高,对休闲运动的关注逐渐加大,瑜伽作为一种身心放松、改善体态的运动,深受女性用户的喜爱。目前,各大瑜伽馆开始结合数字化,建立了新型的线上小程序,帮助大众快速预约体验瑜伽…...
Python--数据类型转换
在Python中,数据类型的转换是一个常见的操作,涉及将一种数据类型转换为另一种数据类型。Python提供了多种内置函数用于执行这种转换,如 int()、str()、float()、list()、tuple()、set()、dict() 等。下面详细讨论Python的基本数据类型及它们之…...

域控ntdsutil修改架构、域命名、PDC、RID、结构主机
#笔记记录# FSMO盒修改 1、提示访问特权不够,不能执行该操作,0x2098 清除缓存账号密码并修改新架构管理员账号密码即可。 背景:更替架构主机、域命名主机 C:\Windows\system32>ntdsutil ntdsutil: roles fsmo maintenance: ?? …...

解决 Swift 6 全局变量不能满足并发安全(concurrency-safe)读写的问题
概述 WWDC 24 终于在 Swift 十岁生日发布了全新的 Swift 6。这不仅意味着 Swift 进入了全新的“大”版本时代,而且 Swift 编译器终于做到了并发代码执行的“绝对安全”。 不过,从 Swift 5 一步迈入“新时代”的小伙伴们可能对新的并发检查有些许“水土不…...
迈入退休生活,全职开发ue独立游戏上架steam
决定退休了。算了算睡后收入,也可以达到每月一万一,正好可以养家糊口。 既然退休了,那就做些想做的事情,别人养花养草,而我打算开发独立游戏上架steam。 一,盘点下目前的技术体系。 1,图形学底…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...