├── src ├── useless.md ├── logging │ ├── useless.md │ ├── singleton.h │ ├── log_recorder.h │ ├── blocking_queue.h │ ├── logger.h │ ├── logger.cpp │ └── log_recorder.cpp ├── conn_data.h ├── test │ ├── dotest.h │ ├── test_config.h │ ├── test.h │ └── test_client.h ├── persistence.cpp ├── persistence.h ├── recovery.h ├── cmd_recoder.h ├── kv_database.h ├── main.cpp ├── utils.h ├── transaction.h ├── config.h ├── store_client.h ├── kvstore.pro ├── tcp_server.h ├── kv_database.cpp ├── tcp_connection.h ├── config.cpp.autosave ├── cmd_recoder.cpp ├── store_server.h ├── tcp_server.cpp ├── config.cpp ├── recovery.cpp ├── tcp_connection.cpp ├── store_client.cpp ├── cmd_protocol.h ├── store_server.cpp └── kvstore.pro.user └── README.md /src/useless.md: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/logging/useless.md: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [c++]使用boost.asio写的简单键值对缓存 2 | 支持put,del, get三个命令。 3 | 用use切换数据库。 4 | 5 | 支持密码。 6 | 7 | 支持持久化。 8 | 9 | 支持事务。 10 | 11 |

配置文件形式如下

12 |

persistence:on

13 |

password:"mima"

14 | 15 | 16 | -------------------------------------------------------------------------------- /src/conn_data.h: -------------------------------------------------------------------------------- 1 | #ifndef CONN_DATA 2 | #define CONN_DATA 3 | #include 4 | #include "transaction.h" 5 | 6 | namespace kvstore { 7 | 8 | struct ConnData { 9 | ConnData() : db(0), buf(), transaction(), login(false) {} 10 | std::size_t db; 11 | std::string buf; 12 | Transaction transaction; 13 | bool login; 14 | }; 15 | //buf高水位 16 | } 17 | #endif // CONN_DATA 18 | 19 | -------------------------------------------------------------------------------- /src/test/dotest.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test.h" 3 | #include "test/test_config.h" 4 | #include "../store_server.h" 5 | #include "../store_client.h" 6 | #include "test/test_client.h" 7 | using namespace kvstore; 8 | namespace test { 9 | 10 | static void do_test(boost::asio::io_service& ioservice) { 11 | test_config(); 12 | test_client_transaction3(); 13 | //test_client_transaction2(); 14 | //test_client_transaction(); 15 | } 16 | 17 | }//namespace 18 | -------------------------------------------------------------------------------- /src/logging/singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLETON_H 2 | #define SINGLETON_H 3 | namespace logging { 4 | 5 | template 6 | class Singleton { 7 | public: 8 | static T& get_instance() { 9 | static T instance_; 10 | return instance_; 11 | } 12 | 13 | ~Singleton() { } 14 | 15 | private: 16 | Singleton() {} 17 | Singleton(const Singleton &other); 18 | Singleton & operator=(const Singleton &other); 19 | }; 20 | } 21 | #endif // SINGLETON_H 22 | 23 | -------------------------------------------------------------------------------- /src/persistence.cpp: -------------------------------------------------------------------------------- 1 | #include "persistence.h" 2 | 3 | #include "cmd_protocol.h" 4 | namespace kvstore { 5 | Persistence::Persistence(StoreServer& server): 6 | server_(server), 7 | cmdRecoder_() 8 | { 9 | 10 | } 11 | 12 | int Persistence::record(const std::string &cmd, int databaseid) { 13 | return cmdRecoder_.record(cmd, databaseid); 14 | } 15 | 16 | int Persistence::recover() { 17 | Recovery recovery; 18 | return recovery.recover(server_); 19 | } 20 | 21 | 22 | } //namespace 23 | 24 | -------------------------------------------------------------------------------- /src/persistence.h: -------------------------------------------------------------------------------- 1 | #ifndef PERSISTENCE_H 2 | #define PERSISTENCE_H 3 | //#include "store_server.h" 4 | #include "cmd_recoder.h" 5 | #include "recovery.h" 6 | namespace kvstore { 7 | class StoreServer; 8 | 9 | class Persistence 10 | { 11 | public: 12 | Persistence(StoreServer& server); 13 | int record(const std::string& cmd, int databaseid); 14 | int recover(); 15 | 16 | private: 17 | StoreServer& server_; 18 | CmdRecoder cmdRecoder_; 19 | }; 20 | 21 | } //namepsace 22 | 23 | #endif // PERSISTENCE_H 24 | -------------------------------------------------------------------------------- /src/recovery.h: -------------------------------------------------------------------------------- 1 | #ifndef RECOVERY_H 2 | #define RECOVERY_H 3 | #include "kv_database.h" 4 | #include 5 | namespace kvstore { 6 | class StoreServer; 7 | class Recovery { 8 | public: 9 | Recovery(); 10 | int recover(StoreServer &server); 11 | 12 | private: 13 | int execute_cmds(StoreServer &server, std::size_t db,const std::vector& cmds); 14 | int read_file(const std::string& filename, std::vector& cmds); 15 | private: 16 | 17 | }; 18 | 19 | } //namespace 20 | 21 | #endif // RECOVERY_H 22 | -------------------------------------------------------------------------------- /src/cmd_recoder.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDRECODER_H 2 | #define CMDRECODER_H 3 | #include 4 | #include 5 | #include 6 | namespace kvstore { 7 | 8 | class CmdRecoder 9 | { 10 | public: 11 | CmdRecoder(std::size_t dbnum); 12 | CmdRecoder(); 13 | int record(const std::string& cmd, int databaseid); 14 | private: 15 | int init(); 16 | private: 17 | std::size_t dbnum_; 18 | std::vector dbfiles_; 19 | }; 20 | static const char* CMD_SAVE_PATH_FOLDER = "cmd_save/"; 21 | 22 | } 23 | #endif // CMDRECODER_H 24 | -------------------------------------------------------------------------------- /src/logging/log_recorder.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_RECORDER_H 2 | #define LOG_RECORDER_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "blocking_queue.h" 9 | #include "logger.h" 10 | 11 | namespace logging { 12 | class LogRecorder { 13 | public: 14 | LogRecorder(); 15 | void submit(std::string msg); 16 | void stop(); 17 | static Logger::OutputCallback callback(); 18 | 19 | private: 20 | void create_dir_and_files(); 21 | void customer_thread_function(); 22 | void adjust_filename(); 23 | private: 24 | BlockingQueue queue_; 25 | std::atomic_bool quit_; 26 | }; 27 | 28 | } // namespace 29 | #endif // LOG_RECORDER_H 30 | -------------------------------------------------------------------------------- /src/kv_database.h: -------------------------------------------------------------------------------- 1 | #ifndef KVDATABASE_H 2 | #define KVDATABASE_H 3 | 4 | #include 5 | //#include 6 | #include 7 | #include 8 | 9 | namespace kvstore { 10 | class KVDatabase { 11 | typedef std::unordered_map StringMap; 12 | public: 13 | KVDatabase(); 14 | KVDatabase(std::size_t num); 15 | 16 | std::size_t size() const; 17 | 18 | int clear(std::size_t id); 19 | int put(std::size_t id, std::string key, std::string value); 20 | int get(std::size_t id, std::string key, std::string& value) const; 21 | int del(std::size_t id, std::string key); 22 | 23 | private: 24 | std::vector maps_; 25 | }; 26 | 27 | 28 | } //namespace 29 | 30 | #endif // KVDATABASE_H 31 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "store_server.h" 3 | #include "test/dotest.h" 4 | using namespace std; 5 | using namespace kvstore; 6 | 7 | 8 | int main() 9 | { 10 | //设置log输出到控制台,并且保存到文件 11 | logging::Logger::set_output(logging::LogRecorder::callback()); 12 | //logging::Logger::set_level(logging::Logger::eINFO); 13 | logging::Logger::set_level(logging::Logger::eDEBUG); 14 | //logging::Logger::set_no_display_function(true); 15 | boost::asio::io_service ioservice; 16 | std::size_t port = 23333; 17 | StoreServer server(ioservice, port); 18 | server.init(); 19 | //做测试入口 20 | test::do_test(ioservice); 21 | ioservice.run(); 22 | cout << "Hello World!" << endl; 23 | return 0; 24 | } 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | #include 4 | #include 5 | #include 6 | namespace utils{ 7 | 8 | template 9 | static std::string to_string(const T& value) { 10 | std::stringstream ss; 11 | ss << value; 12 | std::string str; 13 | ss >> str; 14 | return str; 15 | } 16 | 17 | static std::string& trim(std::string &s) { 18 | if (s.empty()) { 19 | return s; 20 | } 21 | s.erase(0,s.find_first_not_of(" ")); 22 | s.erase(s.find_last_not_of(" ") + 1); 23 | return s; 24 | } 25 | 26 | static std::string& trim_quo(std::string &s) { 27 | if (s.empty()) { 28 | return s; 29 | } 30 | s.erase(0,s.find_first_not_of("\"")); 31 | s.erase(s.find_last_not_of("\"") + 1); 32 | return s; 33 | } 34 | 35 | 36 | } 37 | #endif // UTILS 38 | 39 | -------------------------------------------------------------------------------- /src/transaction.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSACTION 2 | #define TRANSACTION 3 | 4 | #include 5 | #include 6 | #include 7 | namespace kvstore { 8 | 9 | class Transaction { 10 | public: 11 | Transaction() : cmds_(), watchs_(), mult_flag_(false) {} 12 | bool mult() const { return mult_flag_; } 13 | void set_mult(bool flag) { mult_flag_ = flag; } 14 | void add_cmd(const std::string& cmd) { cmds_.push_back(cmd); } 15 | void add_watch(const std::string& key, const std::string& value) { watchs_[key] = value; } 16 | void clear_cmd() { cmds_.clear(); } 17 | void clear_watch() { watchs_.clear(); } 18 | std::vector& cmds() { return cmds_; } 19 | std::map & watchs() { return watchs_; } 20 | private: 21 | std::vector cmds_; 22 | std::map watchs_; 23 | bool mult_flag_; 24 | }; 25 | 26 | } // namespace 27 | #endif // TRANSACTION 28 | 29 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | #include 4 | namespace kvstore { 5 | 6 | struct ConfigInfo { 7 | ConfigInfo() : need_password(false), persistence(false), password("") {} 8 | bool need_password; 9 | bool persistence; 10 | std::string password; 11 | }; 12 | 13 | class Config { 14 | public: 15 | Config(); 16 | Config(const std::string& path); 17 | 18 | bool need_persistence() const { return info_.persistence; } 19 | bool need_password() const { return info_.need_password; } 20 | std::string password() const { return info_.password; } 21 | 22 | private: 23 | void init(const std::string& path); 24 | int parse(const std::string& path); 25 | void process_password(const std::string& value); 26 | void process_persistence(const std::string& value); 27 | private: 28 | ConfigInfo info_; 29 | }; 30 | //方便测试,把函数声明到头文件 31 | int parse_line(const std::string &line, std::string &key, std::string &value); 32 | 33 | 34 | } //namespace 35 | #endif // CONFIG_H 36 | -------------------------------------------------------------------------------- /src/store_client.h: -------------------------------------------------------------------------------- 1 | #ifndef STORECLIENT_H 2 | #define STORECLIENT_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tcp_connection.h" 10 | #include "logging/logger.h" 11 | #include "logging/log_recorder.h" 12 | namespace kvstore { 13 | 14 | class StoreClient { 15 | public: 16 | typedef std::function ConnectCallback; 17 | typedef std::pair Result; 18 | public: 19 | StoreClient(boost::asio::io_service& ioservice); 20 | void connect(const std::string& addr, const std::string& port); 21 | 22 | Result use(std::size_t db); 23 | Result put(const std::string& key, const std::string& value); 24 | Result get(const std::string& key); 25 | Result del(const std::string& key); 26 | Result login(const std::string& password); 27 | Result mult(); 28 | Result exec(); 29 | Result discard(); 30 | Result watch(const std::string& key); 31 | private: 32 | boost::asio::io_service& ioservice_; 33 | boost::asio::ip::tcp::socket socket_; 34 | }; 35 | 36 | }//namespace 37 | 38 | #endif // STORECLIENT_H 39 | -------------------------------------------------------------------------------- /src/logging/blocking_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCKING_QUEUE_H 2 | #define BLOCKING_QUEUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace logging { 9 | 10 | template 11 | class BlockingQueue { 12 | public: 13 | BlockingQueue() : queue_(), mutex_(), cond_() {} 14 | 15 | void push(T new_value) { 16 | std::lock_guard lock(mutex_); 17 | queue_.push(std::move(new_value)); 18 | cond_.notify_one(); 19 | } 20 | 21 | bool empty() const { 22 | std::lock_guard lock(mutex_); 23 | return queue_.empty(); 24 | } 25 | 26 | T pop() { 27 | std::unique_lock lock(mutex_); 28 | cond_.wait(lock, [this]{ return !queue_.empty(); }); 29 | T value = std::move(queue_.front()); 30 | queue_.pop(); 31 | return value; 32 | } 33 | 34 | private: 35 | BlockingQueue(const BlockingQueue& other) = delete; 36 | BlockingQueue& operator=(const BlockingQueue& other) = delete; 37 | BlockingQueue(BlockingQueue&& other) = delete; 38 | 39 | 40 | private: 41 | std::queue queue_; 42 | mutable std::mutex mutex_; 43 | std::condition_variable cond_; 44 | }; 45 | 46 | 47 | 48 | } //namespace 49 | 50 | #endif // BLOCKING_QUEUE_H 51 | 52 | -------------------------------------------------------------------------------- /src/kvstore.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console c++11 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | QMAKE_CXXFLAGS += -std=c++11 7 | QMAKE_CXXFLAGS += -Werror 8 | QMAKE_CXXFLAGS += -Wno-unused-parameter 9 | QMAKE_CXXFLAGS += -Wno-unused-function 10 | QMAKE_CXXFLAGS += -Wno-unused-variable 11 | 12 | 13 | 14 | LIBS += -lboost_coroutine 15 | LIBS += -lboost_context 16 | LIBS += -lboost_filesystem 17 | LIBS += -lboost_thread 18 | LIBS += -lboost_system 19 | LIBS += -lpthread 20 | 21 | 22 | SOURCES += main.cpp \ 23 | logging/log_recorder.cpp \ 24 | logging/logger.cpp \ 25 | tcp_connection.cpp \ 26 | tcp_server.cpp \ 27 | store_server.cpp \ 28 | kv_database.cpp \ 29 | store_client.cpp \ 30 | cmd_recoder.cpp \ 31 | persistence.cpp \ 32 | recovery.cpp \ 33 | config.cpp 34 | 35 | HEADERS += \ 36 | logging/blocking_queue.h \ 37 | logging/log_recorder.h \ 38 | logging/logger.h \ 39 | logging/singleton.h \ 40 | tcp_connection.h \ 41 | tcp_server.h \ 42 | store_server.h \ 43 | kv_database.h \ 44 | test/test.h \ 45 | test/dotest.h \ 46 | store_client.h \ 47 | cmd_recoder.h \ 48 | persistence.h \ 49 | utils.h \ 50 | recovery.h \ 51 | cmd_protocol.h \ 52 | conn_data.h \ 53 | config.h \ 54 | test/test_config.h \ 55 | transaction.h \ 56 | test/test_client.h 57 | 58 | -------------------------------------------------------------------------------- /src/tcp_server.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSERVER_H 2 | #define TCPSERVER_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tcp_connection.h" 10 | #include "logging/logger.h" 11 | #include "logging/log_recorder.h" 12 | namespace kvstore { 13 | 14 | class TcpServer //nocopyable 15 | { 16 | public: 17 | typedef std::function ConnectionCallback; 18 | 19 | public: 20 | TcpServer(io_service& ioservice, std::size_t port); 21 | void set_close_callback(TcpConnection::CloseCallback cb); 22 | void set_read_callback(TcpConnection::ReadCallback cb); 23 | void set_new_conn_callback(ConnectionCallback cb); 24 | private: 25 | void start_accept(); 26 | void handle_accept(TcpConnection::Pointer conn, const boost::system::error_code &ec); 27 | void handle_close(TcpConnection::Pointer conn); 28 | void handle_read(TcpConnection::Pointer conn, std::size_t bytes_transfered); 29 | 30 | private: 31 | TcpServer(const TcpServer&) = delete; 32 | TcpServer(TcpServer&&) = delete; 33 | TcpServer& operator=(const TcpServer&) = delete; 34 | 35 | private: 36 | boost::asio::ip::tcp::acceptor acceptor_; 37 | std::set conns_; 38 | TcpConnection::CloseCallback close_callback_; 39 | TcpConnection::ReadCallback read_callback_; 40 | ConnectionCallback new_conn_callback_; 41 | }; 42 | 43 | 44 | } // namespace 45 | 46 | 47 | 48 | #endif // TCPSERVER_H 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/kv_database.cpp: -------------------------------------------------------------------------------- 1 | #include "kv_database.h" 2 | #include 3 | #include "logging/logger.h" 4 | namespace kvstore { 5 | 6 | //const int DEFAULT_DB_ID = 0; 7 | const int DEFAULT_DB_NUM = 10; 8 | 9 | KVDatabase::KVDatabase() : 10 | maps_(DEFAULT_DB_NUM) 11 | { 12 | } 13 | 14 | KVDatabase::KVDatabase(std::size_t num) : 15 | maps_(num) 16 | { 17 | 18 | } 19 | 20 | std::size_t KVDatabase::size() const { 21 | return maps_.size(); 22 | } 23 | 24 | int KVDatabase::clear(std::size_t id) { 25 | if (id >= size()) { 26 | LOG_DEBUG<<"err"; 27 | return -1; 28 | } 29 | maps_[id].clear(); 30 | return 0; 31 | } 32 | 33 | int KVDatabase::put(std::size_t id, std::string key, std::string value) { 34 | if (id >= size()) { 35 | LOG_DEBUG<<"err"; 36 | return -1; 37 | } 38 | maps_[id][key] = value; 39 | return 0; 40 | } 41 | 42 | int KVDatabase::get(std::size_t id, std::string key, std::string &value) const { 43 | if (id >= size()) { 44 | LOG_DEBUG<<"err"; 45 | return -2; 46 | } 47 | auto iter = maps_[id].find(key); 48 | if (iter == maps_[id].end()) { 49 | LOG_DEBUG<<"err"; 50 | return -1; 51 | } 52 | value = iter->second; 53 | return 0; 54 | } 55 | 56 | int KVDatabase::del(std::size_t id, std::string key) { 57 | if (id >= size()) { 58 | LOG_DEBUG<<"err"; 59 | return -2; 60 | } 61 | auto iter = maps_[id].find(key); 62 | if (iter == maps_[id].end()) { 63 | LOG_DEBUG<<"err"; 64 | return -1; 65 | } 66 | maps_[id].erase(iter); 67 | return 0; 68 | } 69 | 70 | }//namespace 71 | 72 | -------------------------------------------------------------------------------- /src/tcp_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_CONNECTION_H 2 | #define TCP_CONNECTION_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace kvstore { 11 | 12 | using namespace boost::asio::ip; 13 | using boost::asio::io_service; 14 | 15 | class TcpConnection : public std::enable_shared_from_this 16 | { 17 | public: 18 | typedef std::shared_ptr Pointer; 19 | typedef std::weak_ptr WeakPointer; 20 | typedef boost::array ConnBuffer; 21 | typedef std::function CloseCallback; 22 | typedef std::function ReadCallback; 23 | public: 24 | explicit TcpConnection(io_service& io_service); 25 | ~TcpConnection(); 26 | ConnBuffer& buf(); 27 | void start(); 28 | static Pointer create(boost::asio::io_service& io_service); 29 | tcp::socket& socket(); 30 | void set_close_callback(const CloseCallback& cb); 31 | void set_read_callback(const ReadCallback& cb); 32 | void send(const std::string& msg); 33 | void send(const std::vector& msg); 34 | void shutdown(); 35 | bool connecting() const; 36 | 37 | private: 38 | void handle_write(const boost::system::error_code &ec, std::size_t bytes_transfered); 39 | void handle_read(const boost::system::error_code &ec, std::size_t bytes_transfered); 40 | void handle_close(); 41 | 42 | private: 43 | ConnBuffer readbuf_; 44 | std::string buf_; 45 | tcp::socket socket_; 46 | bool is_connecting_; 47 | CloseCallback close_callback_; 48 | ReadCallback read_callback_; 49 | }; 50 | 51 | } // namespace 52 | #endif // TCP_CONNECTION_H 53 | -------------------------------------------------------------------------------- /src/logging/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H 2 | #define LOGGER_H 3 | #include 4 | #include 5 | namespace logging { 6 | 7 | class Logger { 8 | public: 9 | //用来设置不同的日志级别,从muduo拷贝过来的 10 | enum LogLevel { 11 | eTRACE, 12 | eDEBUG, 13 | eINFO, 14 | eWARN, 15 | eERROR, 16 | eFATAL, 17 | eNUM_LOG_LEVELS,//级别个数 18 | }; 19 | typedef std::function OutputCallback; 20 | public: 21 | Logger(const char* filename, int line, const char* func_name, LogLevel level); 22 | ~Logger(); 23 | 24 | std::stringstream& stream(); 25 | 26 | static LogLevel log_level(); 27 | static void set_level(LogLevel level); 28 | static void set_output(OutputCallback cb); 29 | 30 | private: 31 | std::stringstream sstream_; 32 | 33 | private: 34 | static LogLevel level_limit_; 35 | static OutputCallback output_callback_; 36 | }; 37 | 38 | 39 | #define LOG_TRACE if (logging::Logger::log_level() <= logging::Logger::eTRACE) \ 40 | logging::Logger(__FILE__, __LINE__, __func__,logging::Logger::eTRACE).stream() 41 | 42 | #define LOG_DEBUG if (logging::Logger::log_level() <= logging::Logger::eDEBUG) \ 43 | logging::Logger(__FILE__, __LINE__, __func__, logging::Logger::eDEBUG).stream() 44 | 45 | #define LOG_INFO if (logging::Logger::log_level() <= logging::Logger::eINFO) \ 46 | logging::Logger(__FILE__, __LINE__, __func__, logging::Logger::eINFO).stream() 47 | 48 | #define LOG_WARN logging::Logger(__FILE__, __LINE__, __func__, logging::Logger::eWARN).stream() 49 | 50 | #define LOG_ERROR logging::Logger(__FILE__, __LINE__, __func__, logging::Logger::eERROR).stream() 51 | 52 | #define LOG_FATAL logging::Logger(__FILE__, __LINE__, __func__, logging::Logger::eFATAL).stream() 53 | 54 | 55 | 56 | } // namespace 57 | #endif // LOGGER_H 58 | -------------------------------------------------------------------------------- /src/logging/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include "singleton.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace logging { 11 | 12 | Logger::LogLevel DEFAULT_LEVEL = Logger::eTRACE; 13 | 14 | typename Logger::LogLevel Logger::level_limit_ = DEFAULT_LEVEL; 15 | 16 | typename Logger::OutputCallback Logger::output_callback_ = [](std::string msg){ 17 | std::cout< 3 | #include 4 | #include "logging/logger.h" 5 | #include "utils.h" 6 | namespace kvstore { 7 | 8 | static const char* DEFALUT_CONFIG_PATH = "config/config"; 9 | 10 | 11 | Config::Config() { 12 | init(DEFALUT_CONFIG_PATH); 13 | } 14 | 15 | Config::Config(std::string path) { 16 | init(path); 17 | } 18 | 19 | void Config::init(std::string path) { 20 | int err = parse(path); 21 | } 22 | 23 | int Config::parse(std::string path) { 24 | std::ifstream ifs(path); 25 | if (!ifs) { 26 | LOG_DEBUG<<"file error."; 27 | return -1; 28 | } 29 | std::map configs; 30 | std::string line; 31 | std::string key; 32 | std::string value; 33 | while (std::getline(ifs, line)) { 34 | parse_line(line, key, value); 35 | configs[key] = value; 36 | } 37 | 38 | std::vector cmds { 39 | "password", "persistence" 40 | }; 41 | 42 | for (auto &cmd : cmds) { 43 | auto iter = configs.find(cmd); 44 | if (iter) 45 | } 46 | return 0; 47 | } 48 | 49 | 50 | 51 | int parse_line(const std::string &line, std::string &key, std::string &value) { 52 | std::size_t pos = 0; 53 | pos = line.find(":"); 54 | if (pos == std::string::npos) { 55 | LOG_DEBUG<<"config parse error"; 56 | return -1; 57 | } 58 | key = line.substr(0, pos); 59 | utils::trim(key); 60 | ++pos; 61 | if (pos >= line.length()) { 62 | LOG_DEBUG<<"config parse error: no value"; 63 | return -2; 64 | } 65 | value = line.substr(pos, line.length() - pos); 66 | utils::trim(value); 67 | utils::trim_quo(value); 68 | if (value.empty()) { 69 | LOG_DEBUG<<"config parse error: no value"; 70 | return -3; 71 | } 72 | return 0; 73 | } 74 | } //namespace 75 | -------------------------------------------------------------------------------- /src/cmd_recoder.cpp: -------------------------------------------------------------------------------- 1 | #include "cmd_recoder.h" 2 | #include 3 | #include //for mkdir 4 | #include 5 | #include 6 | #include //rename 7 | #include 8 | #include "utils.h" 9 | #include "cmd_protocol.h" 10 | namespace kvstore { 11 | 12 | 13 | const int DEFAULT_DB_NUM = 10; 14 | 15 | CmdRecoder::CmdRecoder(std::size_t dbnum) : 16 | dbnum_(dbnum), 17 | dbfiles_(dbnum) 18 | { 19 | init(); 20 | } 21 | 22 | CmdRecoder::CmdRecoder() : 23 | dbnum_(DEFAULT_DB_NUM), 24 | dbfiles_(DEFAULT_DB_NUM) 25 | { 26 | init(); 27 | } 28 | 29 | int CmdRecoder::record(const std::string &cmd, int databaseid) { 30 | int result = -1; 31 | assert(static_cast(databaseid) (databaseid) >= dbnum_) { 33 | return result; 34 | } 35 | int req_type = static_cast(cmd[POS_TYPE]); 36 | switch (req_type) { 37 | case REQ_TYPE_PUT : 38 | case REQ_TYPE_DEL: 39 | dbfiles_[databaseid]<(dbnum_); ++i) { 61 | filename = CMD_SAVE_PATH_FOLDER; 62 | filename = filename + "/db" + utils::to_string(i) + ".kvdb"; 63 | dbfiles_[i].open(filename, std::ios::app | std::ios::binary); 64 | if (!dbfiles_[i]) { 65 | LOG_DEBUG<<"第"< 4 | #include 5 | #include "tcp_server.h" 6 | #include "cmd_recoder.h" 7 | #include "persistence.h" 8 | #include "conn_data.h" 9 | #include "config.h" 10 | namespace kvstore { 11 | 12 | class StoreServer { 13 | friend class Recovery; 14 | public: 15 | StoreServer(io_service& ioservice, std::size_t port); 16 | public: 17 | int execute_db_cmd(const std::string& cmd, std::size_t db, std::string& retmsg); 18 | void init(); 19 | private: 20 | void handle_close(TcpConnection::Pointer conn); 21 | void handle_read(TcpConnection::Pointer conn, std::size_t bytes_transfered); 22 | void handle_new_conn(TcpConnection::Pointer conn); 23 | int dispath_cmd(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 24 | int execute_cmd_put(const std::string& cmd, std::size_t db, std::string& retmsg); 25 | int execute_cmd_get(const std::string& cmd, std::size_t db, std::string& retmsg); 26 | int execute_cmd_del(const std::string& cmd, std::size_t db, std::string& retmsg); 27 | int execute_cmd_use(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 28 | int execute_cmd_login(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 29 | int execute_cmd_mult(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 30 | int execute_cmd_exec(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 31 | int add_transaction(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 32 | int execute_cmd_discard(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 33 | int execute_cmd_watch(const std::string& cmd, TcpConnection::Pointer conn,std::string& retmsg); 34 | KVDatabase& db(); 35 | private: 36 | 37 | private: 38 | TcpServer tcp_server_; 39 | std::map conns_; 40 | KVDatabase dbs_; 41 | Config config_; 42 | Persistence persistence_; 43 | }; 44 | 45 | }//namespace 46 | #endif // STORESERVER_H 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/tcp_server.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp_server.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "logging/logger.h" 7 | 8 | namespace kvstore { 9 | 10 | TcpServer::TcpServer(io_service &ioservice, size_t port) : 11 | acceptor_(ioservice, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), 12 | conns_() 13 | { 14 | start_accept(); 15 | } 16 | 17 | 18 | void TcpServer::start_accept() { 19 | TcpConnection::Pointer conn = TcpConnection::create(acceptor_.get_io_service()); 20 | acceptor_.async_accept(conn->socket(), std::bind( 21 | &TcpServer::handle_accept, this, conn, std::placeholders::_1)); 22 | } 23 | 24 | void TcpServer::handle_accept(TcpConnection::Pointer conn, const boost::system::error_code &ec) { 25 | if (!ec) { 26 | conns_.insert(conn); 27 | conn->set_close_callback( 28 | std::bind(&TcpServer::handle_close, this, std::placeholders::_1)); 29 | conn->set_read_callback( 30 | std::bind(&TcpServer::handle_read, this, std::placeholders::_1, std::placeholders::_2)); 31 | conn->start(); 32 | if (new_conn_callback_) { 33 | new_conn_callback_(conn); 34 | } 35 | } else { 36 | LOG_ERROR< 3 | #include 4 | #include "logging/logger.h" 5 | #include "utils.h" 6 | namespace kvstore { 7 | 8 | static const char* DEFALUT_CONFIG_PATH = "config/config"; 9 | 10 | 11 | Config::Config() : info_() { 12 | init(DEFALUT_CONFIG_PATH); 13 | } 14 | 15 | Config::Config(const std::string& path) : info_() { 16 | init(path); 17 | } 18 | 19 | void Config::init(const std::string& path) { 20 | int err = parse(path); 21 | } 22 | 23 | int Config::parse(const std::string& path) { 24 | std::ifstream ifs(path); 25 | if (!ifs) { 26 | LOG_DEBUG<<"file error."; 27 | return -1; 28 | } 29 | std::map configs; 30 | std::string line; 31 | std::string key; 32 | std::string value; 33 | while (std::getline(ifs, line)) { 34 | parse_line(line, key, value); 35 | configs[key] = value; 36 | } 37 | 38 | for (auto &pair : configs) { 39 | if (pair.first == "password") { 40 | LOG_INFO<<"启用密码保护,密码为" <= line.length()) { 75 | LOG_DEBUG<<"config parse error: no value"; 76 | return -2; 77 | } 78 | value = line.substr(pos, line.length() - pos); 79 | utils::trim(value); 80 | utils::trim_quo(value); 81 | if (value.empty()) { 82 | LOG_DEBUG<<"config parse error: no value"; 83 | return -3; 84 | } 85 | return 0; 86 | } 87 | } //namespace 88 | -------------------------------------------------------------------------------- /src/recovery.cpp: -------------------------------------------------------------------------------- 1 | #include "recovery.h" 2 | #include 3 | #include 4 | #include 5 | #include "utils.h" 6 | #include "cmd_protocol.h" 7 | #include "logging/logger.h" 8 | #include "store_server.h" 9 | namespace kvstore { 10 | Recovery::Recovery() 11 | { 12 | } 13 | 14 | int Recovery::recover(StoreServer &server) { 15 | std::vector cmds; 16 | std::string filename = CMD_SAVE_PATH_FOLDER; 17 | int err = 0; 18 | for (std::size_t i = 0; i < server.db().size(); ++i) { 19 | filename = CMD_SAVE_PATH_FOLDER; 20 | filename = filename + "/db" + utils::to_string(i) + ".kvdb"; 21 | cmds.clear(); 22 | err = read_file(filename, cmds); 23 | if (err < 0) { 24 | LOG_DEBUG<<"read_file error,filename:" << filename; 25 | continue; 26 | } 27 | //执行命令 28 | err = execute_cmds(server, i, cmds); 29 | if (err < 0) { 30 | //命令有一条执行失败,就清空那一整个数据库 31 | LOG_DEBUG<<"cmd syntax error"; 32 | server.db().clear(i); 33 | continue; 34 | } 35 | } 36 | return 0; 37 | } 38 | 39 | int Recovery::execute_cmds(StoreServer &server, std::size_t db,const std::vector &cmds) { 40 | std::string msg; 41 | int err; 42 | for (std::size_t i = 0; i < cmds.size(); ++i) { 43 | err = server.execute_db_cmd(cmds[i], db, msg); 44 | if (err < 0) { 45 | LOG_ERROR<<"第"<& cmds) { 53 | std::ifstream ifs; 54 | std::string line; 55 | std::string cmdbuf; 56 | ifs.open(filename, std::ios::binary); 57 | if (!ifs) { 58 | LOG_DEBUG<<"file error"; 59 | return -2; 60 | } 61 | while (!cmdbuf.empty() || std::getline(ifs, line)) { 62 | cmdbuf.append(line); 63 | line = ""; 64 | if (cmdbuf.length() <= 3) { 65 | if (!ifs) { 66 | LOG_DEBUG<<"cmd syntax error"; 67 | return -1; 68 | } 69 | continue; 70 | } 71 | unsigned short len = *((unsigned short*) (cmdbuf.data() + POS_TOTAL_LENGTH)); 72 | len = boost::asio::detail::socket_ops::network_to_host_short(len); 73 | if (cmdbuf.length() < len) { 74 | if (!ifs) { 75 | LOG_DEBUG<<"cmd syntax error"; 76 | return -1; 77 | } 78 | continue; 79 | } 80 | cmds.push_back( cmdbuf.substr(0, len)); 81 | cmdbuf.erase(0, len); 82 | } 83 | return 0; 84 | } 85 | 86 | 87 | } //namespace 88 | -------------------------------------------------------------------------------- /src/tcp_connection.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp_connection.h" 2 | 3 | #include 4 | 5 | #include "logging/logger.h" 6 | namespace kvstore { 7 | 8 | TcpConnection::TcpConnection(io_service &io_service) : 9 | readbuf_(), 10 | buf_(), 11 | socket_(io_service), 12 | is_connecting_(false) 13 | { 14 | } 15 | 16 | TcpConnection::~TcpConnection() { 17 | LOG_DEBUG<<"~TcpConnection"; 18 | } 19 | 20 | TcpConnection::ConnBuffer &TcpConnection::buf() { 21 | return readbuf_; 22 | } 23 | 24 | void TcpConnection::start() { 25 | is_connecting_ = true; 26 | socket_.async_read_some(boost::asio::buffer(readbuf_), std::bind( 27 | &TcpConnection::handle_read, this, std::placeholders::_1, std::placeholders::_2)); 28 | } 29 | 30 | TcpConnection::Pointer TcpConnection::create(io_service &io_service) { 31 | return std::make_shared(io_service); 32 | } 33 | 34 | tcp::socket &TcpConnection::socket() { 35 | return socket_; 36 | } 37 | 38 | void TcpConnection::set_close_callback(const TcpConnection::CloseCallback &cb) { 39 | close_callback_ = cb; 40 | } 41 | 42 | void TcpConnection::set_read_callback(const TcpConnection::ReadCallback &cb) { 43 | read_callback_ = cb; 44 | } 45 | 46 | void TcpConnection::send(const std::string &msg) { 47 | boost::asio::async_write(socket_, boost::asio::buffer(msg), std::bind( 48 | &TcpConnection::handle_write, this, std::placeholders::_1, std::placeholders::_2)); 49 | } 50 | 51 | void TcpConnection::send(const std::vector &msg) { 52 | boost::asio::async_write(socket_, boost::asio::buffer(msg), std::bind( 53 | &TcpConnection::handle_write, this, std::placeholders::_1, std::placeholders::_2)); 54 | } 55 | 56 | void TcpConnection::shutdown() { 57 | handle_close(); 58 | } 59 | 60 | bool TcpConnection::connecting() const { 61 | return is_connecting_; 62 | } 63 | 64 | void TcpConnection::handle_write(const boost::system::error_code &ec, std::size_t bytes_transfered) 65 | {} 66 | 67 | void TcpConnection::handle_read(const boost::system::error_code &ec, std::size_t bytes_transfered) { 68 | if (!ec) { 69 | if(read_callback_) { 70 | read_callback_(shared_from_this(), bytes_transfered); 71 | } 72 | socket_.async_read_some(boost::asio::buffer(readbuf_), std::bind( 73 | &TcpConnection::handle_read, this, std::placeholders::_1, std::placeholders::_2)); 74 | } else { 75 | 76 | if (ec.value() == boost::asio::error::eof) {// EOF 77 | handle_close(); 78 | } else { 79 | LOG_DEBUG< buffer(3); 23 | size_t reply_length = boost::asio::read(socket,boost::asio::buffer(buffer)); 24 | std::string response(buffer.data(), reply_length); 25 | unsigned short s = *((unsigned short*) (response.data() + POS_TOTAL_LENGTH)); 26 | s = boost::asio::detail::socket_ops::network_to_host_short(s); 27 | if (s <= 2) { 28 | return std::make_pair(-1,""); 29 | } 30 | if (s == 1 + 2) { 31 | return std::make_pair(response[POS_TYPE],""); 32 | } 33 | std::vector buffer2(s - 1 - 2); 34 | reply_length = boost::asio::read(socket,boost::asio::buffer(buffer2)); 35 | std::string retmsg{buffer2.data(), reply_length}; 36 | return std::make_pair(response[POS_TYPE], std::move(retmsg)); 37 | } 38 | 39 | StoreClient::Result StoreClient::use(std::size_t db) { 40 | std::string msg = get_use_request(db); 41 | boost::asio::write(socket_, boost::asio::buffer(msg)); 42 | return recv_response(socket_); 43 | } 44 | 45 | StoreClient::Result StoreClient::put(const std::string& key, const std::string& value) { 46 | std::string msg = get_put_request(std::move(key), std::move(value)); 47 | boost::asio::write(socket_, boost::asio::buffer(msg)); 48 | return recv_response(socket_); 49 | } 50 | 51 | StoreClient::Result StoreClient::get(const std::string& key) { 52 | std::string msg = get_get_request(key); 53 | boost::asio::write(socket_, boost::asio::buffer(msg)); 54 | return recv_response(socket_); 55 | } 56 | 57 | StoreClient::Result StoreClient::del(const std::string& key) { 58 | std::string msg = get_del_request(key); 59 | boost::asio::write(socket_, boost::asio::buffer(msg)); 60 | return recv_response(socket_); 61 | } 62 | 63 | StoreClient::Result StoreClient::login(const std::string& password) { 64 | std::string msg = get_login_request(password); 65 | boost::asio::write(socket_, boost::asio::buffer(msg)); 66 | return recv_response(socket_); 67 | } 68 | 69 | StoreClient::Result StoreClient::mult() { 70 | std::string msg = get_mult_request(); 71 | boost::asio::write(socket_, boost::asio::buffer(msg)); 72 | return recv_response(socket_); 73 | } 74 | 75 | StoreClient::Result StoreClient::exec() { 76 | std::string msg = get_exec_request(); 77 | boost::asio::write(socket_, boost::asio::buffer(msg)); 78 | return recv_response(socket_); 79 | } 80 | 81 | StoreClient::Result StoreClient::discard() { 82 | std::string msg = get_discard_request(); 83 | boost::asio::write(socket_, boost::asio::buffer(msg)); 84 | return recv_response(socket_); 85 | } 86 | 87 | StoreClient::Result StoreClient::watch(const std::string& key) 88 | { 89 | auto msg = get_watch_request(key); 90 | boost::asio::write(socket_, boost::asio::buffer(msg)); 91 | return recv_response(socket_); 92 | } 93 | 94 | 95 | }//namespace 96 | -------------------------------------------------------------------------------- /src/logging/log_recorder.cpp: -------------------------------------------------------------------------------- 1 | #include "log_recorder.h" 2 | #include 3 | #include 4 | #include 5 | #include //for mkdir 6 | #include 7 | #include 8 | #include //rename 9 | #include 10 | #include 11 | 12 | #include "singleton.h" 13 | namespace logging{ 14 | 15 | const char* LOG_PATH_FOLDER = "log"; 16 | const int MAX_LOG_FILES_NUM = 10; 17 | const int MAX_LOG_FILE_SIZE = 1024 * 10; 18 | 19 | LogRecorder::LogRecorder(): 20 | queue_(), 21 | quit_(false) 22 | { 23 | create_dir_and_files(); 24 | std::thread t(std::bind(&LogRecorder::customer_thread_function, this)); 25 | t.detach(); 26 | } 27 | 28 | void LogRecorder::submit(std::string msg) { 29 | queue_.push(msg); 30 | } 31 | 32 | Logger::OutputCallback LogRecorder::callback() { 33 | return [](std::string msg) { 34 | Singleton::get_instance().submit(msg); 35 | }; 36 | } 37 | 38 | template 39 | int to_int(const T& value) { 40 | std::stringstream ss; 41 | ss << value; 42 | int i = 0; 43 | ss >> i; 44 | return i; 45 | } 46 | 47 | template 48 | std::string to_string(const T& value) { 49 | std::stringstream ss; 50 | ss << value; 51 | std::string str; 52 | ss >> str; 53 | return str; 54 | } 55 | 56 | int get_filesize(const std::string& path) { 57 | struct stat buf; 58 | if (stat(path.c_str(), &buf) < 0) { 59 | //printf("Mesg: %s\n", strerror(errno)); 60 | return -1; 61 | } 62 | // printf("%s's size is %-4d bytes\n", path.c_str(), buf.st_size); 63 | // printf("%s's t_blksize is %-4d bytes\n", path.c_str(), buf.st_blksize); 64 | // printf("%s's blocks is %-4d blocks\n", path.c_str(), buf.st_blocks); 65 | return buf.st_size; 66 | } 67 | 68 | void LogRecorder::create_dir_and_files() { 69 | int isCreate = mkdir(LOG_PATH_FOLDER , 70 | S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO); 71 | if (isCreate != 0) { 72 | //std::cout<<"创建文件夹失败"<= MAX_LOG_FILE_SIZE) { 95 | adjust_filename(); 96 | } 97 | std::ofstream ofs(filename, std::ios::app); 98 | if (ofs) { 99 | std::cout< filenames; 113 | filenames.reserve(MAX_LOG_FILES_NUM); 114 | for (int i = 0; i < MAX_LOG_FILES_NUM; ++i) { 115 | filename = LOG_PATH_FOLDER + std::string("/log") + to_string(i) + ".txt"; 116 | filenames.push_back(filename); 117 | } 118 | 119 | if (remove(filename.c_str()) != 0) { 120 | // std::cout<<"删除文件失败:"<= 0; --i) { 124 | if (rename(filenames[i].c_str(), filenames[i + 1].c_str()) != 0) { //oldname, newname 125 | //std::cout<<"修改文件名失败:"< 4 | #include 5 | namespace test { 6 | 7 | class SimpleDebugPrinter { 8 | public: 9 | SimpleDebugPrinter() :ss_() { } 10 | ~SimpleDebugPrinter() { std::cout< 5 | #include "../store_client.h" 6 | #include "../cmd_protocol.h" 7 | using namespace kvstore; 8 | namespace test { 9 | static void test_client_transaction() { 10 | std::thread t([&](){ 11 | boost::asio::io_service ioservice2; 12 | ioservice2.run(); 13 | StoreClient client(ioservice2); 14 | client.connect("127.0.0.1", "23333"); 15 | StoreClient::Result result; 16 | result = client.use(2); 17 | DEBUG_INFO<<"result:"< 4 | #include 5 | #include 6 | namespace kvstore { 7 | 8 | 9 | const int POS_TYPE = 0; 10 | //request 11 | const int POS_TOTAL_LENGTH = 1; 12 | //get命令 13 | const int POS_GET_KEY_LENGTH = POS_TOTAL_LENGTH + 2; 14 | const int POS_GET_KEY = POS_GET_KEY_LENGTH + 2; 15 | //use命令 16 | const int POS_DB_ID = POS_TOTAL_LENGTH + 2; 17 | //response 18 | const int POS_MSG = POS_TOTAL_LENGTH + 2; 19 | //type r 20 | const int REQ_TYPE_PUT = 0x00; 21 | const int REQ_TYPE_GET = 0x01; 22 | const int REQ_TYPE_DEL = 0x02; 23 | const int REQ_TYPE_USE = 0x03; 24 | const int REQ_TYPE_LOGIN = 0x04; 25 | const int REQ_TYPE_MULT = 0x05; 26 | const int REQ_TYPE_EXEC = 0x06; 27 | const int REQ_TYPE_DISCARD = 0x07; 28 | const int REQ_TYPE_WATCH = 0x08; 29 | const int RESP_TYPE_OK = 0x00; 30 | const int RESP_TYPE_ERR = 0x01; 31 | 32 | 33 | inline int get_pos_value(std::size_t key_len) { 34 | return POS_GET_KEY + static_cast(key_len); 35 | } 36 | 37 | 38 | union Len { 39 | unsigned short us; 40 | char c[2]; 41 | }; 42 | 43 | 44 | //first_item 适用于只有一个值的请求 45 | static int first_item(const std::string& str, std::string& key) { 46 | key = str.substr(3, str.size() - 3); 47 | return 0; 48 | } 49 | 50 | static unsigned short total_len(const std::string& msg) { 51 | unsigned short len = *((unsigned short*) (msg.data() + POS_TOTAL_LENGTH)); 52 | len = boost::asio::detail::socket_ops::network_to_host_short(len); 53 | return len; 54 | } 55 | //get_key 和 get_value 适用于 put请求 56 | static int get_key(const std::string& str, std::size_t total_len, std::string& key) { 57 | unsigned short key_len_s = *((unsigned short*) (str.data() + POS_GET_KEY_LENGTH)); 58 | key_len_s = boost::asio::detail::socket_ops::network_to_host_short(key_len_s); 59 | std::size_t key_len = static_cast(key_len_s); 60 | if (key_len + POS_GET_KEY > total_len ) { 61 | return -1; 62 | } 63 | key = str.substr(POS_GET_KEY, key_len); 64 | return 0; 65 | } 66 | 67 | static int get_value(const std::string& str,std::size_t total_len, 68 | std::size_t key_len, std::string& value) { 69 | const int POS_VALUE = get_pos_value(key_len); 70 | std::size_t value_len = total_len - static_cast(POS_VALUE); 71 | if (POS_VALUE == 0) { 72 | value = ""; 73 | } else { 74 | value = str.substr(POS_VALUE, value_len); 75 | } 76 | return 0; 77 | } 78 | 79 | static std::string get_response(const std::string& msg, int type) { 80 | std::string resp = std::string{"xxx"} + msg; 81 | Len len; 82 | len.us = boost::asio::detail::socket_ops::host_to_network_short(resp.size()); 83 | resp[POS_TYPE] = type; 84 | resp[POS_TOTAL_LENGTH] = len.c[0]; 85 | resp[POS_TOTAL_LENGTH + 1] = len.c[1]; 86 | return resp; 87 | } 88 | 89 | static void set_total_len(std::string& msg) { 90 | Len len; 91 | len.us = static_cast (msg.length()); 92 | len.us = boost::asio::detail::socket_ops::host_to_network_short(len.us); 93 | msg[POS_TOTAL_LENGTH] = len.c[0]; 94 | msg[POS_TOTAL_LENGTH + 1] = len.c[0 + 1]; 95 | } 96 | 97 | static std::string get_use_request(std::size_t db) { 98 | std::string msg("xxxx"); 99 | set_total_len(msg); 100 | msg[POS_DB_ID] = static_cast(db); 101 | msg[POS_TYPE] = REQ_TYPE_USE; 102 | return msg; 103 | } 104 | 105 | static std::string get_put_request(std::string key, std::string value) { 106 | std::string msg = std::string("xxxxx") + key + value; 107 | msg[POS_TYPE] = REQ_TYPE_PUT; 108 | set_total_len(msg); 109 | Len len; 110 | len.us = static_cast (key.length()); 111 | len.us = boost::asio::detail::socket_ops::host_to_network_short(len.us); 112 | msg[POS_GET_KEY_LENGTH] = len.c[0]; 113 | msg[POS_GET_KEY_LENGTH + 1] = len.c[0 + 1]; 114 | return msg; 115 | } 116 | 117 | static std::string get_get_request(std::string key) { 118 | std::string msg = std::string("xxx") + key; 119 | set_total_len(msg); 120 | msg[POS_TYPE] = REQ_TYPE_GET; 121 | return msg; 122 | } 123 | 124 | static std::string get_del_request(std::string key) { 125 | std::string msg = std::string("xxx") + key; 126 | set_total_len(msg); 127 | msg[POS_TYPE] = REQ_TYPE_DEL; 128 | return msg; 129 | } 130 | 131 | static std::string get_login_request(std::string password) { 132 | std::string msg = std::string("xxx") + password; 133 | set_total_len(msg); 134 | msg[POS_TYPE] = REQ_TYPE_LOGIN; 135 | return msg; 136 | } 137 | 138 | static std::string get_mult_request() { 139 | std::string msg("xxx"); 140 | set_total_len(msg); 141 | msg[POS_TYPE] = REQ_TYPE_MULT; 142 | return msg; 143 | } 144 | 145 | static std::string get_exec_request() { 146 | std::string msg("xxx"); 147 | set_total_len(msg); 148 | msg[POS_TYPE] = REQ_TYPE_EXEC; 149 | return msg; 150 | } 151 | 152 | static std::string get_discard_request() { 153 | std::string msg("xxx"); 154 | set_total_len(msg); 155 | msg[POS_TYPE] = REQ_TYPE_DISCARD; 156 | return msg; 157 | } 158 | 159 | static std::string get_watch_request(const std::string& key) { 160 | std::string msg = std::string("xxx") + key; 161 | set_total_len(msg); 162 | msg[POS_TYPE] = REQ_TYPE_WATCH; 163 | return msg; 164 | } 165 | 166 | 167 | }//namespace 168 | 169 | 170 | //协议的长度2字节采用unsigned short, 一字节采用unsigned char 171 | //客户端request 172 | 173 | // -------------------------------------------------------------------------------------------------------- 174 | // | 请求类型 | 总 长 度 | key长度 | key正文 + value 正文(如果有)| 175 | // | 1byte | 2bytes | 2bytes | 变长 | 176 | //---------------------------------------------------------------------------------------------------------- 177 | 178 | //请求类型 179 | //0x00 put 180 | //0x01 get 181 | //0x02 del 182 | //0x03 use 183 | //0x04 login 184 | //0x05 mult 185 | //0x06 exec 186 | 187 | //get 188 | // ---------------------------------------------------------------------- 189 | // | 请求类型 | 总 长 度 | key正文| 190 | // | 1byte | 2bytes | 变长 | 191 | //------------------------------------------------------------------------ 192 | 193 | //put 194 | // ---------------------------------------------------------------------------------------- 195 | // | 请求类型 | 总 长 度 | key长度 | key正文 + value正文| 196 | // | 1byte | 2bytes | 2bytes | 变 长 | 197 | //---------------------------------------------------------------------------------------- 198 | 199 | //del 200 | // ----------------------------------------------------------------------- 201 | // | 请求类型 | 总 长 度 | key长度 | key正文| 202 | // | 1byte | 2bytes | 2bytes | 变长 | 203 | //----------------------------------------------------------------------- 204 | 205 | //use 206 | // ---------------------------------------------------------- 207 | // | 请求类型 | 总 长 度 | 数据库id | 208 | // | 1byte | 2bytes | 1byte | 209 | //----------------------------------------------------------- 210 | 211 | //watch 212 | // ----------------------------------------------------------------------- 213 | // | 请求类型 | 总 长 度 | key长度 | key正文| 214 | // | 1byte | 2bytes | 2bytes | 变长 | 215 | //----------------------------------------------------------------------- 216 | 217 | //login 218 | // ---------------------------------------------------------- 219 | // | 请求类型 | 总 长 度 | 密码正文 | 220 | // | 1byte | 2bytes | 变长 | 221 | //---------------------------------------------------------- 222 | 223 | 224 | //discard 225 | 226 | //服务端response、 227 | // ---------------------------------------------------------- 228 | // | 答复类型 | 总长度 | message | 229 | // | 1byte | 2bytes | 变长 | 230 | //------------------------------------------------------------ 231 | 232 | //答复类型 233 | // 0x00 ok 234 | // 0x01 error 235 | 236 | 237 | 238 | #endif // PROTOCOL 239 | 240 | -------------------------------------------------------------------------------- /src/store_server.cpp: -------------------------------------------------------------------------------- 1 | #include "store_server.h" 2 | #include "cmd_protocol.h" 3 | #include 4 | namespace kvstore { 5 | StoreServer::StoreServer(io_service& ioservice, std::size_t port) : 6 | tcp_server_(ioservice, port), 7 | conns_(), 8 | dbs_(), 9 | config_(), 10 | persistence_(*this) 11 | { 12 | tcp_server_.set_close_callback( 13 | std::bind(&StoreServer::handle_close, this, std::placeholders::_1)); 14 | tcp_server_.set_new_conn_callback( 15 | std::bind(&StoreServer::handle_new_conn, this, std::placeholders::_1)); 16 | tcp_server_.set_read_callback( 17 | std::bind(&StoreServer::handle_read, this, std::placeholders::_1, std::placeholders::_2)); 18 | LOG_INFO<<"kvstore server start!"; 19 | } 20 | 21 | void StoreServer::handle_close(TcpConnection::Pointer conn) { 22 | auto iter = conns_.find(conn); 23 | assert(iter != conns_.end()); 24 | conns_.erase(iter); 25 | } 26 | 27 | void StoreServer::handle_read(TcpConnection::Pointer conn, std::size_t bytes_transfered) { 28 | ConnData& data = conns_[conn]; 29 | std::string& buf = data.buf; 30 | //没有获得完整命令,就返回 31 | buf.append(std::string(conn->buf().data(), bytes_transfered)); 32 | if (buf.length() < 3) { 33 | return; 34 | } 35 | std::size_t len = static_cast(total_len(buf)); 36 | if (buf.length() < len) { 37 | return; 38 | } 39 | std::string cmd = buf.substr(0, len); 40 | buf.erase(0, len); 41 | std::string retmsg(""); 42 | //将命令分发到对应的处理函数中,返回结果 43 | int err = dispath_cmd(cmd, conn, retmsg); 44 | //发送response 45 | std::string response(""); 46 | if (err < 0) { 47 | response = get_response(retmsg, RESP_TYPE_ERR); 48 | } else { 49 | response = get_response(retmsg, RESP_TYPE_OK); 50 | persistence_.record(cmd, data.db); 51 | } 52 | conn->send(response); 53 | //尾递归,处理没有处理完的命令 54 | handle_read(conn, 0); 55 | } 56 | 57 | void StoreServer::handle_new_conn(TcpConnection::Pointer conn) { 58 | conns_[conn] = ConnData(); 59 | } 60 | 61 | int StoreServer::dispath_cmd(const std::string &cmd, 62 | TcpConnection::Pointer conn, std::string &retmsg) 63 | { 64 | ConnData& data = conns_[conn]; 65 | int err = 0; 66 | if (cmd[POS_TYPE] == REQ_TYPE_LOGIN) { 67 | //输入密码登陆 68 | return execute_cmd_login(cmd, conn, retmsg); 69 | } 70 | if (!data.login && config_.need_password()) { 71 | retmsg = "please enter password"; 72 | LOG_DEBUG<<"用户未登录"; 73 | return -1; 74 | } 75 | if (cmd[POS_TYPE] == REQ_TYPE_MULT) { 76 | //mult 77 | return execute_cmd_mult(cmd, conn, retmsg); 78 | } 79 | if (cmd[POS_TYPE] == REQ_TYPE_WATCH) { 80 | //watch 81 | return execute_cmd_watch(cmd, conn, retmsg); 82 | } 83 | if (cmd[POS_TYPE] == REQ_TYPE_EXEC) { 84 | //exec 85 | return execute_cmd_exec(cmd, conn, retmsg); 86 | } 87 | if (cmd[POS_TYPE] == REQ_TYPE_DISCARD) { 88 | //discard 89 | return execute_cmd_discard(cmd, conn, retmsg); 90 | } 91 | if (data.transaction.mult()) { 92 | //在输入了mult之后,get put del clear会进入这,加入事务 93 | return add_transaction(cmd, conn, retmsg); 94 | } 95 | if (cmd[POS_TYPE] == REQ_TYPE_USE) { 96 | //切换db 97 | return execute_cmd_use(cmd, conn, retmsg); 98 | } 99 | //get put del clear 100 | err = execute_db_cmd(cmd, data.db, retmsg); 101 | return err; 102 | } 103 | 104 | int StoreServer::execute_db_cmd(const std::string &cmd, std::size_t db, std::string &retmsg) { 105 | int req_type = static_cast(cmd[POS_TYPE]); 106 | int err = -1; 107 | switch (req_type) { 108 | case REQ_TYPE_PUT : 109 | err = execute_cmd_put(cmd, db, retmsg); 110 | LOG_DEBUG<<"execute_cmd_put"; 111 | return err; 112 | case REQ_TYPE_GET: 113 | err = execute_cmd_get(cmd, db, retmsg); 114 | LOG_DEBUG<<"execute_cmd_get"; 115 | return err; 116 | case REQ_TYPE_DEL: 117 | err = execute_cmd_del(cmd, db, retmsg); 118 | LOG_DEBUG<<"execute_cmd_del"; 119 | return err; 120 | default: 121 | break; 122 | } 123 | retmsg = "cmd syntax error"; 124 | return err; 125 | } 126 | 127 | void StoreServer::init() { 128 | LOG_DEBUG<<"开始恢复数据"; 129 | persistence_.recover(); 130 | LOG_DEBUG<<"恢复数据完成"; 131 | } 132 | 133 | KVDatabase &StoreServer::db() { 134 | return dbs_; 135 | } 136 | 137 | int StoreServer::execute_cmd_put(const std::string &cmd, std::size_t db,std::string &retmsg) { 138 | std::string key; 139 | int err = get_key(cmd, cmd.length(), key); 140 | if (err < 0) { 141 | //todo 142 | LOG_DEBUG<<"err1"; 143 | retmsg = "error"; 144 | return -1; 145 | } 146 | std::string value; 147 | err = get_value(cmd, cmd.length(), key.length(), value); 148 | if (err < 0) { 149 | //todo 150 | LOG_DEBUG<<"err2"; 151 | retmsg = "error"; 152 | return -2; 153 | } 154 | err = dbs_.put(db, std::move(key), std::move(value)); 155 | if (err < 0) { 156 | //todo 157 | LOG_DEBUG<<"err3"; 158 | retmsg = "error"; 159 | return -3; 160 | } 161 | retmsg = "ok"; 162 | return 0; 163 | } 164 | 165 | int StoreServer::execute_cmd_del(const std::string &cmd, std::size_t db,std::string &retmsg) { 166 | std::string keystring; 167 | int err = first_item(cmd, keystring); 168 | if (err < 0) { 169 | //todo 170 | LOG_DEBUG<<"err1"; 171 | retmsg = "error"; 172 | return -1; 173 | } 174 | err = dbs_.del(db, std::move(keystring)); 175 | if (err < 0) { 176 | //todo 177 | LOG_DEBUG<<"null"; 178 | retmsg = "null"; 179 | return -2; 180 | } 181 | retmsg = "ok"; 182 | return 0; 183 | } 184 | 185 | int StoreServer::execute_cmd_use(const std::string &cmd, 186 | TcpConnection::Pointer conn, std::string &retmsg) { 187 | unsigned char c = cmd[POS_DB_ID]; 188 | std::size_t db = static_cast(c); 189 | if (db >= dbs_.size()) { 190 | retmsg = "error:number is too large"; 191 | LOG_DEBUG<<"err"; 192 | return -1; 193 | } 194 | retmsg = "ok"; 195 | conns_[conn].db = db; 196 | return 0; 197 | } 198 | 199 | int StoreServer::execute_cmd_login(const std::string &cmd, 200 | TcpConnection::Pointer conn, std::string &retmsg) 201 | { 202 | retmsg = "ok"; 203 | if (!config_.need_password()) { 204 | return 0; 205 | } 206 | std::string password; 207 | int err = first_item(cmd, password); 208 | if (err < 0) { 209 | LOG_DEBUG<<"err"; 210 | retmsg = "syntax error"; 211 | return -1; 212 | } 213 | 214 | if (password != config_.password()) { 215 | LOG_DEBUG<<"err"; 216 | retmsg = "password wrong"; 217 | return -2; 218 | } 219 | conns_[conn].login = true; 220 | return 0; 221 | } 222 | 223 | int StoreServer::execute_cmd_mult(const std::string &cmd, 224 | TcpConnection::Pointer conn, std::string &retmsg) 225 | { 226 | ConnData& data = conns_[conn]; 227 | if (data.transaction.mult()) { 228 | retmsg = "error"; 229 | return -1; 230 | } 231 | data.transaction.set_mult(true); 232 | retmsg = "ok"; 233 | return 0; 234 | } 235 | 236 | int StoreServer::execute_cmd_exec(const std::string &cmd, 237 | TcpConnection::Pointer conn, std::string &retmsg) 238 | { 239 | //todo 240 | ConnData& data = conns_[conn]; 241 | int err = 0; 242 | for (auto &pair : data.transaction.watchs()) { 243 | std::string value(""); 244 | dbs_.get(data.db, pair.first, value); 245 | if (value != pair.second) { 246 | err = -1; 247 | break; 248 | } 249 | } 250 | if (err == -1) { 251 | retmsg="watch value has been changed"; 252 | return err; 253 | } 254 | std::string ret; 255 | retmsg = ""; 256 | for (auto &cmd : data.transaction.cmds()) { 257 | err = execute_db_cmd(cmd, data.db, ret); 258 | retmsg = retmsg + ret + "\r\n"; 259 | if (err < 0) { 260 | break; 261 | } 262 | } 263 | if (!retmsg.empty()) { 264 | retmsg.erase(retmsg.size() - 2, 2); 265 | } 266 | data.transaction.set_mult(false); 267 | data.transaction.clear_cmd(); 268 | data.transaction.clear_watch(); 269 | return err; 270 | } 271 | 272 | int StoreServer::add_transaction(const std::string &cmd, 273 | TcpConnection::Pointer conn, std::string &retmsg) 274 | { 275 | ConnData& data = conns_[conn]; 276 | if (cmd[POS_TYPE] == REQ_TYPE_USE) { 277 | retmsg = "error"; 278 | return -1; 279 | } 280 | data.transaction.add_cmd(cmd); 281 | retmsg = "queued"; 282 | return 0; 283 | } 284 | 285 | int StoreServer::execute_cmd_discard(const std::string &cmd, 286 | TcpConnection::Pointer conn, std::string &retmsg) 287 | { 288 | 289 | ConnData& data = conns_[conn]; 290 | if (!data.transaction.mult()) { 291 | retmsg = "don't need to discard"; 292 | return -1; 293 | } 294 | data.transaction.set_mult(false); 295 | data.transaction.clear_cmd(); 296 | data.transaction.clear_watch(); 297 | retmsg = "ok"; 298 | return 0; 299 | } 300 | 301 | int StoreServer::execute_cmd_watch(const std::string &cmd, 302 | TcpConnection::Pointer conn, std::string &retmsg) 303 | { 304 | std::string keystring; 305 | int err = first_item(cmd, keystring); 306 | if (err < 0) { 307 | //todo 308 | LOG_DEBUG<<"cmd syntax error"; 309 | retmsg = "cmd syntax error"; 310 | return -1; 311 | } 312 | std::string value; 313 | ConnData& data = conns_[conn]; 314 | err = dbs_.get(data.db, std::move(keystring), value); 315 | if (err < 0) { 316 | value = ""; 317 | } 318 | data.transaction.add_watch(keystring, value); 319 | return 0; 320 | } 321 | 322 | int StoreServer::execute_cmd_get(const std::string &cmd, std::size_t db,std::string &retmsg) { 323 | std::string keystring; 324 | int err = first_item(cmd, keystring); 325 | if (err < 0) { 326 | //todo 327 | LOG_DEBUG<<"err1"; 328 | retmsg = "error"; 329 | return -1; 330 | } 331 | std::string value; 332 | err = dbs_.get(db, std::move(keystring), value); 333 | if (err < 0) { 334 | //todo 335 | LOG_DEBUG<<"null"; 336 | retmsg = "null"; 337 | return -2; 338 | } 339 | retmsg = value; 340 | return 0; 341 | } 342 | 343 | 344 | }//namespace 345 | 346 | -------------------------------------------------------------------------------- /src/kvstore.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {a526d735-51c7-4b8c-a058-95f544668f26} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | 0 45 | 8 46 | true 47 | 1 48 | true 49 | true 50 | true 51 | false 52 | 53 | 54 | 55 | ProjectExplorer.Project.PluginSettings 56 | 57 | 58 | 59 | ProjectExplorer.Project.Target.0 60 | 61 | deskon 62 | deskon 63 | {f2b5f4a2-6448-4c1b-afcc-09e49986b0b3} 64 | 0 65 | 0 66 | 0 67 | 68 | /home/sunfish/qtcode/build-kvstore-deskon-Debug 69 | 70 | 71 | true 72 | qmake 73 | 74 | QtProjectManager.QMakeBuildStep 75 | false 76 | true 77 | 78 | false 79 | false 80 | false 81 | 82 | 83 | true 84 | Make 85 | 86 | Qt4ProjectManager.MakeStep 87 | 88 | -w 89 | -r 90 | 91 | false 92 | 93 | 94 | 95 | 2 96 | 构建 97 | 98 | ProjectExplorer.BuildSteps.Build 99 | 100 | 101 | 102 | true 103 | Make 104 | 105 | Qt4ProjectManager.MakeStep 106 | 107 | -w 108 | -r 109 | 110 | true 111 | clean 112 | 113 | 114 | 1 115 | 清理 116 | 117 | ProjectExplorer.BuildSteps.Clean 118 | 119 | 2 120 | false 121 | 122 | Debug 123 | 124 | Qt4ProjectManager.Qt4BuildConfiguration 125 | 2 126 | true 127 | 128 | 129 | /home/sunfish/qtcode/build-kvstore-deskon-Release 130 | 131 | 132 | true 133 | qmake 134 | 135 | QtProjectManager.QMakeBuildStep 136 | false 137 | true 138 | 139 | false 140 | false 141 | false 142 | 143 | 144 | true 145 | Make 146 | 147 | Qt4ProjectManager.MakeStep 148 | 149 | -w 150 | -r 151 | 152 | false 153 | 154 | 155 | 156 | 2 157 | 构建 158 | 159 | ProjectExplorer.BuildSteps.Build 160 | 161 | 162 | 163 | true 164 | Make 165 | 166 | Qt4ProjectManager.MakeStep 167 | 168 | -w 169 | -r 170 | 171 | true 172 | clean 173 | 174 | 175 | 1 176 | 清理 177 | 178 | ProjectExplorer.BuildSteps.Clean 179 | 180 | 2 181 | false 182 | 183 | Release 184 | 185 | Qt4ProjectManager.Qt4BuildConfiguration 186 | 0 187 | true 188 | 189 | 2 190 | 191 | 192 | 0 193 | 部署 194 | 195 | ProjectExplorer.BuildSteps.Deploy 196 | 197 | 1 198 | 在本地部署 199 | 200 | ProjectExplorer.DefaultDeployConfiguration 201 | 202 | 1 203 | 204 | 205 | 206 | false 207 | false 208 | false 209 | false 210 | true 211 | 0.01 212 | 10 213 | true 214 | 1 215 | 25 216 | 217 | 1 218 | true 219 | false 220 | true 221 | valgrind 222 | 223 | 0 224 | 1 225 | 2 226 | 3 227 | 4 228 | 5 229 | 6 230 | 7 231 | 8 232 | 9 233 | 10 234 | 11 235 | 12 236 | 13 237 | 14 238 | 239 | 2 240 | 241 | kvstore 242 | 243 | Qt4ProjectManager.Qt4RunConfiguration:/home/sunfish/qtcode/kvstore/kvstore.pro 244 | 245 | kvstore.pro 246 | false 247 | true 248 | 249 | 3768 250 | false 251 | true 252 | false 253 | false 254 | true 255 | 256 | 1 257 | 258 | 259 | 260 | ProjectExplorer.Project.Target.1 261 | 262 | qt5-deskon 263 | qt5-deskon 264 | {0bb78d17-6d5c-470b-b253-1672982557ca} 265 | 0 266 | 0 267 | 0 268 | 269 | /home/sunfish/qtcode/build-kvstore-qt5_deskon-Debug 270 | 271 | 272 | true 273 | qmake 274 | 275 | QtProjectManager.QMakeBuildStep 276 | false 277 | true 278 | 279 | false 280 | false 281 | false 282 | 283 | 284 | true 285 | Make 286 | 287 | Qt4ProjectManager.MakeStep 288 | 289 | -w 290 | -r 291 | 292 | false 293 | 294 | 295 | 296 | 2 297 | 构建 298 | 299 | ProjectExplorer.BuildSteps.Build 300 | 301 | 302 | 303 | true 304 | Make 305 | 306 | Qt4ProjectManager.MakeStep 307 | 308 | -w 309 | -r 310 | 311 | true 312 | clean 313 | 314 | 315 | 1 316 | 清理 317 | 318 | ProjectExplorer.BuildSteps.Clean 319 | 320 | 2 321 | false 322 | 323 | Debug 324 | 325 | Qt4ProjectManager.Qt4BuildConfiguration 326 | 2 327 | true 328 | 329 | 330 | /home/sunfish/qtcode/build-kvstore-qt5_deskon-Release 331 | 332 | 333 | true 334 | qmake 335 | 336 | QtProjectManager.QMakeBuildStep 337 | false 338 | true 339 | 340 | false 341 | false 342 | false 343 | 344 | 345 | true 346 | Make 347 | 348 | Qt4ProjectManager.MakeStep 349 | 350 | -w 351 | -r 352 | 353 | false 354 | 355 | 356 | 357 | 2 358 | 构建 359 | 360 | ProjectExplorer.BuildSteps.Build 361 | 362 | 363 | 364 | true 365 | Make 366 | 367 | Qt4ProjectManager.MakeStep 368 | 369 | -w 370 | -r 371 | 372 | true 373 | clean 374 | 375 | 376 | 1 377 | 清理 378 | 379 | ProjectExplorer.BuildSteps.Clean 380 | 381 | 2 382 | false 383 | 384 | Release 385 | 386 | Qt4ProjectManager.Qt4BuildConfiguration 387 | 0 388 | true 389 | 390 | 2 391 | 392 | 393 | 0 394 | 部署 395 | 396 | ProjectExplorer.BuildSteps.Deploy 397 | 398 | 1 399 | 在本地部署 400 | 401 | ProjectExplorer.DefaultDeployConfiguration 402 | 403 | 1 404 | 405 | 406 | 407 | false 408 | false 409 | false 410 | false 411 | true 412 | 0.01 413 | 10 414 | true 415 | 1 416 | 25 417 | 418 | 1 419 | true 420 | false 421 | true 422 | valgrind 423 | 424 | 0 425 | 1 426 | 2 427 | 3 428 | 4 429 | 5 430 | 6 431 | 7 432 | 8 433 | 9 434 | 10 435 | 11 436 | 12 437 | 13 438 | 14 439 | 440 | -1 441 | 442 | 443 | 444 | %{buildDir} 445 | 自定义执行档 446 | 447 | ProjectExplorer.CustomExecutableRunConfiguration 448 | 3768 449 | false 450 | true 451 | false 452 | false 453 | true 454 | 455 | 1 456 | 457 | 458 | 459 | ProjectExplorer.Project.TargetCount 460 | 2 461 | 462 | 463 | ProjectExplorer.Project.Updater.FileVersion 464 | 18 465 | 466 | 467 | Version 468 | 18 469 | 470 | 471 | --------------------------------------------------------------------------------