学懂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,图形学底…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...