当前位置: 首页 > news >正文

学懂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 套接字编程的基本步骤

  1. 创建套接字
  2. 绑定地址
  3. 监听(服务端)
  4. 接受连接(服务端)
  5. 连接(客户端)
  6. 发送和接收数据
  7. 关闭套接字

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_INETsin_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

运行结果

  1. 启动服务器:

    ./server
    

    输出

    Hello from client
    Hello message sent
    
  2. 启动客户端:

    ./client
    

    输出

    Hello message sent
    Hello from server
    

三、深入理解与高级应用

3.1 异步I/O与事件驱动编程

异步I/O:允许程序在等待I/O操作完成时继续执行其他任务,提高程序的并发性能。实现方式包括:

  • 多线程:每个I/O操作分配一个线程处理。
  • 事件驱动:使用事件循环处理I/O事件,例如selectpollepoll等。
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;
}

 

解析

  1. Session类:管理单个客户端连接。
    • do_read:异步读取数据,使用async_read_some方法。
    • do_write:异步写入数据,使用async_write方法。
  2. Server类:管理所有客户端连接。
    • do_accept:异步接受新的连接,使用async_accept方法。
  3. main函数:创建io_contextServer实例,调用io_context.run()运行事件循环。

四、核心技术及高级话题

4.1 同步I/O与异步I/O的选择

同步I/O

  • 简单直接,易于编程和调试。
  • 适合处理少量并发连接。

异步I/O

  • 提高并发性能,适合高并发场景。
  • 编程复杂度较高,需要处理回调、状态管理等。
4.2 多线程与事件驱动模型

多线程模型

  • 每个连接分配一个线程处理。
  • 线程同步开销大,线程数量有限制。

事件驱动模型

  • 使用事件循环处理I/O事件。
  • 无需大量线程,仅需少量线程处理所有连接。
4.3 高效网络编程的关键技术
  • I/O复用:如selectpollepoll,实现单线程高效处理多个连接。
  • 零拷贝:减少内存拷贝,提高数据传输效率。
  • 内存池:复用内存,减少内存分配和释放开销。
  • 负载均衡:分配连接到不同服务器,均衡负载,提高系统吞吐量。
4.4 使用C++11/14/17/20新特性优化网络编程
  • 智能指针:如std::shared_ptrstd::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;
}

 

解析

  1. HttpSession类:管理单个HTTP会话。
    • do_read:异步读取HTTP请求。
    • handle_request:处理HTTP请求,生成响应。
    • do_write:异步发送HTTP响应。
  2. HttpServer类:管理所有HTTP会话。
    • do_accept:异步接受新的HTTP连接。
  3. main函数:创建io_contextHttpServer实例,调用io_context.run()运行事件循环。

运行结果

  1. 启动HTTP服务器:

    ./HttpServer 8080
    
  2. 使用浏览器或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操作,如selectpollepoll或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 套接字&#xff08;Socket&#xff09;概念 1.2 底层原理与网络协议 1.2.1 网络协议 1.2.2 套接字工作原理 二、C套接字编程核心技术 2.1 套接字编程的基本步骤 2.2 套接字编程详细实现 2.2.1 创建套接字 2.2.2 绑定地址 2.2.3 监听和接…...

SpringBoot-配置加载顺序

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

第八周:机器学习笔记

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

音乐怎么剪切掉一部分?5个方法,轻松学会音频分割!(2024全新)

音乐怎么剪切掉一部分&#xff1f;音频文件是娱乐和创作的重要基础。音频在我们日常生活中发挥着重要作用&#xff0c;从音乐播放列表到有趣的视频&#xff0c;它无处不在。无论是音乐爱好者还是内容创作者&#xff0c;我们常常需要对音频文件进行剪切和编辑。想象一下&#xf…...

洛谷 CF295D Greg and Caves

题目来源于&#xff1a;洛谷 题目本质&#xff1a;动态规划dp&#xff0c;枚举 解题思路&#xff1a;将整个洞分成两半&#xff0c;一半递增&#xff0c;一半递减。我们分别 DP 求值&#xff0c;最后合并。状态转移方程为&#xff1a;dpi,j​k2∑j​(j−k1)dpi−1,k​1。枚举极…...

【图像处理】在图像处理算法开发中,有哪些常见的主观评价指标和客观评价指标?

主观评价指标 在图像处理算法开发中&#xff0c;主观评价指标依赖于观察者的个人感受和判断&#xff0c;通常用于评估图像的视觉质量。以下是一些常见的主观评价指标&#xff1a; 平均意见分数 (Mean Opinion Score, MOS)&#xff1a;通过收集多个评价者的评分并计算平均值来评…...

从零开始学cv-6:图像的灰度变换

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

使用Apache POI和POI-OOXML实现word模板文档自动填充功能

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

【HarmonyOS NEXT星河版开发学习】综合测试案例-各平台评论部分

目录 前言 功能展示 整体页面布局 最新和最热 写评论 点赞功能 界面构建 初始数据的准备 列表项部分的渲染 底部区域 index部分 知识点概述 List组件 List组件简介 ListItem组件详解 ListItemGroup组件介绍 ForEach循环渲染 列表分割线设置 列表排列方向设…...

垂直行业数字化表现抢眼 亚信科技全年利润展望乐观

大数据产业创新服务媒体 ——聚焦数据 改变商业 2024年8月14日&#xff0c;亚信科技控股有限公司&#xff08;股票代码&#xff1a;01675.HK&#xff09;公布了公司截至2024年6月30日的中期业绩。 财报数据显示&#xff0c;2024年上半年&#xff0c;亚信科技的营业收入为人民币…...

EmguCV学习笔记 VB.Net 4.1 颜色变换

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

【MySQL进阶之路】表结构的操作

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

3分钟搞定PDF转PPT!你一定要知道的3款转换神器!

在数字办公成为主流的当下&#xff0c;我们每天会收到各类基于数字化方式存储的办公文档&#xff0c;如PDF、PPT、Word、Excel文档等。 日常处理这些文档时&#xff0c;经常需要在不同格式的文档之间进行切换和转换&#xff0c;其中将PDF转换为PPT就是一个非常高频的需求&…...

【EasyExcel】导出excel-设置动态表头并导出数据

需求背景&#xff1a; 导出excel的设置某些表头动态导出(可以根据筛选条件或一些属性的数据量)&#xff0c;方便导出后用户查看想看的信息。 一、技术选型&#xff1a; easyExcel的原生数据处理 二、方案设计&#xff1a; 根据EasyExcel支持的表头List<List<String>…...

深入探索 Elasticsearch 8:新特性与核心原理剖析(上)

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

瑜伽馆预约小程序,在线预约,提高商业价值

随着大众生活质量的提高&#xff0c;对休闲运动的关注逐渐加大&#xff0c;瑜伽作为一种身心放松、改善体态的运动&#xff0c;深受女性用户的喜爱。目前&#xff0c;各大瑜伽馆开始结合数字化&#xff0c;建立了新型的线上小程序&#xff0c;帮助大众快速预约体验瑜伽&#xf…...

Python--数据类型转换

在Python中&#xff0c;数据类型的转换是一个常见的操作&#xff0c;涉及将一种数据类型转换为另一种数据类型。Python提供了多种内置函数用于执行这种转换&#xff0c;如 int()、str()、float()、list()、tuple()、set()、dict() 等。下面详细讨论Python的基本数据类型及它们之…...

域控ntdsutil修改架构、域命名、PDC、RID、结构主机

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

解决 Swift 6 全局变量不能满足并发安全(concurrency-safe)读写的问题

概述 WWDC 24 终于在 Swift 十岁生日发布了全新的 Swift 6。这不仅意味着 Swift 进入了全新的“大”版本时代&#xff0c;而且 Swift 编译器终于做到了并发代码执行的“绝对安全”。 不过&#xff0c;从 Swift 5 一步迈入“新时代”的小伙伴们可能对新的并发检查有些许“水土不…...

迈入退休生活,全职开发ue独立游戏上架steam

决定退休了。算了算睡后收入&#xff0c;也可以达到每月一万一&#xff0c;正好可以养家糊口。 既然退休了&#xff0c;那就做些想做的事情&#xff0c;别人养花养草&#xff0c;而我打算开发独立游戏上架steam。 一&#xff0c;盘点下目前的技术体系。 1&#xff0c;图形学底…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...