网站首页 > 技术文章 正文
在Linux多线程服务端编程中,可以使用反射机制来根据类型名称自动创建Message对象。这种反射机制可以帮助在运行时动态地创建对象,而不需要提前知道对象的具体类型。
下面是关于根据类型名称反射自动创建Message对象的解释及举例:
解释:
- 获取消息描述符:首先,需要获取消息类型的描述符。描述符是一个包含有关消息类型的元数据的对象,可以通过描述符获取消息类型的名称和其他信息。
- 查找消息类型:使用描述符和类型名称,可以查找到对应的消息类型。这可以通过调用描述符的FindMessageTypeByName方法来实现。
- 创建消息对象:一旦找到了消息类型,就可以使用它来创建相应的消息对象。可以通过调用消息类型的New方法来创建新的消息对象。
举例:
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
// 根据类型名称自动创建Message对象
google::protobuf::Message* createMessage(const std::string& typeName) {
// 获取消息描述符
const google::protobuf::Descriptor* descriptor =
google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
if (descriptor) {
// 查找消息类型
const google::protobuf::Message* prototype =
google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
if (prototype) {
// 创建消息对象
return prototype->New();
}
}
return nullptr;
}
int main() {
// 使用示例
std::string typeName = "MyMessage"; // 消息类型名称
google::protobuf::Message* message = createMessage(typeName);
if (message) {
// 对消息对象进行操作
message->set_field1("Hello");
message->set_field2(123);
// ...
delete message;
}
return 0;
}
在上面的示例中,createMessage函数根据类型名称自动创建了一个MyMessage类型的消息对象,并对其进行了操作。这种方法可以方便地根据类型名称动态地创建消息对象,而不需要提前知道对象的具体类型。
在Linux多线程服务端编程中,可以使用反射机制来根据类型名称自动创建Message对象。这种反射机制可以帮助在运行时动态地创建对象,而不需要提前知道对象的具体类型。
下面是关于根据类型名称反射自动创建Message对象的解释及举例:
解释:
- 获取消息描述符:首先,需要获取消息类型的描述符。描述符是一个包含有关消息类型的元数据的对象,可以通过描述符获取消息类型的名称和其他信息。
- 查找消息类型:使用描述符和类型名称,可以查找到对应的消息类型。这可以通过调用描述符的FindMessageTypeByName方法来实现。
- 创建消息对象:一旦找到了消息类型,就可以使用它来创建相应的消息对象。可以通过调用消息类型的New方法来创建新的消息对象。
举例:
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
// 根据类型名称自动创建Message对象
google::protobuf::Message* createMessage(const std::string& typeName) {
// 获取消息描述符
const google::protobuf::Descriptor* descriptor =
google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
if (descriptor) {
// 查找消息类型
const google::protobuf::Message* prototype =
google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
if (prototype) {
// 创建消息对象
return prototype->New();
}
}
return nullptr;
}
int main() {
// 使用示例
std::string typeName = "MyMessage"; // 消息类型名称
google::protobuf::Message* message = createMessage(typeName);
if (message) {
// 对消息对象进行操作
message->set_field1("Hello");
message->set_field2(123);
// ...
delete message;
}
return 0;
}
在上面的示例中,createMessage函数根据类型名称自动创建了一个MyMessage类型的消息对象,并对其进行了操作。这种方法可以方便地根据类型名称动态地创建消息对象,而不需要提前知道对象的具体类型。
在Linux多线程服务端编程中,Protobuf(Protocol Buffers)是一种用于序列化结构化数据的格式。它使用二进制编码来表示数据,具有高效、紧凑和可扩展的特性。Protobuf可以用于在不同的系统之间传输数据,同时还支持多种编程语言。
下面是关于Protobuf传输格式的解释及举例:
解释:
- 定义消息类型:首先,需要定义消息类型的.proto文件,其中定义了消息的字段和类型。例如,可以定义一个简单的消息类型如下:
syntax = "proto3";
message MyMessage {
string name = 1;
int32 age = 2;
}
- 生成代码:使用Protobuf编译器(protoc)根据.proto文件生成相应的代码。例如,使用以下命令生成C++代码:
protoc --cpp_out=. my_message.proto
- 序列化和反序列化:在发送端,将消息对象序列化为二进制数据;在接收端,将接收到的二进制数据反序列化为消息对象。这样可以在不同的系统之间传输数据。
举例:
服务端代码示例(C++):
#include "my_message.pb.h"
#include <iostream>
#include <string>
void processMessage(const std::string& data) {
MyMessage message;
if (message.ParseFromString(data)) {
std::cout << "Received message: " << message.name() << ", " << message.age() << std::endl;
// 处理消息
}
}
int main() {
std::string receivedData = "...\x0A\x05Alice\x10\x1E..."; // 接收到的数据
processMessage(receivedData);
return 0;
}
客户端代码示例(C++):
#include "my_message.pb.h"
#include <iostream>
#include <string>
std::string createMessage() {
MyMessage message;
message.set_name("Alice");
message.set_age(30);
std::string serializedData;
message.SerializeToString(&serializedData);
return serializedData;
}
int main() {
std::string dataToSend = createMessage(); // 待发送的数据
std::cout << "Sending message: " << dataToSend << std::endl;
// 发送数据到服务端
return 0;
}
在上述示例中,服务端接收到客户端发送的数据后,使用ParseFromString()方法将数据反序列化为MyMessage对象。客户端使用SerializeToString()方法将MyMessage对象序列化为二进制数据。这样,就可以在服务端和客户端之间传输数据。
在Linux多线程服务端编程中,muduo是一个基于Reactor模式的C++网络库,提供了高性能的网络编程框架。muduo库中提供了Protobuf编解码器和消息分发器,用于处理基于Protobuf的消息传输。
解释:
- Protobuf编解码器:muduo提供了一个Protobuf编解码器,用于将Protobuf消息对象序列化为二进制数据,并将二进制数据反序列化为Protobuf消息对象。编解码器使用了muduo库提供的Buffer类来处理数据的读写。
- 消息分发器:muduo还提供了一个消息分发器,用于根据消息类型将收到的消息分发给相应的处理函数。消息分发器使用了muduo库提供的Callback类来实现消息处理函数的注册和调用。
举例:
下面是一个使用muduo库实现Protobuf编解码器和消息分发器的示例:
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/protobuf/ProtobufCodec.h>
#include "my_message.pb.h"
using namespace muduo;
using namespace muduo::net;
// 处理MyMessage消息的回调函数
void onMyMessage(const TcpConnectionPtr& conn, const MyMessagePtr& message, Timestamp receiveTime) {
// 处理收到的消息
// ...
}
int main() {
EventLoop loop;
InetAddress listenAddr(8888);
TcpServer server(&loop, listenAddr, "MyServer");
// 注册MyMessage消息的处理函数
server.setConnectionCallback([](const TcpConnectionPtr& conn) {
// 连接建立时的处理
if (conn->connected()) {
conn->setMessageCallback([](const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime) {
// 收到消息时的处理
ProtobufCodec codec;
codec.onMessage(conn, buf, receiveTime, onMyMessage);
});
}
});
server.start();
loop.loop();
return 0;
}
在上述示例中,我们创建了一个muduo的TcpServer,并设置了连接回调函数和消息回调函数。在消息回调函数中,我们使用了muduo提供的ProtobufCodec来处理收到的消息,并将其反序列化为MyMessage对象。然后,我们将MyMessage对象传递给onMyMessage函数进行处理。
通过使用muduo库提供的Protobuf编解码器和消息分发器,我们可以方便地处理基于Protobuf的消息传输,实现高性能的Linux多线程服务端编程。
C++标准库本身并没有提供专门用于实现Protobuf编解码的类,因为Protobuf是由Google开发的一种数据序列化协议,它需要使用Protobuf库来进行编解码操作。但是,C++标准库提供了一些基本的工具和类,可以辅助实现Protobuf编解码器。
下面是一个使用C++标准库实现Protobuf编解码器的简单示例:
#include <iostream>
#include <string>
#include "my_message.pb.h"
// 编码器:将MyMessage对象序列化为二进制数据
std::string encode(const MyMessage& message) {
std::string buffer;
if (!message.SerializeToString(&buffer)) {
std::cerr << "Failed to serialize MyMessage." << std::endl;
}
return buffer;
}
// 解码器:将二进制数据反序列化为MyMessage对象
MyMessage decode(const std::string& buffer) {
MyMessage message;
if (!message.ParseFromString(buffer)) {
std::cerr << "Failed to parse MyMessage." << std::endl;
}
return message;
}
int main() {
// 创建一个MyMessage对象
MyMessage message;
message.set_id(123);
message.set_name("John Doe");
// 使用编码器将MyMessage对象序列化为二进制数据
std::string encodedData = encode(message);
// 输出序列化后的二进制数据
std::cout << "Encoded data: " << encodedData << std::endl;
// 使用解码器将二进制数据反序列化为MyMessage对象
MyMessage decodedMessage = decode(encodedData);
// 输出反序列化后的MyMessage对象
std::cout << "Decoded message: " << decodedMessage.id() << ", " << decodedMessage.name() << std::endl;
return 0;
}
在上述示例中,我们使用了C++标准库提供的string类来存储编码后的二进制数据,并使用了Protobuf库提供的SerializeToString和ParseFromString函数来进行编解码操作。encode函数接受一个MyMessage对象作为参数,并将其序列化为二进制数据;decode函数接受一个二进制数据字符串作为参数,并将其反序列化为MyMessage对象。
请注意,为了使示例能够编译通过并运行,你需要先定义一个名为my_message.pb.h的Protobuf消息定义文件,并使用protoc工具生成相应的C++代码。
C++标准库本身并没有提供专门用于实现Protobuf编解码的类,因为Protobuf是由Google开发的一种数据序列化协议,它需要使用Protobuf库来进行编解码操作。但是,C++标准库提供了一些基本的工具和类,可以辅助实现Protobuf编解码器。
下面是一个使用C++标准库实现Protobuf编解码器的简单示例:
#include <iostream>
#include <string>
#include "my_message.pb.h"
// 编码器:将MyMessage对象序列化为二进制数据
std::string encode(const MyMessage& message) {
std::string buffer;
if (!message.SerializeToString(&buffer)) {
std::cerr << "Failed to serialize MyMessage." << std::endl;
}
return buffer;
}
// 解码器:将二进制数据反序列化为MyMessage对象
MyMessage decode(const std::string& buffer) {
MyMessage message;
if (!message.ParseFromString(buffer)) {
std::cerr << "Failed to parse MyMessage." << std::endl;
}
return message;
}
int main() {
// 创建一个MyMessage对象
MyMessage message;
message.set_id(123);
message.set_name("John Doe");
// 使用编码器将MyMessage对象序列化为二进制数据
std::string encodedData = encode(message);
// 输出序列化后的二进制数据
std::cout << "Encoded data: " << encodedData << std::endl;
// 使用解码器将二进制数据反序列化为MyMessage对象
MyMessage decodedMessage = decode(encodedData);
// 输出反序列化后的MyMessage对象
std::cout << "Decoded message: " << decodedMessage.id() << ", " << decodedMessage.name() << std::endl;
return 0;
}
在上述示例中,我们使用了C++标准库提供的string类来存储编码后的二进制数据,并使用了Protobuf库提供的SerializeToString和ParseFromString函数来进行编解码操作。encode函数接受一个MyMessage对象作为参数,并将其序列化为二进制数据;decode函数接受一个二进制数据字符串作为参数,并将其反序列化为MyMessage对象。
请注意,为了使示例能够编译通过并运行,你需要先定义一个名为my_message.pb.h的Protobuf消息定义文件,并使用protoc工具生成相应的C++代码。
在C++标准库中,并没有直接提供消息分发器(dispatcher)的类,但是可以通过使用C++标准库中的一些类和模式来实现消息分发器的功能。
消息分发器的作用是将接收到的消息分发给相应的处理函数或对象。它可以用于实现事件驱动的编程模型,使得不同的消息可以被不同的处理函数或对象处理。
举例:
下面是一个简单的示例,演示如何使用C++标准库中的函数指针和std::map来实现一个简单的消息分发器:
#include <iostream>
#include <map>
typedef void (*MessageHandler)(const std::string& message);
void HandleMessage1(const std::string& message) {
std::cout << "Handling message 1: " << message << std::endl;
}
void HandleMessage2(const std::string& message) {
std::cout << "Handling message 2: " << message << std::endl;
}
int main() {
std::map<std::string, MessageHandler> messageHandlers;
messageHandlers["message1"] = HandleMessage1;
messageHandlers["message2"] = HandleMessage2;
std::string message;
while (std::getline(std::cin, message)) {
if (messageHandlers.find(message) != messageHandlers.end()) {
MessageHandler handler = messageHandlers[message];
handler(message);
} else {
std::cout << "Unknown message: " << message << std::endl;
}
}
return 0;
}
在上面的示例中,我们定义了两个处理函数HandleMessage1和HandleMessage2,它们分别用于处理不同的消息类型。然后,我们使用std::map来建立消息和处理函数之间的映射关系。在主循环中,我们从标准输入读取消息,并根据消息在map中查找对应的处理函数,然后调用该处理函数来处理消息。如果找不到对应的处理函数,我们输出一个错误消息。这样,我们就实现了一个简单的消息分发器。
在C++标准库中,并没有直接提供消息分发器(dispatcher)的类,但是可以通过使用C++标准库中的一些类和模式来实现消息分发器的功能。
消息分发器的作用是将接收到的消息分发给相应的处理函数或对象。它可以用于实现事件驱动的编程模型,使得不同的消息可以被不同的处理函数或对象处理。
举例:
下面是一个简单的示例,演示如何使用C++标准库中的函数指针和std::map来实现一个简单的消息分发器:
#include <iostream>
#include <map>
typedef void (*MessageHandler)(const std::string& message);
void HandleMessage1(const std::string& message) {
std::cout << "Handling message 1: " << message << std::endl;
}
void HandleMessage2(const std::string& message) {
std::cout << "Handling message 2: " << message << std::endl;
}
int main() {
std::map<std::string, MessageHandler> messageHandlers;
messageHandlers["message1"] = HandleMessage1;
messageHandlers["message2"] = HandleMessage2;
std::string message;
while (std::getline(std::cin, message)) {
if (messageHandlers.find(message) != messageHandlers.end()) {
MessageHandler handler = messageHandlers[message];
handler(message);
} else {
std::cout << "Unknown message: " << message << std::endl;
}
}
return 0;
}
在上面的示例中,我们定义了两个处理函数HandleMessage1和HandleMessage2,它们分别用于处理不同的消息类型。然后,我们使用std::map来建立消息和处理函数之间的映射关系。在主循环中,我们从标准输入读取消息,并根据消息在map中查找对应的处理函数,然后调用该处理函数来处理消息。如果找不到对应的处理函数,我们输出一个错误消息。这样,我们就实现了一个简单的消息分发器。
在C++标准库中,并没有直接提供ProtobufDispatcher这个类。但是可以通过使用C++标准库中的一些类和模式来实现ProtobufDispatcher的功能。下面介绍两种常见的实现方式:
- 使用函数指针和std::map
#include <iostream>
#include <map>
#include <functional>
typedef std::function<void(const std::string&)> MessageHandler;
class ProtobufDispatcher {
public:
void RegisterHandler(int messageType, MessageHandler handler) {
handlers_[messageType] = handler;
}
void DispatchMessage(int messageType, const std::string& message) {
auto it = handlers_.find(messageType);
if (it != handlers_.end()) {
it->second(message);
} else {
std::cout << "No handler registered for message type: " << messageType << std::endl;
}
}
private:
std::map<int, MessageHandler> handlers_;
};
void HandleMessage1(const std::string& message) {
std::cout << "Handling message 1: " << message << std::endl;
}
void HandleMessage2(const std::string& message) {
std::cout << "Handling message 2: " << message << std::endl;
}
int main() {
ProtobufDispatcher dispatcher;
dispatcher.RegisterHandler(1, HandleMessage1);
dispatcher.RegisterHandler(2, HandleMessage2);
dispatcher.DispatchMessage(1, "Hello, message 1");
dispatcher.DispatchMessage(2, "Hello, message 2");
dispatcher.DispatchMessage(3, "Hello, message 3");
return 0;
}
- 使用std::unordered_map和std::any
#include <iostream>
#include <unordered_map>
#include <any>
class ProtobufDispatcher {
public:
template <typename T>
void RegisterHandler(int messageType, void (T::*handler)(const std::string&), T* instance) {
handlers_[messageType] = [instance, handler](const std::string& message) {
(instance->*handler)(message);
};
}
void DispatchMessage(int messageType, const std::string& message) {
auto it = handlers_.find(messageType);
if (it != handlers_.end()) {
it->second(message);
} else {
std::cout << "No handler registered for message type: " << messageType << std::endl;
}
}
private:
std::unordered_map<int, std::function<void(const std::string&)>> handlers_;
};
class MessageHandler {
public:
void HandleMessage1(const std::string& message) {
std::cout << "Handling message 1: " << message << std::endl;
}
void HandleMessage2(const std::string& message) {
std::cout << "Handling message 2: " << message << std::endl;
}
};
int main() {
ProtobufDispatcher dispatcher;
MessageHandler handler;
dispatcher.RegisterHandler(1, &MessageHandler::HandleMessage1, &handler);
dispatcher.RegisterHandler(2, &MessageHandler::HandleMessage2, &handler);
dispatcher.DispatchMessage(1, "Hello, message 1");
dispatcher.DispatchMessage(2, "Hello, message 2");
dispatcher.DispatchMessage(3, "Hello, message 3");
return 0;
}
以上是两种常见的ProtobufDispatcher的实现方式,可以根据具体需求选择适合的方式。这些实现方式都可以实现将接收到的消息分发给相应的处理函数或对象。
ProtobufCodec和ProtobufDispatcher在C++标准库中并不存在,它们是自定义的类或组件,用于处理Protobuf消息的编解码和分发。它们的意义在于简化和统一Protobuf消息的处理过程,提高代码的可维护性和可扩展性。
ProtobufCodec的意义:
- 将Protobuf对象序列化为二进制数据并反序列化回对象,简化了消息的编解码过程。
- 提供了统一的接口,使得不同的模块或组件可以使用相同的编解码器进行消息处理。
- 可以处理不同类型的Protobuf消息,使得代码更加灵活和可扩展。
ProtobufDispatcher的意义:
- 将接收到的消息分发给相应的处理函数或对象,简化了消息的分发过程。
- 可以根据消息类型动态注册和注销处理函数,使得代码更加灵活和可配置。
- 可以将消息的处理逻辑从主循环中解耦出来,提高代码的可维护性和可扩展性。
下面是一个示例,展示了ProtobufCodec和ProtobufDispatcher的使用:
#include <iostream>
#include <string>
#include "my_message.pb.h"
// Protobuf编解码器
class ProtobufCodec {
public:
std::string encode(const MyMessage& message) {
std::string buffer;
if (!message.SerializeToString(&buffer)) {
std::cerr << "Failed to serialize MyMessage." << std::endl;
}
return buffer;
}
MyMessage decode(const std::string& buffer) {
MyMessage message;
if (!message.ParseFromString(buffer)) {
std::cerr << "Failed to parse MyMessage." << std::endl;
}
return message;
}
};
// Protobuf消息分发器
class ProtobufDispatcher {
public:
using MessageHandler = std::function<void(const MyMessage&)>;
void registerHandler(int messageType, MessageHandler handler) {
handlers_[messageType] = handler;
}
void handleMessage(int messageType, const std::string& buffer) {
if (handlers_.count(messageType) > 0) {
MyMessage message = codec_.decode(buffer);
handlers_[messageType](message);
} else {
std::cerr << "No handler registered for message type: " << messageType << std::endl;
}
}
private:
ProtobufCodec codec_;
std::map<int, MessageHandler> handlers_;
};
// 示例使用
void handleFooMessage(const MyMessage& message) {
std::cout << "Handling FooMessage: " << message.foo() << std::endl;
}
int main() {
ProtobufDispatcher dispatcher;
dispatcher.registerHandler(1, handleFooMessage);
ProtobufCodec codec;
MyMessage fooMessage;
fooMessage.set_foo("Hello, Protobuf!");
std::string buffer = codec.encode(fooMessage);
dispatcher.handleMessage(1, buffer);
return 0;
}
在上述示例中,ProtobufCodec负责将MyMessage对象进行编码和解码,ProtobufDispatcher负责将接收到的消息分发给相应的处理函数。通过使用ProtobufCodec和ProtobufDispatcher,我们可以更加方便地处理Protobuf消息,并且可以根据需要注册和注销处理函数。
在C++标准库中,可以使用<ctime>头文件来获取程序中的时间信息。该头文件中定义了一些与时间相关的函数和结构体,可以用于获取当前时间、计算时间差等操作。
以下是一些常用的时间函数和结构体:
- time_t:表示从1970年1月1日开始经过的秒数,可以通过time()函数获取当前的time_t值。
#include <ctime>
time_t now = time(nullptr); // 获取当前时间的time_t值
- struct tm:表示日期和时间的结构体,包含了年、月、日、时、分、秒等信息。可以使用localtime()函数将time_t值转换为struct tm结构体。
#include <ctime>
time_t now = time(nullptr);
struct tm* localTime = localtime(&now); // 将time_t值转换为本地时间的struct tm结构体
- strftime():可以将struct tm结构体格式化为指定的字符串。
#include <ctime>
time_t now = time(nullptr);
struct tm* localTime = localtime(&now);
char buffer[80];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localTime); // 格式化struct tm为字符串
通过使用以上的时间函数和结构体,我们可以在程序中获取当前时间、格式化时间、计算时间差等操作。
在Linux系统中,可以使用C++标准库中的<ctime>头文件提供的函数来获取时间信息。这些函数包括:
- time():获取当前时间的秒数,返回一个time_t类型的值。
#include <iostream>
#include <ctime>
int main() {
time_t currentTime = time(nullptr);
std::cout << "Current time: " << currentTime << std::endl;
return 0;
}
- localtime():将time_t类型的时间值转换为本地时间,并以tm结构体的形式返回。
#include <iostream>
#include <ctime>
int main() {
time_t currentTime = time(nullptr);
struct tm* localTime = localtime(¤tTime);
std::cout << "Current local time: " << asctime(localTime);
return 0;
}
- strftime():将tm结构体中的时间信息格式化为字符串。
#include <iostream>
#include <ctime>
int main() {
time_t currentTime = time(nullptr);
struct tm* localTime = localtime(¤tTime);
char buffer[80];
strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", localTime);
std::cout << "Formatted time: " << buffer << std::endl;
return 0;
}
这些函数可以帮助我们获取当前时间、转换为本地时间、格式化时间字符串等操作,方便在程序中使用时间信息。
C++标准库中没有直接提供测量两台机器的网络延迟和时间差的功能。但可以使用一些其他的库或工具来实现这个功能。
一种常用的方法是使用ping命令来测量两台机器之间的网络延迟。在C++程序中可以使用system函数来执行ping命令,并解析命令输出来获取延迟信息。
以下是一个使用C++程序来执行ping命令并获取延迟信息的示例:
#include <iostream>
#include <cstdio>
#include <cstring>
int main() {
// 执行ping命令
const char* command = "ping -c 4 google.com";
FILE* pipe = popen(command, "r");
if (!pipe) {
std::cerr << "Error executing command" << std::endl;
return -1;
}
// 读取命令输出
const int buffer_size = 128;
char buffer[buffer_size];
std::string result;
while (!feof(pipe)) {
if (fgets(buffer, buffer_size, pipe) != NULL) {
result += buffer;
}
}
// 关闭命令管道
pclose(pipe);
// 解析延迟信息
const char* delay_start = "time=";
const char* delay_end = " ms";
size_t start_pos = result.find(delay_start);
size_t end_pos = result.find(delay_end, start_pos);
if (start_pos != std::string::npos && end_pos != std::string::npos) {
std::string delay_str = result.substr(start_pos + strlen(delay_start), end_pos - start_pos - strlen(delay_start));
double delay = std::stod(delay_str);
std::cout << "延迟: " << delay << " ms" << std::endl;
} else {
std::cerr << "Failed to parse delay information" << std::endl;
}
return 0;
}
上述示例中,我们使用了popen函数来执行ping命令并获取输出结果。然后通过解析输出结果来获取延迟信息。
需要注意的是,这种方法只能提供一个大致的延迟估计,并且需要依赖系统的ping命令。如果需要更精确的延迟测量,可以考虑使用专门的网络测量工具或库。
C++标准库中没有直接提供timing wheel来踢掉空闲连接的功能。但可以通过自己实现一个timing wheel来实现这个功能。
Timing wheel是一种基于时间轮算法的数据结构,用于管理定时任务。它将时间划分为多个时间槽,并将任务按照其到期时间放置在相应的时间槽中。每经过一个时间槽,就会检查该槽中的任务是否到期,如果到期则执行相应的操作。
以下是一个简单的示例代码,演示如何使用timing wheel来踢掉空闲连接:
#include <iostream>
#include <chrono>
#include <thread>
#include <vector>
#include <unordered_map>
// 定义连接对象
struct Connection {
int id;
std::chrono::time_point<std::chrono::system_clock> lastActiveTime;
};
// 定义时间槽
struct Slot {
std::vector<Connection*> connections;
};
class TimingWheel {
public:
TimingWheel(int slotCount, int tickInterval)
: slotCount_(slotCount), tickInterval_(tickInterval), currentSlot_(0) {
slots_.resize(slotCount_);
}
// 添加连接到时间轮
void addConnection(Connection* connection) {
int slotIndex = (currentSlot_ + 1) % slotCount_;
slots_[slotIndex].connections.push_back(connection);
connectionMap_[connection->id] = connection;
}
// 踢掉空闲连接
void kickIdleConnections() {
int slotIndex = currentSlot_;
for (auto connection : slots_[slotIndex].connections) {
auto now = std::chrono::system_clock::now();
auto idleTime = std::chrono::duration_cast<std::chrono::seconds>(now - connection->lastActiveTime);
if (idleTime.count() >= tickInterval_) {
// 执行踢掉连接的操作
std::cout << "Kicking idle connection: " << connection->id << std::endl;
connectionMap_.erase(connection->id);
delete connection;
}
}
slots_[slotIndex].connections.clear();
currentSlot_ = (currentSlot_ + 1) % slotCount_;
}
private:
int slotCount_; // 时间槽数量
int tickInterval_; // 时间间隔
int currentSlot_; // 当前时间槽索引
std::vector<Slot> slots_; // 时间槽数组
std::unordered_map<int, Connection*> connectionMap_; // 连接对象的映射表
};
int main() {
TimingWheel timingWheel(10, 5); // 10个时间槽,每个时间槽5秒
Connection* connection1 = new Connection{1, std::chrono::system_clock::now()};
Connection* connection2 = new Connection{2, std::chrono::system_clock::now()};
Connection* connection3 = new Connection{3, std::chrono::system_clock::now()};
timingWheel.addConnection(connection1);
timingWheel.addConnection(connection2);
timingWheel.addConnection(connection3);
// 模拟时间流逝
for (int i = 0; i < 20; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1));
timingWheel.kickIdleConnections();
}
return 0;
}
以上示例中,我们使用了一个TimingWheel类来管理连接对象。在每个时间槽中,我们检查连接对象的最后活动时间,如果连接已经超过指定的时间间隔没有活动,则执行踢掉连接的操作。通过模拟时间流逝,我们可以看到空闲连接被成功踢掉的效果。
C++标准库中没有直接提供消息广播服务的功能。但我们可以使用其他库或自己实现一个简单的消息广播服务。
消息广播服务是一种发布-订阅模式的通信方式,其中一个消息发布者可以向多个消息订阅者广播消息。下面是一个简单的示例实现:
#include <iostream>
#include <vector>
class MessageSubscriber {
public:
virtual void receiveMessage(const std::string& message) = 0;
};
class MessagePublisher {
public:
void subscribe(MessageSubscriber* subscriber) {
subscribers.push_back(subscriber);
}
void unsubscribe(MessageSubscriber* subscriber) {
auto it = std::find(subscribers.begin(), subscribers.end(), subscriber);
if (it != subscribers.end()) {
subscribers.erase(it);
}
}
void publishMessage(const std::string& message) {
for (auto subscriber : subscribers) {
subscriber->receiveMessage(message);
}
}
private:
std::vector<MessageSubscriber*> subscribers;
};
class ConsoleSubscriber : public MessageSubscriber {
public:
void receiveMessage(const std::string& message) override {
std::cout << "Received message: " << message << std::endl;
}
};
int main() {
MessagePublisher publisher;
ConsoleSubscriber subscriber1;
ConsoleSubscriber subscriber2;
publisher.subscribe(&subscriber1);
publisher.subscribe(&subscriber2);
publisher.publishMessage("Hello, subscribers!");
publisher.unsubscribe(&subscriber2);
publisher.publishMessage("Goodbye, subscribers!");
return 0;
}
在上面的示例中,我们定义了一个MessageSubscriber接口和一个MessagePublisher类。MessageSubscriber是一个纯虚类,定义了一个receiveMessage方法,用于接收消息。MessagePublisher类负责管理订阅者列表,并提供订阅、取消订阅和发布消息的功能。
我们还定义了一个ConsoleSubscriber类,它是MessageSubscriber的一个实现,用于在控制台上显示接收到的消息。
在main函数中,我们创建了一个MessagePublisher对象和两个ConsoleSubscriber对象。然后,我们订阅了这两个订阅者,并向它们发布了两条消息。最后,我们取消了一个订阅者的订阅,并再次发布了一条消息。
运行上述代码,输出将会是:
Received message: Hello, subscribers!
Received message: Hello, subscribers!
Received message: Goodbye, subscribers!
这个简单的消息广播服务示例可以作为一个基础,你可以根据自己的需求进行扩展和改进。例如,你可以添加更多的订阅者类型,实现不同的消息处理逻辑,或者使用其他库来实现更复杂的消息广播功能。
C++标准库本身并没有提供直接的TCP中继器功能。但我们可以使用C++标准库中的套接字(Socket)相关函数来实现TCP中继器。
TCP中继器是一种将客户端与服务器之间的TCP连接中继到另一个服务器的应用程序。它可以用于实现数据转发、负载均衡、数据过滤等功能。
下面是一个简单的示例,演示如何使用C++标准库中的套接字函数来实现TCP中继器:
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 1024
int main() {
int serverSocket, clientSocket, relaySocket;
struct sockaddr_in serverAddr, clientAddr, relayAddr;
char buffer[BUFFER_SIZE];
// 创建服务器套接字
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
std::cerr << "Failed to create server socket" << std::endl;
return 1;
}
// 绑定服务器地址和端口
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8888);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
std::cerr << "Failed to bind server socket" << std::endl;
return 1;
}
// 监听客户端连接
if (listen(serverSocket, 5) < 0) {
std::cerr << "Failed to listen on server socket" << std::endl;
return 1;
}
std::cout << "Server listening on port 8888" << std::endl;
while (true) {
socklen_t clientAddrLen = sizeof(clientAddr);
// 接受客户端连接
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket < 0) {
std::cerr << "Failed to accept client connection" << std::endl;
continue;
}
std::cout << "Accepted client connection from " << inet_ntoa(clientAddr.sin_addr) << std::endl;
// 创建中继套接字
relaySocket = socket(AF_INET, SOCK_STREAM, 0);
if (relaySocket < 0) {
std::cerr << "Failed to create relay socket" << std::endl;
close(clientSocket);
continue;
}
// 连接中继服务器
relayAddr.sin_family = AF_INET;
relayAddr.sin_port = htons(9999);
relayAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(relaySocket, (struct sockaddr*)&relayAddr, sizeof(relayAddr)) < 0) {
std::cerr << "Failed to connect to relay server" << std::endl;
close(clientSocket);
close(relaySocket);
continue;
}
std::cout << "Connected to relay server" << std::endl;
// 开始中继数据
while (true) {
memset(buffer, 0, BUFFER_SIZE);
// 从客户端接收数据
int bytesRead = recv(clientSocket, buffer, BUFFER_SIZE, 0);
if (bytesRead <= 0) {
std::cerr << "Error reading from client socket" << std::endl;
break;
}
// 将数据发送到中继服务器
int bytesSent = send(relaySocket, buffer, bytesRead, 0);
if (bytesSent <= 0) {
std::cerr << "Error sending data to relay server" << std::endl;
break;
}
// 从中继服务器接收数据
memset(buffer, 0, BUFFER_SIZE);
bytesRead = recv(relaySocket, buffer, BUFFER_SIZE, 0);
if (bytesRead <= 0) {
std::cerr << "Error reading from relay server" << std::endl;
break;
}
// 将数据发送回客户端
bytesSent = send(clientSocket, buffer, bytesRead, 0);
if (bytesSent <= 0) {
std::cerr << "Error sending data to client" << std::endl;
break;
}
}
// 关闭套接字
close(clientSocket);
close(relaySocket);
}
// 关闭服务器套接字
close(serverSocket);
return 0;
}
在这个示例中,我们创建了一个服务器套接字,并绑定到本地8888端口。然后我们使用accept函数接受客户端连接,并创建一个中继套接字。接下来,我们将客户端的数据转发到中继服务器,并将中继服务器的响应发送回客户端。最后,我们关闭套接字并继续等待下一个客户端连接。
请注意,这只是一个简单的示例,实际的TCP中继器可能需要更多的错误处理和功能。
C++标准库本身并没有提供直接的socks代理服务器功能。但我们可以使用第三方库来实现socks代理服务器。
Socks代理服务器是一种允许客户端通过代理服务器与远程服务器进行通信的协议。它可以用于隐藏客户端的真实IP地址,提供安全性和隐私性。
一个常用的第三方库是libsocks,它是一个用C++编写的开源库,提供了socks代理服务器的功能。下面是一个简单的示例:
#include <iostream>
#include <socks/libsocks.h>
int main() {
// 创建socks代理服务器
socks::Server server;
// 设置代理服务器的监听地址和端口
server.setListenAddress("127.0.0.1", 1080);
// 启动代理服务器
server.start();
// 等待客户端连接
server.waitForClient();
// 接收客户端请求
socks::Request request = server.receiveRequest();
// 处理客户端请求
// ...
// 发送响应给客户端
socks::Response response;
// ...
server.sendResponse(response);
// 关闭代理服务器
server.stop();
return 0;
}
在这个示例中,我们使用libsocks库创建了一个socks代理服务器,并设置了监听地址和端口。然后我们启动代理服务器,并等待客户端连接。一旦有客户端连接,我们可以接收客户端的请求,处理请求并发送响应给客户端。最后,我们关闭代理服务器。
请注意,这只是一个简单的示例,实际的socks代理服务器可能需要更复杂的逻辑来处理客户端的请求和响应。
C++标准库中没有curl的相关功能。但是,你可以使用libcurl库来在C++中进行网络通信。libcurl是一个强大的开源库,提供了各种网络协议的客户端实现,包括HTTP、FTP、SMTP等。
下面是一个使用libcurl库进行HTTP请求的简单示例:
#include <iostream>
#include <curl/curl.h>
int main() {
CURL* curl = curl_easy_init();
if (curl) {
// 设置请求的URL
curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com");
// 设置回调函数处理响应数据
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, [](char* ptr, size_t size, size_t nmemb, std::string* data) {
data->append(ptr, size * nmemb);
return size * nmemb;
});
// 设置回调函数的参数
std::string response;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
// 执行请求
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "Response: " << response << std::endl;
}
// 清理资源
curl_easy_cleanup(curl);
}
return 0;
}
上述代码使用libcurl库发送一个HTTPS请求,并将响应数据保存到字符串中。你可以根据需要设置请求的URL、请求头、请求体等。此外,libcurl还提供了很多其他的选项和功能,你可以根据具体需求进行配置和使用。
C++标准库中没有c-ares DNS的相关功能。c-ares是一个独立的C库,用于非阻塞的DNS解析。
c-ares库提供了异步的DNS解析功能,可以在后台进行DNS解析操作,而不会阻塞主线程。它可以用于实现高性能的网络应用程序,特别是在需要同时进行多个DNS查询的情况下。
以下是一个使用c-ares库进行DNS解析的简单示例:
#include <ares.h>
#include <arpa/inet.h>
int main() {
// 初始化c-ares库
ares_library_init(ARES_LIB_INIT_ALL);
// 创建一个c-ares上下文
ares_channel channel;
ares_init(&channel);
// 设置DNS服务器地址
struct ares_options options;
options.servers = "8.8.8.8"; // 使用Google Public DNS服务器
ares_init_options(&channel, &options, ARES_OPT_SERVERS);
// 发起DNS解析请求
struct ares_addrinfo_hints hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; // IPv4
struct ares_addrinfo* result;
int status = ares_getaddrinfo(channel, "www.example.com", NULL, &hints, &result);
// 处理DNS解析结果
if (status == ARES_SUCCESS) {
struct ares_addrinfo* curr = result;
while (curr != NULL) {
struct sockaddr_in* addr = (struct sockaddr_in*)curr->ai_addr;
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(addr->sin_addr), ip, INET_ADDRSTRLEN);
printf("IP address: %s\n", ip);
curr = curr->ai_next;
}
ares_freeaddrinfo(result);
} else {
printf("DNS resolution failed: %s\n", ares_strerror(status));
}
// 清理c-ares上下文
ares_destroy(channel);
ares_library_cleanup();
return 0;
}
上述示例中,我们使用c-ares库进行DNS解析。首先,我们初始化c-ares库并创建一个c-ares上下文。然后,我们设置DNS服务器地址为Google Public DNS服务器。接下来,我们发起DNS解析请求,并处理解析结果。最后,我们清理c-ares上下文并关闭c-ares库。
请注意,为了使用c-ares库,您需要安装c-ares库及其头文件,并在编译时链接c-ares库。具体的安装和链接方式可能因操作系统和编译环境而异。
C++标准库中没有UDNS(User Datagram Name System)的相关功能。UDNS是一个用于DNS解析的开源库,它提供了非阻塞的DNS解析功能。
如果你想在C++中使用UDNS库,你需要将UDNS库作为一个第三方库引入到你的项目中。以下是一个使用UDNS库的示例:
#include <udns.h>
#include <iostream>
int main() {
const char* hostname = "www.example.com";
struct hostent* host = NULL;
// 初始化UDNS库
dns_init(NULL, NULL);
// 进行DNS解析
int result = dns_query(hostname, NULL, &host);
if (result == DNS_ERR_NONE) {
// 打印解析结果
std::cout << "IP address: " << inet_ntoa(*((struct in_addr*)host->h_addr_list[0])) << std::endl;
} else {
std::cerr << "DNS query failed" << std::endl;
}
// 释放资源
dns_free(host);
dns_shutdown();
return 0;
}
在上述示例中,我们使用了UDNS库的函数来进行DNS解析。首先,我们初始化UDNS库,然后使用dns_query函数来进行DNS解析。如果解析成功,我们可以通过host结构体获取到解析结果,然后打印出IP地址。最后,我们释放资源并关闭UDNS库。
请注意,上述示例仅用于演示UDNS库的基本用法。实际使用时,你可能需要处理更多的错误情况和异常情况。
C++标准库本身并没有提供直接的短址服务功能。但我们可以使用第三方库或者API来实现短址服务。
短址服务是一种将长URL转换为短URL的服务。它可以用于简化长URL,提高用户友好性和分享性。
一个常用的第三方库是Bitly,它提供了API来进行长URL到短URL的转换。以下是一个使用Bitly API的示例:
#include <iostream>
#include <curl/curl.h>
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
size_t totalSize = size * nmemb;
output->append((char*)contents, totalSize);
return totalSize;
}
std::string shortenURL(const std::string& longURL, const std::string& accessToken) {
std::string bitlyURL = "https://api-ssl.bitly.com/v4/shorten";
CURL* curl = curl_easy_init();
std::string response;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, bitlyURL.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ("{\"long_url\": \"" + longURL + "\"}").c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, ("Authorization: Bearer " + accessToken).c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
}
curl_easy_cleanup(curl);
}
return response;
}
int main() {
std::string longURL = "https://www.example.com/very/long/url";
std::string accessToken = "YOUR_BITLY_ACCESS_TOKEN";
std::string shortURL = shortenURL(longURL, accessToken);
std::cout << "Short URL: " << shortURL << std::endl;
return 0;
}
在上述示例中,我们使用了libcurl库来进行HTTP请求,并使用Bitly API进行长URL到短URL的转换。需要注意的是,你需要替换YOUR_BITLY_ACCESS_TOKEN为你自己的Bitly访问令牌。
这样,当你运行上述代码时,它将发送HTTP POST请求到Bitly API,并返回一个包含短URL的JSON响应。
C++标准库本身并不提供直接的“串并转换”连接服务器及其自动化测试的功能。但我们可以使用其他库或自己实现来实现这个功能。
"串并转换"连接服务器是指在测试过程中,将多个并发的连接请求转换为串行的连接请求,以便进行自动化测试。这种测试方法可以确保服务器在高并发情况下的稳定性和性能。
为了实现这个功能,我们可以使用第三方库,如Boost.Asio,它提供了异步网络编程的功能,可以轻松地实现并发连接和自动化测试。
以下是一个简单的示例代码,使用Boost.Asio库来实现"串并转换"连接服务器及其自动化测试的功能:
#include <iostream>
#include <boost/asio.hpp>
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "Connected to server" << std::endl;
}
else
{
std::cout << "Failed to connect to server: " << error.message() << std::endl;
}
}
int main()
{
boost::asio::io_context io_context;
// 创建多个连接请求
boost::asio::ip::tcp::socket socket1(io_context);
boost::asio::ip::tcp::socket socket2(io_context);
boost::asio::ip::tcp::socket socket3(io_context);
// 发起连接请求
socket1.async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8080), handle_connect);
socket2.async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8080), handle_connect);
socket3.async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8080), handle_connect);
// 运行IO上下文,处理连接请求
io_context.run();
return 0;
}
在上面的示例中,我们创建了三个连接请求,并使用async_connect函数异步发起连接请求。每个连接请求都有一个回调函数handle_connect,用于处理连接成功或失败的情况。
通过运行IO上下文io_context,我们可以处理并发的连接请求,实现"串并转换"的效果。
请注意,上述示例只是一个简单的示例,实际应用中可能需要更复杂的逻辑和错误处理。
C++标准库本身并没有提供直接的socks代理服务器功能。但我们可以使用第三方库来实现socks代理服务器。
Socks代理服务器是一种允许客户端通过代理服务器与远程服务器进行通信的协议。它可以用于隐藏客户端的真实IP地址,提供安全性和隐私性。
一个常用的第三方库是libsocks,它是一个用C++编写的开源库,提供了socks代理服务器的功能。下面是一个简单的示例:
#include <iostream>
#include <socks/libsocks.h>
int main() {
// 创建socks代理服务器
socks::Server server;
// 设置代理服务器监听的地址和端口
server.setListenAddress("127.0.0.1", 1080);
// 启动代理服务器
if (!server.start()) {
std::cerr << "Failed to start socks proxy server" << std::endl;
return 1;
}
// 保持服务器运行
while (true) {
// 处理客户端连接
server.handleConnections();
}
return 0;
}
在上面的示例中,我们使用libsocks库创建了一个socks代理服务器,并将其监听在本地地址127.0.0.1的1080端口上。然后,我们通过调用server.start()方法启动代理服务器,并通过循环调用server.handleConnections()方法来处理客户端连接。
请注意,这只是一个简单的示例,实际使用时可能需要更多的配置和错误处理。具体的用法和功能可以参考libsocks库的文档。
C++标准库可以与其他库进行集成,以扩展其功能或实现特定的需求。这种集成可以通过包含其他库的头文件、使用其他库的函数或类、以及与其他库共享数据等方式实现。
举例来说,假设我们需要在C++程序中进行图像处理,我们可以使用C++标准库的iostream库来读取和写入图像文件,但是要进行图像处理的功能可能需要使用其他库,比如OpenCV。我们可以将OpenCV库集成到我们的C++程序中,使用OpenCV提供的函数和类来进行图像处理操作。
以下是一个简单的示例,演示了如何将C++标准库的iostream库与OpenCV库进行集成,实现图像的读取和显示:
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat image = cv::imread("image.jpg"); // 使用OpenCV库读取图像文件
if (image.empty()) {
std::cout << "Failed to open image file" << std::endl;
return -1;
}
cv::imshow("Image", image); // 使用OpenCV库显示图像
cv::waitKey(0); // 等待按键
return 0;
}
在上述示例中,我们使用了C++标准库的iostream库来输出错误消息,使用了OpenCV库的imread函数来读取图像文件,使用了OpenCV库的imshow函数来显示图像。通过这种方式,我们实现了C++标准库与其他库的集成。
猜你喜欢
- 2024-09-15 网络I/O模型(我们所熟知的网络io模型)
- 2024-09-15 C++资深开发工程师带你深入浅出了解Linux后台开发
- 2024-09-15 浅谈linux下C++ 协程与网络编程(linux c++线程同步)
- 2024-09-15 刚学会C++的小白用这个开源框架,做个 RPC 服务要多久?
- 2024-09-15 精选 22 个 C++ 项目,推荐新人练手首选
- 2024-09-15 Node.js 程序员的 C++ 进修指南「1」:SetTimeout
- 2024-09-15 Linux多线程服务端编程 第五章 高效的多线程日志
- 2024-09-15 TCP/IP详解 卷2:实现 第二章 存储器缓存
- 2024-09-15 高性能IO模型分析-Reactor模式和Proactor模式(二)
- 2024-09-15 C++在线五子棋对战(网页版)项目:websocket协议
- 1512℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 556℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 504℃MySQL service启动脚本浅析(r12笔记第59天)
- 482℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 480℃启用MySQL查询缓存(mysql8.0查询缓存)
- 460℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 440℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 438℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)