在linux里面的服务进程,需要提供两个接口:基于命令行的cli接口,基于api的应用接口。
其中cli命令接口,经过解析后,调用的也是api接口,实现业务功能。
cli的设计模式:命令解析器、dispatch器、callback handler
个人写的小项目来说,一般在启动的时候,用于Get一些参数,如ip port,thread_num,period,等。
对于中型大型项目,参数往外是在运行的过程中,按需传入。因此需要设计为c/s模式。即:使用Unix本机通讯、或Http服务器和主进程一起运行。 cli另外实现一个unix通讯或http客户端,找到daemon进程。这种模式是一种服务器代理模式,代理的对象就是命令行传入。
linux c 中的命令解析器是用getopt()和getopt_long来获取参数的
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *optstring);
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
在c++ boost库提供了一个封装好的模板,弥补了getopt()的不足之处,提供更丰富,安全,易用的cli解析接口。
//cli.hpp
#include <iostream>
#include <iostream>
#include <string>
#include <boost/program_options.hpp>
using namespace std;
namespace bpo = boost::program_options;
namespace ipfs
{
namespace detail {
enum SUB_CMD {
INIT = 1,
DAEMON,
ADD,
OBJECT,
CAT,
GET,
LS,
HELP,
UNKNOW,
};
SUB_CMD cmd_parser(int argc, char **argv, vector<string> & item) {
item.clear();
// 定义命令描述、命令参数
const string descs = "USAGE\
\n ipfs - Global p2p merkle-dag filesystem.\
\n\
\n ipfs [--config=<config> | -c] [--debug | -D] [--help] [-h] [--api=<api>] [--offline] [--cid-base=<base>] [--upgrade-cidv0-in-output]\
\n [--encoding=<encoding> | --enc] [--timeout=<timeout>] <command> ...\
";
bpo::options_description opts(descs);
opts.add_options()
("init", "Initialize ipfs local configuration")
("daemon", bpo::value<string>(), "Start a long-running daemon process")
("add", bpo::value<std::vector<std::string>>()->multitoken(), "the file name which add to ipfs repository")
("object", bpo::value<string>(), "Interact with raw dag nodes")
("cat", bpo::value<string>(), "Show IPFS object data")
("get", bpo::value<string>(), "Download IPFS objects")
("ls", bpo::value<string>(), "List links from an object")
("help", "this is a program to find a specified file");
// 保存参数到解析器
bpo::variables_map vm;
try{
bpo::store(bpo::parse_command_line(argc, argv, opts), vm);
}
catch(...){
cout << "输入的参数中存在未定义的选项!\n";
cout << opts << endl;
return UNKNOW;
}
// 读取参数解析器
if( vm.count("init") ) {
cout << "init repository." << endl;
return INIT;
}
else if( vm.count("daemon") ) {
string mode = vm["daemon"].as<string>();
item.push_back(mode);
//cout << "start ipfs daemon." << endl;
return DAEMON;
}
else if( vm.count("add") ) {
//std::cout << "file which added is:";
for(auto& str : vm["add"].as<std::vector<std::string> >() ) {
//std::cout << str << " ";
item.push_back(str);
}
//std::cout << std::endl;
//cout << "add file " << vm["add"].as<string>() << " to ipfs repository" << endl;
return ADD;
}
else if( vm.count("object") ) {
string str = vm["object"].as<string>();
//cout << "object " << str << " to ipfs repository" << endl;
item.push_back(str);
return OBJECT;
}
else if( vm.count("cat") ) {
string id = vm["cat"].as<string>();
//cout << "cat " << id << " from ipfs repository" << endl;
item.push_back(id);
return CAT;
}
else if( vm.count("get") ) {
string id = vm["get"].as<string>();
//cout << "get " << id << " get from ipfs repository" << endl;
item.push_back(id);
return GET;
}
else if( vm.count("ls") ) {
string id = vm["ls"].as<string>();
//cout << "ls " << id << " to ipfs repository" << endl;
item.push_back(id);
return LS;
}
else if( vm.count("help") ) {
cout << opts << endl;
return HELP;
}
else if( vm.empty() ) {
cout << "no options found \n";
cout << opts << endl;
}
return UNKNOW;
}
}
}
//main.cc
#include <iostream>
#include <boost/program_options.hpp>
#include "cli.hpp"
using namespace std;
namespace po = boost::program_options;
int main(int agrc, char **argv)
{
vector<string> item;
SUB_CMD cmd = cmd_parser(argc, argv, item);
switch (cmd) {
case INIT:
break;
case DAEMON:
{
bool bootstrap_mode = false;
for (auto & it : item) {
if (it == "bootstrap") {
bootstrap_mode = true;
break;
}
}
if (bootstrap_mode) {
cout << " mode is daemon." << endl;
daemon(1, 1);
}
break;
case ADD:
// 只操作本地仓库
for (auto& path : item) {
cout << "ADD" << endl;
}
break;
case OBJECT:
break;
case CAT:
;
break;
case GET:
;
break;
case LS:
;
break;
case HELP:
;
break;
case UNKNOW:
;
break;
}
}