├── api ├── cpp │ ├── .gitignore │ └── README.md └── php │ ├── test.php │ └── sim.php ├── test ├── test_app.conf ├── test.cpp ├── Makefile ├── test_app.cpp ├── test_client.cpp ├── simple_server.cpp ├── test_link.cpp ├── test_msg.cpp ├── bench_client.cpp └── test_server.cpp ├── .gitignore ├── Makefile ├── src ├── client │ ├── Makefile │ ├── client.h │ ├── demo.cpp │ └── client.cpp ├── decoder.h ├── fde.cpp ├── sim.h ├── message.h ├── Makefile ├── handler.cpp ├── server.h ├── util │ ├── app.h │ ├── daemon.h │ ├── file.h │ ├── ip_filter.h │ ├── thread.h │ ├── config.h │ ├── log.h │ ├── app.cpp │ ├── config.cpp │ ├── log.cpp │ └── strings.h ├── link.h ├── fde.h ├── message.cpp ├── handler.h ├── decoder.cpp ├── sim.cpp ├── fde_select.cpp ├── fde_epoll.cpp ├── link.cpp └── server.cpp ├── README.md └── LICENSE /api/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | *.h 2 | *.cpp 3 | -------------------------------------------------------------------------------- /api/cpp/README.md: -------------------------------------------------------------------------------- 1 | sim C++ client 2 | === 3 | 4 | -------------------------------------------------------------------------------- /test/test_app.conf: -------------------------------------------------------------------------------- 1 | pidfile = ./test_app.pid 2 | 3 | logger: 4 | level: debug 5 | output: stdout 6 | rotate: 7 | size: 1000000000 8 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | 5 | int main(int argc, char **argv){ 6 | return 0; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.out 4 | *.class 5 | tmp 6 | .DS_Store 7 | *.pyc 8 | *.swp 9 | *_cpy_* 10 | *.dSYM* 11 | *.pid 12 | build_config.mk 13 | output 14 | src/client/demo 15 | 16 | 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | $(shell sh build.sh 1>&2) 2 | include build_config.mk 3 | 4 | all: 5 | cd src; make 6 | cd test; make 7 | 8 | clean: 9 | cd src; make clean 10 | cd test; make clean 11 | rm -rf *.o a.out 12 | -------------------------------------------------------------------------------- /src/client/Makefile: -------------------------------------------------------------------------------- 1 | include ../../build_config.mk 2 | CFLAGS += -I../ 3 | OUTPUT_DIR=../../api/cpp 4 | 5 | all: lib 6 | ${CXX} -I$(OUTPUT_DIR) -o demo demo.cpp $(OUTPUT_DIR)/libsim-client.a 7 | 8 | lib: client.h client.cpp 9 | $(CXX) $(CFLAGS) -c client.cpp 10 | ar -cru libsim-client.a\ 11 | client.o\ 12 | ../sim.o ../message.o ../decoder.o ../link.o 13 | cp client.h ../message.h libsim-client.a ../../api/cpp 14 | 15 | clean: 16 | rm -rf *.o *.a *.out 17 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | include ../build_config.mk 2 | 3 | CFLAGS += -I../output/include 4 | CLIBS += ../output/libsim.a 5 | 6 | all: $(OBJS) 7 | g++ -o test_msg.out $(CFLAGS) test_msg.cpp $(CLIBS) 8 | g++ -o test_link.out $(CFLAGS) test_link.cpp $(CLIBS) 9 | g++ -o test_server.out $(CFLAGS) test_server.cpp $(CLIBS) 10 | g++ -o test_client.out $(CFLAGS) test_client.cpp $(CLIBS) 11 | g++ -o bench_client.out $(CFLAGS) bench_client.cpp $(CLIBS) 12 | g++ -o test_app.out $(CFLAGS) test_app.cpp $(CLIBS) 13 | g++ -o simple_server.out $(CFLAGS) simple_server.cpp $(CLIBS) 14 | 15 | clean: 16 | rm -rf *.o *.a *.out 17 | 18 | -------------------------------------------------------------------------------- /src/decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_DECODER_H_ 2 | #define SIM_DECODER_H_ 3 | 4 | #include 5 | #include "message.h" 6 | 7 | namespace sim{ 8 | 9 | class Decoder{ 10 | public: 11 | Decoder(){ 12 | buffer_offset = 0; 13 | } 14 | // 当你从 socket 中读到数据, 或者从文件中读到数据时, 将数据压入解码器的数据缓冲区 15 | int push(const char *buf, int len); 16 | // 解析缓冲区中的数据, 如果解析出一个完整的 FIX 报文, 返回 1, 17 | // 如果数据不足一个报文, 返回 0, 你应该继续读取数据并压入解码器. 18 | // 如果出错(如系统错误), 返回 -1. 19 | int parse(Message *msg); 20 | private: 21 | // TODO: 优化成 ring buffer, 直接从 socket 里读 22 | std::string buffer; 23 | int buffer_offset; 24 | }; 25 | 26 | }; // namespace sim 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/client/client.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_CLIENT_H_ 2 | #define SIM_CLIENT_H_ 3 | 4 | #include "message.h" 5 | 6 | namespace sim{ 7 | 8 | class Link; 9 | std::string encode(const std::string s, bool force_ascii=false); 10 | std::string decode(const std::string s); 11 | 12 | class Client 13 | { 14 | public: 15 | static Client* connect(const char *ip, int port); 16 | static Client* connect(const std::string &ip, int port); 17 | 18 | int send(const Message &msg); // blocking send 19 | int recv(Message *msg); // blocking recv 20 | 21 | ~Client(); 22 | private: 23 | Client(); 24 | Link *link; 25 | }; 26 | 27 | }; // namespace sim 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /test/test_app.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | 5 | class MyApplication : public sim::Application 6 | { 7 | public: 8 | virtual int loop_once(); 9 | virtual int init(); 10 | virtual int free(); 11 | }; 12 | 13 | int MyApplication::init(){ 14 | log_info("server started."); 15 | return 0; 16 | } 17 | 18 | int MyApplication::free(){ 19 | log_info("server exit."); 20 | return 0; 21 | } 22 | 23 | int MyApplication::loop_once(){ 24 | sleep(1); 25 | log_debug(""); 26 | return 0; 27 | } 28 | 29 | int main(int argc, char **argv){ 30 | MyApplication app; 31 | return app.main(argc, argv); 32 | } 33 | -------------------------------------------------------------------------------- /src/fde.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #include 7 | #include "fde.h" 8 | 9 | namespace sim{ 10 | 11 | struct Fdevent* Fdevents::get_fde(int fd){ 12 | while((int)events.size() <= fd){ 13 | struct Fdevent *fde = new Fdevent(); 14 | fde->fd = events.size(); 15 | fde->s_flags = FDEVENT_NONE; 16 | fde->data.num = 0; 17 | fde->data.ptr = NULL; 18 | events.push_back(fde); 19 | } 20 | return events[fd]; 21 | } 22 | 23 | }; // namespace sim 24 | 25 | 26 | #ifdef HAVE_EPOLL 27 | #include "fde_epoll.cpp" 28 | #else 29 | #include "fde_select.cpp" 30 | #endif 31 | -------------------------------------------------------------------------------- /src/client/demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "client.h" 8 | 9 | int main(int argc, char **argv){ 10 | printf("Usage: %s [ip] [port]\n", argv[0]); 11 | const char *ip = (argc >= 2)? argv[1] : "127.0.0.1"; 12 | int port = (argc >= 3)? atoi(argv[2]) : 8800; 13 | 14 | // connect to server 15 | sim::Client *client = sim::Client::connect(ip, port); 16 | if(client == NULL){ 17 | printf("fail to connect to server!\n"); 18 | return 0; 19 | } 20 | 21 | sim::Message req, resp; 22 | req.set_type("ping"); 23 | client->send(req); 24 | client->recv(&resp); 25 | printf("resp: %s\n", resp.encode().c_str()); 26 | 27 | delete client; 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /test/test_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | 5 | int main(int argc, char **argv){ 6 | sim::Link *link = sim::Link::connect("127.0.0.1", 8800); 7 | if(!link){ 8 | log_fatal("%s", strerror(errno)); 9 | exit(0); 10 | } 11 | 12 | sim::Message msg; 13 | msg.add("ping"); 14 | msg.add("hello world!"); 15 | 16 | link->send(msg); 17 | link->flush(); 18 | 19 | while(1){ 20 | int ret; 21 | ret = link->read(); 22 | log_debug("read %d", ret); 23 | if(ret == 0){ 24 | exit(0); 25 | } 26 | sim::Message msg; 27 | ret = link->recv(&msg); 28 | if(ret == -1){ 29 | exit(0); 30 | }else if(ret == 0){ 31 | continue; 32 | } 33 | log_debug("received msg: %s", msg.encode().c_str()); 34 | break; 35 | } 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /test/simple_server.cpp: -------------------------------------------------------------------------------- 1 | #include "sim/sim.h" 2 | 3 | class MyHandler : public sim::Handler 4 | { 5 | public: 6 | virtual sim::HandlerState proc(const sim::Request &req, sim::Response *resp){ 7 | std::string cmd = req.msg.type(); 8 | if(cmd == "ping"){ 9 | resp->msg.add("ok"); 10 | resp->msg.add("pong"); 11 | }else{ 12 | resp->msg.add("ok"); 13 | resp->msg.add(cmd); 14 | } 15 | return this->resp(); 16 | } 17 | }; 18 | 19 | int main(int argc, char **argv){ 20 | const char *ip = "127.0.0.1"; 21 | int port = 8800; 22 | sim::Server *serv = sim::Server::listen(ip, port); 23 | if(!serv){ 24 | log_fatal("%s", strerror(errno)); 25 | exit(0); 26 | } 27 | log_info("server listen on %s:%d", ip, port); 28 | 29 | MyHandler handler; 30 | serv->add_handler(&handler); 31 | 32 | serv->loop(); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /src/sim.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_SIM_H_ 2 | #define SIM_SIM_H_ 3 | 4 | #include 5 | #include 6 | #include "message.h" 7 | #include "decoder.h" 8 | #include "fde.h" 9 | #include "link.h" 10 | #include "server.h" 11 | #include "handler.h" 12 | #include "util/config.h" 13 | #include "util/app.h" 14 | #include "util/log.h" 15 | 16 | namespace sim{ 17 | 18 | const static char KV_SEP_BYTE = '='; 19 | const static char KV_END_BYTE = ' '; 20 | const static char MSG_END_BYTE = '\n'; 21 | 22 | 23 | inline static 24 | double microtime(){ 25 | struct timeval now; 26 | gettimeofday(&now, NULL); 27 | double ret = now.tv_sec + now.tv_usec/1000.0/1000.0; 28 | return ret; 29 | } 30 | 31 | std::string encode(const std::string s, bool force_ascii=false); 32 | std::string decode(const std::string s); 33 | 34 | }; // namespace sim 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/message.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_MESSAGE_H_ 2 | #define SIM_MESSAGE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace sim{ 10 | 11 | class Message{ 12 | public: 13 | Message(); 14 | ~Message(); 15 | 16 | void reset(); 17 | 18 | std::string type() const; 19 | void set_type(const std::string &type); 20 | 21 | void add(const std::string &val); // 自增 tag, 从 0 开始 22 | void set(int tag, int32_t val); 23 | void set(int tag, int64_t val); 24 | void set(int tag, const char *val); 25 | void set(int tag, const std::string &val); 26 | const std::string* get(int tag) const; 27 | 28 | std::string encode() const; 29 | const std::map* fields() const{ 30 | return &fields_; 31 | } 32 | 33 | private: 34 | std::string type_; 35 | std::map fields_; 36 | friend class Decoder; 37 | }; 38 | 39 | }; // namespace sim 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /test/test_link.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | 5 | int main(int argc, char **argv){ 6 | sim::Link *serv = sim::Link::listen("127.0.0.1", 8800); 7 | if(!serv){ 8 | log_fatal(""); 9 | exit(0); 10 | } 11 | while(1){ 12 | sim::Link *link = serv->accept(); 13 | if(!link){ 14 | log_fatal(""); 15 | exit(0); 16 | } 17 | log_debug("accept link from %s:%d", link->remote_ip, link->remote_port); 18 | 19 | while(1){ 20 | int ret; 21 | ret = link->read(); 22 | if(ret == 0){ 23 | delete link; 24 | log_error("link closed."); 25 | break; 26 | } 27 | while(1){ 28 | sim::Message msg; 29 | ret = link->recv(&msg); 30 | if(ret == -1){ 31 | delete link; 32 | log_error("parse error!"); 33 | break; 34 | }else if(ret == 0){ 35 | break; 36 | } 37 | log_debug("%s", msg.encode().c_str()); 38 | } 39 | } 40 | } 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sim 2 | 3 | C++ network server framework, `nc` and `telnet` friendly. 4 | 5 | # demo 6 | 7 | #include "sim/sim.h" 8 | 9 | class MyHandler : public sim::Handler 10 | { 11 | public: 12 | virtual sim::HandlerState proc(const sim::Request &req, sim::Response *resp){ 13 | std::string cmd = req.msg.type(); 14 | if(cmd == "ping"){ 15 | resp->msg.add("ok"); 16 | resp->msg.add("pong"); 17 | }else{ 18 | resp->msg.add("ok"); 19 | resp->msg.add(cmd); 20 | } 21 | return this->resp(); 22 | } 23 | }; 24 | 25 | int main(int argc, char **argv){ 26 | const char *ip = "127.0.0.1"; 27 | int port = 8800; 28 | sim::Server *serv = sim::Server::listen(ip, port); 29 | if(!serv){ 30 | log_fatal("%s", strerror(errno)); 31 | exit(0); 32 | } 33 | log_info("server listen on %s:%d", ip, port); 34 | 35 | MyHandler handler; 36 | serv->add_handler(&handler); 37 | 38 | serv->loop(); 39 | return 0; 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | include ../build_config.mk 2 | 3 | CFLAGS += 4 | OBJS = sim.o message.o decoder.o link.o handler.o server.o \ 5 | fde.o log.o app.o config.o 6 | LIB=libsim.a 7 | HEADER_FILES=sim.h message.h decoder.h fde.h link.h handler.h server.h 8 | UTIL_HEADERS=util/thread.h util/strings.h util/log.h util/config.h util/app.h 9 | 10 | all: $(OBJS) 11 | mkdir -p $(HEADER_OUTPUT_DIR)/util 12 | ar -cru $(OUTPUT_DIR)/$(LIB) ${OBJS} 13 | cp $(HEADER_FILES) $(HEADER_OUTPUT_DIR) 14 | cp $(UTIL_HEADERS) $(HEADER_OUTPUT_DIR)/util 15 | 16 | fde.o: fde.h fde.cpp fde_select.cpp fde_epoll.cpp 17 | ${CXX} ${CFLAGS} -c fde.cpp 18 | app.o: util/app.h util/app.cpp 19 | $(CXX) ${CFLAGS} -c util/app.cpp 20 | log.o: util/log.h util/log.cpp 21 | $(CXX) ${CFLAGS} -c util/log.cpp 22 | config.o: util/config.h util/config.cpp 23 | $(CXX) ${CFLAGS} -c util/config.cpp 24 | .cpp.o: 25 | $(CXX) ${CFLAGS} -c $< -o $@ 26 | 27 | clean: 28 | rm -rf *.o *.a *.out $(OUTPUT_DIR)/$(LIB) $(HEADER_OUTPUT_DIR) 29 | 30 | -------------------------------------------------------------------------------- /src/handler.cpp: -------------------------------------------------------------------------------- 1 | #include "util/log.h" 2 | #include "handler.h" 3 | #include "util/thread.h" 4 | 5 | namespace sim{ 6 | 7 | int Handler::m_init(){ 8 | resps = new SelectableQueue(); 9 | return this->init(); 10 | } 11 | 12 | int Handler::m_free(){ 13 | delete resps; 14 | return this->free(); 15 | } 16 | 17 | int Handler::fd(){ 18 | return resps->fd(); 19 | } 20 | 21 | HandlerState Handler::accept(const Session &sess){ 22 | return HANDLE_OK; 23 | } 24 | 25 | HandlerState Handler::close(const Session &sess){ 26 | return HANDLE_OK; 27 | } 28 | 29 | HandlerState Handler::proc(const Request &req, Response *resp){ 30 | return HANDLE_OK; 31 | } 32 | 33 | void Handler::async_send(Response *resp){ 34 | this->resps->push(resp); 35 | } 36 | 37 | Response* Handler::handle(){ 38 | while(this->resps->size() > 0){ 39 | Response *resp; 40 | if(this->resps->pop(&resp) == 1 && resp != NULL){ 41 | return resp; 42 | } 43 | } 44 | return NULL; 45 | } 46 | 47 | }; // namespace sim 48 | -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_SERVER_H_ 2 | #define SIM_SERVER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "link.h" 8 | #include "handler.h" 9 | 10 | namespace sim{ 11 | 12 | class Fdevents; 13 | 14 | class Server 15 | { 16 | public: 17 | static Server* listen(const std::string &ip, int port); 18 | //static Server* create(...); 19 | Server(); 20 | ~Server(); 21 | 22 | void add_handler(Handler *handler); 23 | void loop(); 24 | int loop_once(); 25 | 26 | //int send(int64_t sess_id, const Message &msg); 27 | //int send_all(const Message &msg); 28 | private: 29 | Fdevents *fdes; 30 | Link *serv_link; 31 | int link_count; 32 | std::map sessions; 33 | std::vector handlers; 34 | 35 | Session* accept_session(); 36 | Session* get_session(int64_t sess_id); 37 | int close_session(Session *sess); 38 | int read_session(Session *sess); 39 | int write_session(Session *sess); 40 | }; 41 | 42 | }; // namespace sim 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/client/client.cpp: -------------------------------------------------------------------------------- 1 | #include "client.h" 2 | #include "link.h" 3 | 4 | namespace sim{ 5 | 6 | Client::Client(){ 7 | link = NULL; 8 | } 9 | 10 | Client::~Client(){ 11 | delete link; 12 | } 13 | 14 | Client* Client::connect(const char *ip, int port){ 15 | return Client::connect(std::string(ip), port); 16 | } 17 | 18 | Client* Client::connect(const std::string &ip, int port){ 19 | Link *link = Link::connect(ip, port); 20 | if(!link){ 21 | return NULL; 22 | } 23 | Client *client = new Client(); 24 | client->link = link; 25 | return client; 26 | } 27 | 28 | int Client::send(const Message &msg){ 29 | this->link->send(msg); 30 | return this->link->flush(); 31 | } 32 | 33 | int Client::recv(Message *msg){ 34 | while(1){ 35 | int ret; 36 | ret = link->recv(msg); 37 | if(ret == -1){ 38 | return -1; 39 | }else if(ret == 1){ 40 | return 1; 41 | }else{ 42 | // 43 | } 44 | 45 | ret = link->read(); 46 | if(ret <= 0){ 47 | return -1; 48 | } 49 | } 50 | } 51 | 52 | }; // namespace sim 53 | -------------------------------------------------------------------------------- /src/util/app.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_UTIL_APP_H 2 | #define SIM_UTIL_APP_H 3 | 4 | #include 5 | 6 | namespace sim{ 7 | 8 | class Config; 9 | 10 | class Application{ 11 | public: 12 | Application(){}; 13 | virtual ~Application(){}; 14 | 15 | int main(int argc, char **argv); 16 | 17 | virtual void usage(int argc, char **argv); 18 | virtual void welcome(); 19 | virtual int init(); 20 | virtual int free(); 21 | virtual int loop_once() = 0; 22 | 23 | protected: 24 | struct AppArgs{ 25 | bool is_daemon; 26 | std::string pidfile; 27 | std::string conf_file; 28 | std::string work_dir; 29 | std::string start_opt; 30 | 31 | AppArgs(){ 32 | is_daemon = false; 33 | start_opt = "start"; 34 | } 35 | }; 36 | 37 | Config *conf; 38 | AppArgs app_args; 39 | 40 | private: 41 | void parse_args(int argc, char **argv); 42 | void my_init(); 43 | 44 | int read_pid(); 45 | void write_pid(); 46 | void check_pidfile(); 47 | void remove_pidfile(); 48 | void kill_process(); 49 | }; 50 | 51 | 52 | }; // namespace sim 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /api/php/test.php: -------------------------------------------------------------------------------- 1 | send($req); 43 | $resp = $sim->recv(); 44 | } 45 | $speed = $total / (microtime(1) - $stime); 46 | printf("request speed: %d qps\n", $speed); 47 | 48 | 49 | echo sim_encode($req) . ""; 50 | echo sim_encode($resp) . ""; 51 | -------------------------------------------------------------------------------- /src/util/daemon.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef SIM_UTIL_DAEMON_H 7 | #define SIM_UTIL_DAEMON_H 8 | 9 | int daemonize(const char *dir=NULL){ 10 | switch(fork()){ 11 | case -1: 12 | return -1; 13 | case 0: 14 | break; 15 | default: 16 | exit(0); 17 | } 18 | if(setsid() == -1){ 19 | exit(0); 20 | } 21 | if(dir != NULL){ 22 | if(chdir(dir) == -1){ 23 | exit(0); 24 | } 25 | } 26 | 27 | if(close(STDIN_FILENO) == -1){ 28 | exit(0); 29 | } 30 | if(close(STDOUT_FILENO) == -1){ 31 | exit(0); 32 | } 33 | if(close(STDERR_FILENO) == -1){ 34 | exit(0); 35 | } 36 | 37 | int fd = open("/dev/null", O_RDWR, 0); 38 | if(fd == -1){ 39 | exit(0); 40 | } 41 | if(dup2(fd, STDIN_FILENO) == -1){ 42 | exit(0); 43 | } 44 | if(dup2(fd, STDOUT_FILENO) == -1){ 45 | exit(0); 46 | } 47 | if(dup2(fd, STDERR_FILENO) == -1){ 48 | exit(0); 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /test/test_msg.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | 5 | int main(int argc, char **argv){ 6 | sim::Message msg; 7 | msg.set_type("ok"); 8 | msg.set(34, 1); 9 | msg.set(35, "A"); 10 | msg.set(49, "CLIENT 06\n你好"); 11 | msg.set(56, "SERVER"); 12 | msg.set(52, "20150424-03:06:59.114"); 13 | msg.set(96, "H:12345678:12345678:"); 14 | msg.set(98, 0); 15 | msg.set(141, "Y"); 16 | msg.set(108, 20); 17 | 18 | std::string s = msg.encode(); 19 | printf("%s \n", s.c_str()); 20 | // 将 s 写入 socket, 相当于向服务器发送了请求 21 | 22 | sim::Decoder decoder; 23 | for(int i=0; i<2000; i++){ 24 | // 模拟从网络读取数据, 一次只读取一个字节 25 | decoder.push(s.data() + (i%s.size()), 1); 26 | 27 | while(1){ 28 | sim::Message tmp; 29 | int ret = decoder.parse(&tmp); 30 | if(ret == -1){ 31 | printf("error! ret = %d\n", ret); 32 | exit(0); 33 | }else if(ret == 0){ 34 | // 报文未就绪, 继续读网络 35 | break; 36 | } 37 | 38 | //printf("%s\n", tmp.get(49)->c_str()); 39 | printf("%s", tmp.encode().c_str()); 40 | // 继续解析, 因为可能一次读取到多个报文 41 | } 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Sim Authors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the Sim nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /src/link.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_LINK_H_ 2 | #define SIM_LINK_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "decoder.h" 11 | 12 | namespace sim{ 13 | 14 | class Link{ 15 | private: 16 | int sock; 17 | bool noblock_; 18 | bool error_; 19 | Decoder decoder_; 20 | Link(bool is_server=false); 21 | 22 | // TODO: max_recv_buf_size, max_send_buf_size 23 | public: 24 | std::string output; 25 | 26 | char remote_ip[INET_ADDRSTRLEN]; 27 | int remote_port; 28 | 29 | double create_time; 30 | double active_time; 31 | 32 | ~Link(); 33 | void close(); 34 | void nodelay(bool enable=true); 35 | // noblock(true) is supposed to corperate with IO Multiplex, 36 | // otherwise, flush() may cause a lot unneccessary write calls. 37 | void noblock(bool enable=true); 38 | void keepalive(bool enable=true); 39 | 40 | int fd() const{ 41 | return sock; 42 | } 43 | bool error() const{ 44 | return error_; 45 | } 46 | void mark_error(){ 47 | error_ = true; 48 | } 49 | 50 | static Link* connect(const char *ip, int port); 51 | static Link* connect(const std::string &ip, int port); 52 | static Link* listen(const char *ip, int port); 53 | static Link* listen(const std::string &ip, int port); 54 | Link* accept(); 55 | 56 | // read network data info buffer 57 | int read(); 58 | int write(); 59 | // flush buffered data to network 60 | // REQUIRES: nonblock 61 | int flush(); 62 | 63 | int recv(Message *msg); 64 | int send(const Message &msg); 65 | }; 66 | 67 | 68 | }; // namespace sim 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/util/file.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef SIM_UTIL_FILE_H_ 7 | #define SIM_UTIL_FILE_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static inline 15 | bool file_exists(const std::string &filename){ 16 | struct stat st; 17 | return stat(filename.c_str(), &st) == 0; 18 | } 19 | 20 | static inline 21 | bool is_dir(const std::string &filename){ 22 | struct stat st; 23 | if(stat(filename.c_str(), &st) == -1){ 24 | return false; 25 | } 26 | return (bool)S_ISDIR(st.st_mode); 27 | } 28 | 29 | static inline 30 | bool is_file(const std::string &filename){ 31 | struct stat st; 32 | if(stat(filename.c_str(), &st) == -1){ 33 | return false; 34 | } 35 | return (bool)S_ISREG(st.st_mode); 36 | } 37 | 38 | // return number of bytes read 39 | static inline 40 | int file_get_contents(const std::string &filename, std::string *content){ 41 | char buf[8192]; 42 | FILE *fp = fopen(filename.c_str(), "rb"); 43 | if(!fp){ 44 | return -1; 45 | } 46 | int ret = 0; 47 | while(!feof(fp) && !ferror(fp)){ 48 | int n = fread(buf, 1, sizeof(buf), fp); 49 | if(n > 0){ 50 | ret += n; 51 | content->append(buf, n); 52 | } 53 | } 54 | fclose(fp); 55 | return ret; 56 | } 57 | 58 | // return number of bytes written 59 | static inline 60 | int file_put_contents(const std::string &filename, const std::string &content){ 61 | FILE *fp = fopen(filename.c_str(), "wb"); 62 | if(!fp){ 63 | return -1; 64 | } 65 | int ret = fwrite(content.data(), 1, content.size(), fp); 66 | fclose(fp); 67 | return ret == (int)content.size()? ret : -1; 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/fde.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef UTIL_FDE_H 7 | #define UTIL_FDE_H 8 | 9 | #include 10 | #include 11 | 12 | #ifdef __linux__ 13 | #define HAVE_EPOLL 1 14 | #endif 15 | 16 | #ifdef HAVE_EPOLL 17 | #include 18 | #else 19 | #include 20 | #endif 21 | 22 | /* 23 | #define FDEVENT_NONE (0) 24 | #define FDEVENT_IN (1<<0) 25 | #define FDEVENT_PRI (1<<1) 26 | #define FDEVENT_OUT (1<<2) 27 | #define FDEVENT_HUP (1<<3) 28 | #define FDEVENT_ERR (1<<4) 29 | */ 30 | 31 | namespace sim{ 32 | 33 | const static int FDEVENT_NONE = (0); 34 | const static int FDEVENT_IN = (1<<0); 35 | const static int FDEVENT_PRI = (1<<1); 36 | const static int FDEVENT_OUT = (1<<2); 37 | const static int FDEVENT_HUP = (1<<3); 38 | const static int FDEVENT_ERR = (1<<4); 39 | 40 | struct Fdevent{ 41 | int fd; 42 | int s_flags; // subscribed events 43 | int events; // ready events 44 | struct{ 45 | int num; 46 | void *ptr; 47 | }data; 48 | 49 | bool readable() const{ 50 | return events & FDEVENT_IN; 51 | } 52 | bool writable() const{ 53 | return events & FDEVENT_OUT; 54 | } 55 | }; 56 | 57 | 58 | class Fdevents{ 59 | public: 60 | typedef std::vector events_t; 61 | private: 62 | #ifdef HAVE_EPOLL 63 | static const int MAX_FDS = 8 * 1024; 64 | int ep_fd; 65 | struct epoll_event ep_events[MAX_FDS]; 66 | #else 67 | int maxfd; 68 | fd_set readset; 69 | fd_set writeset; 70 | #endif 71 | events_t events; 72 | events_t ready_events; 73 | 74 | struct Fdevent *get_fde(int fd); 75 | public: 76 | Fdevents(); 77 | ~Fdevents(); 78 | 79 | bool isset(int fd, int flag); 80 | int set(int fd, int flags, int data_num, void *data_ptr); 81 | int del(int fd); 82 | int clr(int fd, int flags); 83 | const events_t* wait(int timeout_ms=-1); 84 | }; 85 | 86 | 87 | }; // namespace sim 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/message.cpp: -------------------------------------------------------------------------------- 1 | #include "util/strings.h" 2 | #include "sim.h" 3 | 4 | namespace sim{ 5 | 6 | Message::Message(){ 7 | } 8 | 9 | Message::~Message(){ 10 | } 11 | 12 | void Message::reset(){ 13 | fields_.clear(); 14 | } 15 | 16 | std::string Message::type() const{ 17 | return type_; 18 | } 19 | 20 | void Message::set_type(const std::string &type){ 21 | type_ = type; 22 | } 23 | 24 | void Message::set(int tag, int32_t val){ 25 | this->set(tag, (int64_t)val); 26 | } 27 | 28 | void Message::set(int tag, int64_t val){ 29 | this->set(tag, str(val)); 30 | } 31 | 32 | void Message::set(int tag, const char *val){ 33 | this->set(tag, str(val)); 34 | } 35 | 36 | void Message::set(int tag, const std::string &val){ 37 | if(tag == 0){ 38 | this->set_type(val); 39 | } 40 | fields_[tag] = val; 41 | } 42 | 43 | void Message::add(const std::string &val){ 44 | int tag; 45 | std::map::const_reverse_iterator it; 46 | it = fields_.rbegin(); 47 | if(it == fields_.rend()){ 48 | tag = 0; 49 | }else{ 50 | tag = it->first + 1; 51 | } 52 | this->set(tag, val); 53 | } 54 | 55 | const std::string* Message::get(int tag) const{ 56 | std::map::const_iterator it; 57 | it = fields_.find(tag); 58 | if(it == fields_.end()){ 59 | return NULL; 60 | } 61 | return &(it->second); 62 | } 63 | 64 | static std::string encode_field(int tag, const std::string &val){ 65 | std::string buffer; 66 | buffer.append(str(tag)); 67 | buffer.push_back(sim::KV_SEP_BYTE); 68 | buffer.append(sim::encode(val)); 69 | buffer.push_back(sim::KV_END_BYTE); 70 | return buffer; 71 | } 72 | 73 | std::string Message::encode() const{ 74 | std::string buffer; 75 | buffer.append(encode_field(0, this->type())); 76 | std::map::const_iterator it; 77 | for(it=fields_.begin(); it!=fields_.end(); it++){ 78 | int tag = it->first; 79 | if(tag == 0){ 80 | continue; 81 | } 82 | const std::string &val = it->second; 83 | buffer.append(encode_field(tag, val)); 84 | } 85 | buffer[buffer.size()-1] = sim::MSG_END_BYTE; 86 | return buffer; 87 | } 88 | 89 | }; // namespace sim 90 | -------------------------------------------------------------------------------- /src/handler.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM_HANDLER_H_ 2 | #define SIM_HANDLER_H_ 3 | 4 | #include "link.h" 5 | #include "message.h" 6 | 7 | template 8 | class SelectableQueue; 9 | 10 | namespace sim{ 11 | 12 | class Session 13 | { 14 | public: 15 | int64_t id; 16 | Link *link; 17 | 18 | Session(){ 19 | static int64_t inc = 0; 20 | this->id = inc ++; 21 | this->link = NULL; 22 | } 23 | ~Session(){ 24 | } 25 | }; 26 | 27 | class Request{ 28 | public: 29 | Message msg; 30 | Session sess; 31 | 32 | double stime; 33 | double time_wait; 34 | double time_proc; 35 | }; 36 | 37 | class Response{ 38 | public: 39 | Message msg; 40 | Session sess; 41 | }; 42 | 43 | 44 | typedef enum{ 45 | HANDLE_OK = 0, 46 | HANDLE_FAIL = 1, 47 | HANDLE_RESP = 1, 48 | }HandlerState; 49 | 50 | 51 | class Handler 52 | { 53 | public: 54 | Handler(){}; 55 | virtual ~Handler(){}; 56 | 57 | // 当有新客户端进来时, 此方法被调用. 如果返回 HANDLE_FAIL, 连接将被关闭. 58 | virtual HandlerState accept(const Session &sess); 59 | // 当客户端被关闭时, 此方法被调用. 60 | virtual HandlerState close(const Session &sess); 61 | 62 | // 当收到客户端的一个请求报文时, 调用此方法. 63 | // 如果有响应需要立即返回给客户端, 将响应加到 resp 中, 并返回 HANDLE_RESP; 64 | virtual HandlerState proc(const Request &req, Response *resp); 65 | 66 | virtual int init(){ return 0; } 67 | virtual int free(){ return 0; } 68 | //virtual void thread(); 69 | 70 | /***** 以下是特殊方法, 你一般不需要关心. *****/ 71 | 72 | // 此方法默认返回异步响应队列的 fd, 你可以重写此方法, 返回你自己的 fd. 73 | virtual int fd(); 74 | 75 | // 当 fd() 有可读事件时, 本函数被调用. 76 | // 如果此方法有响应需要立即返回, 请返回 Response 实例, 外面会负责释放内存. 77 | // 如无响应, 返回 NULL. 78 | virtual Response* handle(); 79 | 80 | protected: 81 | // 将异步响应加入到队列中, 该响应会被发送给客户端. 82 | // 如果 Handler 是多线程的, 可以会调用本方法将响应发给客户端. 83 | void async_send(Response *resp); 84 | 85 | HandlerState ok(){ return HANDLE_OK; }; 86 | HandlerState fail(){ return HANDLE_FAIL; }; 87 | HandlerState resp(){ return HANDLE_RESP; }; 88 | 89 | private: 90 | SelectableQueue *resps; 91 | 92 | int m_init(); 93 | int m_free(); 94 | friend class Server; 95 | }; 96 | 97 | }; // namespace sim 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/decoder.cpp: -------------------------------------------------------------------------------- 1 | #include "util/strings.h" 2 | #include "util/log.h" 3 | #include "sim.h" 4 | 5 | namespace sim{ 6 | 7 | const static int BUF_RESIZE_TRIGGER = 16 * 1024; 8 | 9 | int Decoder::push(const char *buf, int len){ 10 | buffer.append(buf, len); 11 | //log_debug("'%s'", str_escape(buffer).c_str()); 12 | return len; 13 | } 14 | 15 | int Decoder::parse(Message *msg){ 16 | msg->reset(); 17 | 18 | if(buffer_offset >= BUF_RESIZE_TRIGGER){ 19 | //log_debug("resize buffer"); 20 | buffer = std::string(buffer.data() + buffer_offset, buffer.size() - buffer_offset); 21 | buffer_offset = 0; 22 | } 23 | 24 | while(buffer.size() > buffer_offset && isspace(buffer[buffer_offset])){ 25 | buffer_offset ++; 26 | } 27 | if(buffer.size() == buffer_offset){ 28 | return 0; 29 | } 30 | 31 | const char *key = buffer.data() + buffer_offset; 32 | const char *msg_end = (const char *)memchr(key, sim::MSG_END_BYTE, buffer.size() - buffer_offset); 33 | if(!msg_end){ 34 | return 0; 35 | } 36 | int msg_len = msg_end - key + 1; 37 | int size = msg_len; 38 | 39 | int auto_tag = 0; 40 | while(1){ 41 | int key_len = 0; 42 | int val_len; 43 | int tag; 44 | 45 | const char *end; 46 | end = (const char *)memchr(key, sim::KV_END_BYTE, size); 47 | // 兼容最后一个 空格 被省略的情况 48 | if(end == NULL){ 49 | end = msg_end; 50 | } 51 | 52 | const char *val = (const char *)memchr(key, sim::KV_SEP_BYTE, end - key); 53 | if(val == NULL){ 54 | val = key; 55 | tag = auto_tag; 56 | }else{ 57 | val ++; 58 | key_len = val - key - 1; 59 | size -= key_len + 1; 60 | std::string key_s(key, key_len); 61 | tag = str_to_int(key_s); 62 | } 63 | 64 | val_len = end - val; 65 | size -= val_len + 1; 66 | 67 | if(val_len > 0 && val[val_len - 1] == '\r'){ 68 | val_len -= 1; 69 | } 70 | 71 | //printf("%u key: %u, val: %u\n", __LINE__, key_len, val_len); 72 | 73 | std::string val_s(val, val_len); 74 | msg->set(tag, val_s); 75 | 76 | key = end + 1; 77 | auto_tag = tag + 1; 78 | 79 | if(key >= msg_end){ 80 | std::map::iterator it; 81 | for(it=msg->fields_.begin(); it!=msg->fields_.end(); it++){ 82 | it->second = sim::decode(it->second); 83 | } 84 | buffer_offset += msg_len; 85 | //log_debug("msg.len: %d, buffer.len: %d", msg_len, buffer.size()); 86 | return 1; 87 | } 88 | } 89 | return 0; 90 | } 91 | 92 | }; // namespace sim 93 | -------------------------------------------------------------------------------- /src/util/ip_filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef ICOMET_IPFILTER_H 7 | #define ICOMET_IPFILTER_H 8 | 9 | #include 10 | #include 11 | 12 | // filter ip address 13 | class IpFilter{ 14 | private: 15 | bool deny_all; 16 | bool allow_all; 17 | bool empty_; 18 | std::set deny; 19 | std::set allow; 20 | 21 | bool check_hit(const std::set &m, const std::string &ip){ 22 | if(m.empty()){ 23 | return false; 24 | } 25 | std::set::const_iterator it; 26 | it = m.upper_bound(ip); 27 | if(it == m.end()){ 28 | return false; 29 | } 30 | const std::string &prefix = *it; 31 | 32 | int len = prefix.size() - 1; 33 | if(prefix[len] == '='){ 34 | return prefix.compare(0, len, ip) == 0; 35 | }else if(ip.size() > len){ 36 | return ip.compare(0, len, prefix, 0, len) == 0; 37 | } 38 | return false; 39 | } 40 | 41 | bool is_full_ip(const std::string &ip_prefix){ 42 | int n = 0; 43 | for(int i=0; i<(int)ip_prefix.size(); i++){ 44 | if(ip_prefix[i] == '.'){ 45 | n ++; 46 | } 47 | } 48 | return n == 3; 49 | } 50 | 51 | public: 52 | 53 | IpFilter(){ 54 | deny_all = false; 55 | allow_all = false; 56 | empty_ = true; 57 | } 58 | 59 | bool empty(){ 60 | return empty_; 61 | } 62 | 63 | void add_allow(const std::string &ip_prefix){ 64 | if(ip_prefix == "all" || ip_prefix == "*"){ 65 | allow_all = true; 66 | }else{ 67 | // '@' and '=' is greater than any char in ip 68 | std::string prefix = ip_prefix + (is_full_ip(ip_prefix)? "=" : "@"); 69 | allow.insert(prefix); 70 | } 71 | empty_ = false; 72 | } 73 | 74 | void add_deny(const std::string &ip_prefix){ 75 | if(ip_prefix == "all" || ip_prefix == "*"){ 76 | deny_all = true; 77 | }else{ 78 | // '@' and '=' is greater than any char in ip 79 | std::string prefix = ip_prefix + (is_full_ip(ip_prefix)? "=" : "@"); 80 | deny.insert(prefix); 81 | } 82 | empty_ = false; 83 | } 84 | 85 | bool check_pass(const std::string &ip){ 86 | if(empty_){ 87 | return true; 88 | } 89 | // check specified allow/deny 90 | if(check_hit(allow, ip)){ 91 | return true; 92 | } 93 | if(check_hit(deny, ip)){ 94 | return false; 95 | } 96 | if(deny_all){ 97 | return false; 98 | } 99 | if(allow_all){ 100 | return true; 101 | } 102 | return false; 103 | } 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/util/thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef SIM_UTIL_THREAD_H_ 7 | #define SIM_UTIL_THREAD_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | class Mutex{ 19 | private: 20 | pthread_mutex_t mutex; 21 | public: 22 | Mutex(){ 23 | pthread_mutex_init(&mutex, NULL); 24 | } 25 | ~Mutex(){ 26 | pthread_mutex_destroy(&mutex); 27 | } 28 | void lock(){ 29 | pthread_mutex_lock(&mutex); 30 | } 31 | void unlock(){ 32 | pthread_mutex_unlock(&mutex); 33 | } 34 | }; 35 | 36 | class Locking{ 37 | private: 38 | Mutex *mutex; 39 | // No copying allowed 40 | Locking(const Locking&); 41 | void operator=(const Locking&); 42 | public: 43 | Locking(Mutex *mutex){ 44 | this->mutex = mutex; 45 | this->mutex->lock(); 46 | } 47 | ~Locking(){ 48 | this->mutex->unlock(); 49 | } 50 | 51 | }; 52 | 53 | // Selectable queue, multi writers, single reader 54 | template 55 | class SelectableQueue{ 56 | private: 57 | int fds[2]; 58 | public: 59 | Mutex mutex; 60 | std::queue items; 61 | 62 | SelectableQueue(); 63 | ~SelectableQueue(); 64 | int fd(){ 65 | return fds[0]; 66 | } 67 | int size(); 68 | // multi writer 69 | int push(const T item); 70 | // single reader 71 | int pop(T *data); 72 | }; 73 | 74 | 75 | template 76 | SelectableQueue::SelectableQueue(){ 77 | if(pipe(fds) == -1){ 78 | exit(0); 79 | } 80 | } 81 | 82 | template 83 | SelectableQueue::~SelectableQueue(){ 84 | close(fds[0]); 85 | close(fds[1]); 86 | } 87 | 88 | template 89 | int SelectableQueue::size(){ 90 | Locking l(&mutex); 91 | return items.size(); 92 | } 93 | 94 | template 95 | int SelectableQueue::push(const T item){ 96 | Locking l(&mutex); 97 | items.push(item); 98 | if(::write(fds[1], "1", 1) == -1){ 99 | exit(0); 100 | } 101 | return 1; 102 | } 103 | 104 | template 105 | int SelectableQueue::pop(T *data){ 106 | int n, ret = 1; 107 | char buf[1]; 108 | 109 | while(1){ 110 | n = ::read(fds[0], buf, 1); 111 | if(n < 0){ 112 | if(errno == EINTR){ 113 | continue; 114 | }else{ 115 | return -1; 116 | } 117 | }else if(n == 0){ 118 | ret = -1; 119 | }else{ 120 | Locking l(&mutex); 121 | if(items.empty()){ 122 | fprintf(stderr, "%s %d error!\n", __FILE__, __LINE__); 123 | return -1; 124 | } 125 | *data = items.front(); 126 | items.pop(); 127 | } 128 | break; 129 | } 130 | return ret; 131 | } 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /test/bench_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | 5 | int main(int argc, char **argv){ 6 | set_log_level("info"); 7 | 8 | sim::Link *link = sim::Link::connect("127.0.0.1", 8800); 9 | if(!link){ 10 | log_fatal(""); 11 | exit(0); 12 | } 13 | 14 | sim::Fdevents *fdes; 15 | fdes = new sim::Fdevents(); 16 | 17 | fdes->set(link->fd(), sim::FDEVENT_IN|sim::FDEVENT_OUT, 0, link); 18 | 19 | sim::Message msg; 20 | msg.add("ping"); 21 | msg.add("hello world!"); 22 | std::string msg_str = msg.encode(); 23 | 24 | int total_reqs = 0; 25 | if(argc > 1){ 26 | total_reqs = atoi(argv[1]); 27 | } 28 | total_reqs = total_reqs > 0? total_reqs : 1000; 29 | 30 | int sent_reqs = 0; 31 | int recv_reqs = 0; 32 | 33 | double stime = sim::microtime(); 34 | 35 | while(1){ 36 | const sim::Fdevents::events_t *events; 37 | events = fdes->wait(50); 38 | if(events == NULL){ 39 | log_error("events.wait error: %s", strerror(errno)); 40 | break; 41 | } 42 | 43 | for(int i=0; i<(int)events->size(); i++){ 44 | const sim::Fdevent *fde = events->at(i); 45 | sim::Link *link = (sim::Link *)fde->data.ptr; 46 | if(fde->readable()){ 47 | int ret; 48 | ret = link->read(); 49 | log_debug("read %d", ret); 50 | if(ret == 0){ 51 | delete link; 52 | log_error("link closed."); 53 | exit(0); 54 | } 55 | while(1){ 56 | sim::Message msg; 57 | ret = link->recv(&msg); 58 | if(ret == -1){ 59 | delete link; 60 | log_error("parse error!"); 61 | exit(0); 62 | }else if(ret == 0){ 63 | break; 64 | } 65 | recv_reqs ++; 66 | if(recv_reqs % 1000 == 1 || recv_reqs == total_reqs){ 67 | log_debug("received %d message(s)", recv_reqs); 68 | } 69 | //printf("recv: %s", msg.encode().c_str()); 70 | if(recv_reqs == total_reqs){ 71 | double etime = sim::microtime(); 72 | double ts = etime - stime; 73 | int qps = total_reqs / ts; 74 | log_info("recv all, time: %.2f ms, %d qps", 1000*ts, qps); 75 | exit(0); 76 | } 77 | } 78 | } 79 | if(fde->writable()){ 80 | sent_reqs ++; 81 | if(sent_reqs <= total_reqs){ 82 | //log_debug("send %d", sent_reqs); 83 | link->output.append(msg_str); 84 | if(sent_reqs % 1000 == 1 || sent_reqs == total_reqs){ 85 | log_debug("sent %d message(s)", sent_reqs); 86 | } 87 | } 88 | if(link->output.empty()){ 89 | double etime = sim::microtime(); 90 | double ts = etime - stime; 91 | int qps = total_reqs / ts; 92 | log_info("sent all, time: %.2f s, %d qps", 1000*ts, qps); 93 | fdes->clr(link->fd(), sim::FDEVENT_OUT); 94 | } 95 | link->write(); 96 | //log_debug("write %d", ret); 97 | } 98 | } 99 | } 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /src/util/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef SIM_UTIL__CONFIG_H 7 | #define SIM_UTIL__CONFIG_H 8 | 9 | /* 10 | 语法定义: 11 | 空白字符为 '\t \r\n'(制表符, 空格, 回车, 换行) 12 | 忽略只包含空白字符的行 13 | 有效行以 '\t*' 开头 14 | 注释行以 '\t*#' 开头 15 | key 和 value 之间可以用等号'='或者冒号':'分隔 16 | key 不包含任何空白字符, 两端的空白字符被忽略 17 | value 两端的空白字符被忽略 18 | 配置项可以有包含关系, 用一个 TAB 缩进表示父子关系 19 | 20 | 配置读取: 21 | 用键名获取子配置项 22 | 用斜杠'/'或者句号'.'分隔的配置项路径获取配置项 23 | 把配置项的值作为整形(int)返回 24 | 把配置项的值作为字符串(char *)返回 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | namespace sim{ 32 | 33 | /* special filenames: stdin, stdout, stderr */ 34 | class Config{ 35 | private: 36 | Config *parent; 37 | int depth; 38 | 39 | Config* build_key_path(const char *key); 40 | Config* add_child(const char *key, const char *val="", int lineno=0); 41 | const Config* find_child(const char *key) const; 42 | public: 43 | Config(const char *key=NULL, const char *val=NULL); 44 | ~Config(); 45 | 46 | static Config* load(const char *filename); 47 | int save(FILE *fp) const; 48 | int save(const char *filename) const; 49 | 50 | std::vector children; 51 | std::string key; 52 | std::string val; 53 | 54 | Config* set(const char *key, const char *val); 55 | const Config* get(const char *key) const; 56 | int num() const; 57 | int get_num(const char *key) const; 58 | int64_t get_int64(const char *key) const; 59 | const char* str() const; 60 | const char* get_str(const char *key) const; 61 | 62 | bool is_comment() const{ 63 | return key[0] == '#'; 64 | } 65 | std::string ToString() const{ 66 | return key + ": " + val; 67 | } 68 | }; 69 | 70 | /* 71 | 配置文件示例: 72 | 73 | # this is a comment 74 | 75 | author : ideawu 76 | url: http://www.ideawu.net 77 | 78 | proxy : 79 | php = 80 | host = 127.0.0.1 81 | port = 8088 82 | py : 83 | host = 127.0.0.1 84 | port = 8080 85 | 86 | cgi = 87 | pl = /usr/bin/perl 88 | 89 | 应用程序示例: 90 | 91 | #include 92 | #include "config.h" 93 | 94 | int main(int argc, char **argv){ 95 | struct config *cfg, *c; 96 | 97 | cfg = cfg_load_file("cfg_test.conf"); 98 | if(!cfg){ 99 | return 0; 100 | } 101 | 102 | printf("\n"); 103 | printf("proxy.php.host = %s\n", cfg_getstr(cfg, "proxy.php.host")); 104 | printf("proxy.php.port = %d\n", cfg_getnum(cfg, "proxy.php.port")); 105 | printf("cgi.pl = %s\n", cfg_getstr(cfg, "cgi.pl")); 106 | printf("\n"); 107 | 108 | c = cfg_get(cfg, "author"); 109 | printf("author: %s\n", cfg_str(c)); 110 | printf("url: %s\n", cfg_getstr(c, "url")); 111 | printf("\n"); 112 | 113 | cfg_free(cfg); 114 | return 0; 115 | } 116 | 117 | */ 118 | 119 | 120 | }; // namespace sim 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /src/sim.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sim.h" 3 | 4 | namespace sim{ 5 | 6 | std::string version(){ 7 | return "1.0"; 8 | } 9 | 10 | std::string encode(const std::string s, bool force_ascii){ 11 | std::string ret; 12 | int size = (int)s.size(); 13 | for(int i=0; i= '!' && c <= '~'){ 53 | ret.push_back(c); 54 | }else{ 55 | ret.append("\\x"); 56 | unsigned char d = c; 57 | ret.push_back(hex[d >> 4]); 58 | ret.push_back(hex[d & 0x0f]); 59 | } 60 | } 61 | break; 62 | } 63 | } 64 | return ret; 65 | } 66 | 67 | inline static 68 | int hex_int(char c){ 69 | if(c >= '0' && c <= '9'){ 70 | return c - '0'; 71 | }else{ 72 | return c - 'a' + 10; 73 | } 74 | } 75 | 76 | std::string decode(const std::string s){ 77 | int size = (int)s.size(); 78 | std::string ret; 79 | for(int i=0; i= size - 1){ 86 | break; 87 | } 88 | char c2 = s[++i]; 89 | switch(c2){ 90 | case 's': 91 | ret.push_back(' '); 92 | break; 93 | case '\\': 94 | ret.push_back('\\'); 95 | break; 96 | case 'a': 97 | ret.push_back('\a'); 98 | break; 99 | case 'b': 100 | ret.push_back('\b'); 101 | break; 102 | case 'f': 103 | ret.push_back('\f'); 104 | break; 105 | case 'v': 106 | ret.push_back('\v'); 107 | break; 108 | case 'r': 109 | ret.push_back('\r'); 110 | break; 111 | case 'n': 112 | ret.push_back('\n'); 113 | break; 114 | case 't': 115 | ret.push_back('\t'); 116 | break; 117 | case '0': 118 | ret.push_back('\0'); 119 | break; 120 | case 'x': 121 | if(i < size - 2){ 122 | char c3 = s[++i]; 123 | char c4 = s[++i]; 124 | ret.push_back((char)((hex_int(c3) << 4) + hex_int(c4))); 125 | } 126 | break; 127 | default: 128 | ret.push_back(c2); 129 | break; 130 | } 131 | } 132 | return ret; 133 | } 134 | 135 | }; // namespace sim 136 | 137 | -------------------------------------------------------------------------------- /src/fde_select.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef UTIL_FDE_SELECT_H 7 | #define UTIL_FDE_SELECT_H 8 | 9 | namespace sim{ 10 | 11 | Fdevents::Fdevents(){ 12 | maxfd = -1; 13 | FD_ZERO(&readset); 14 | FD_ZERO(&writeset); 15 | } 16 | 17 | Fdevents::~Fdevents(){ 18 | for(size_t i=0; is_flags & flag); 28 | } 29 | 30 | int Fdevents::set(int fd, int flags, int data_num, void *data_ptr){ 31 | if(fd > FD_SETSIZE - 1){ 32 | return -1; 33 | } 34 | 35 | struct Fdevent *fde = get_fde(fd); 36 | if(fde->s_flags & flags){ 37 | return 0; 38 | } 39 | 40 | if(flags & FDEVENT_IN) FD_SET(fd, &readset); 41 | if(flags & FDEVENT_OUT) FD_SET(fd, &writeset); 42 | 43 | fde->data.num = data_num; 44 | fde->data.ptr = data_ptr; 45 | fde->s_flags |= flags; 46 | maxfd = fd > maxfd? fd: maxfd; 47 | 48 | return 0; 49 | } 50 | 51 | int Fdevents::del(int fd){ 52 | FD_CLR(fd, &readset); 53 | FD_CLR(fd, &writeset); 54 | 55 | struct Fdevent *fde = get_fde(fd); 56 | fde->s_flags = FDEVENT_NONE; 57 | while(maxfd >= 0 && this->events[maxfd]->s_flags == 0){ 58 | maxfd --; 59 | } 60 | return 0; 61 | } 62 | 63 | int Fdevents::clr(int fd, int flags){ 64 | struct Fdevent *fde = get_fde(fd); 65 | if(!(fde->s_flags & flags)){ 66 | return 0; 67 | } 68 | if(flags & FDEVENT_IN) FD_CLR(fd, &readset); 69 | if(flags & FDEVENT_OUT) FD_CLR(fd, &writeset); 70 | 71 | fde->s_flags &= ~flags; 72 | while(this->events[maxfd]->s_flags == 0){ 73 | maxfd --; 74 | } 75 | return 0; 76 | } 77 | 78 | const Fdevents::events_t* Fdevents::wait(int timeout_ms){ 79 | struct timeval tv; 80 | struct Fdevent *fde; 81 | int i, ret; 82 | 83 | ready_events.clear(); 84 | 85 | fd_set t_readset = readset; 86 | fd_set t_writeset = writeset; 87 | 88 | if(timeout_ms >= 0){ 89 | tv.tv_sec = timeout_ms / 1000; 90 | tv.tv_usec = (timeout_ms % 1000) * 1000; 91 | ret = ::select(maxfd + 1, &t_readset, &t_writeset, NULL, &tv); 92 | }else{ 93 | ret = ::select(maxfd + 1, &t_readset, &t_writeset, NULL, NULL); 94 | } 95 | if(ret < 0){ 96 | if(errno == EINTR){ 97 | return &ready_events; 98 | } 99 | return NULL; 100 | } 101 | 102 | if(ret > 0){ 103 | for(i = 0; i <= maxfd && (int)ready_events.size() < ret; i++){ 104 | fde = this->events[i]; 105 | 106 | fde->events = FDEVENT_NONE; 107 | if(FD_ISSET(i, &t_readset)) fde->events |= FDEVENT_IN; 108 | if(FD_ISSET(i, &t_writeset)) fde->events |= FDEVENT_OUT; 109 | 110 | if(fde->events){ 111 | ready_events.push_back(fde); 112 | } 113 | } 114 | } 115 | 116 | return &ready_events; 117 | } 118 | 119 | 120 | }; // namespace sim 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /src/fde_epoll.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef UTIL_FDE_EPOLL_H 7 | #define UTIL_FDE_EPOLL_H 8 | 9 | namespace sim{ 10 | 11 | Fdevents::Fdevents(){ 12 | ep_fd = epoll_create(1024); 13 | } 14 | 15 | Fdevents::~Fdevents(){ 16 | for(int i=0; i<(int)events.size(); i++){ 17 | delete events[i]; 18 | } 19 | if(ep_fd){ 20 | ::close(ep_fd); 21 | } 22 | events.clear(); 23 | ready_events.clear(); 24 | } 25 | 26 | bool Fdevents::isset(int fd, int flag){ 27 | struct Fdevent *fde = get_fde(fd); 28 | return (bool)(fde->s_flags & flag); 29 | } 30 | 31 | int Fdevents::set(int fd, int flags, int data_num, void *data_ptr){ 32 | struct Fdevent *fde = get_fde(fd); 33 | if(fde->s_flags & flags){ 34 | return 0; 35 | } 36 | int ctl_op = fde->s_flags? EPOLL_CTL_MOD : EPOLL_CTL_ADD; 37 | 38 | fde->s_flags |= flags; 39 | fde->data.num = data_num; 40 | fde->data.ptr = data_ptr; 41 | 42 | struct epoll_event epe; 43 | epe.data.ptr = fde; 44 | epe.events = 0; 45 | if(fde->s_flags & FDEVENT_IN) epe.events |= EPOLLIN; 46 | if(fde->s_flags & FDEVENT_OUT) epe.events |= EPOLLOUT; 47 | 48 | int ret = epoll_ctl(ep_fd, ctl_op, fd, &epe); 49 | if(ret == -1){ 50 | return -1; 51 | } 52 | return 0; 53 | } 54 | 55 | int Fdevents::del(int fd){ 56 | struct epoll_event epe; 57 | int ret = epoll_ctl(ep_fd, EPOLL_CTL_DEL, fd, &epe); 58 | if(ret == -1){ 59 | return -1; 60 | } 61 | 62 | struct Fdevent *fde = get_fde(fd); 63 | fde->s_flags = FDEVENT_NONE; 64 | return 0; 65 | } 66 | 67 | int Fdevents::clr(int fd, int flags){ 68 | struct Fdevent *fde = get_fde(fd); 69 | if(!(fde->s_flags & flags)){ 70 | return 0; 71 | } 72 | 73 | fde->s_flags &= ~flags; 74 | int ctl_op = fde->s_flags? EPOLL_CTL_MOD: EPOLL_CTL_DEL; 75 | 76 | struct epoll_event epe; 77 | epe.data.ptr = fde; 78 | epe.events = 0; 79 | if(fde->s_flags & FDEVENT_IN) epe.events |= EPOLLIN; 80 | if(fde->s_flags & FDEVENT_OUT) epe.events |= EPOLLOUT; 81 | 82 | int ret = epoll_ctl(ep_fd, ctl_op, fd, &epe); 83 | if(ret == -1){ 84 | return -1; 85 | } 86 | return 0; 87 | } 88 | 89 | const Fdevents::events_t* Fdevents::wait(int timeout_ms){ 90 | struct Fdevent *fde; 91 | struct epoll_event *epe; 92 | ready_events.clear(); 93 | 94 | int nfds = epoll_wait(ep_fd, ep_events, MAX_FDS, timeout_ms); 95 | if(nfds == -1){ 96 | if(errno == EINTR){ 97 | return &ready_events; 98 | } 99 | return NULL; 100 | } 101 | 102 | for(int i = 0; i < nfds; i++){ 103 | epe = &ep_events[i]; 104 | fde = (struct Fdevent *)epe->data.ptr; 105 | 106 | fde->events = FDEVENT_NONE; 107 | if(epe->events & EPOLLIN) fde->events |= FDEVENT_IN; 108 | if(epe->events & EPOLLPRI) fde->events |= FDEVENT_IN; 109 | if(epe->events & EPOLLOUT) fde->events |= FDEVENT_OUT; 110 | if(epe->events & EPOLLHUP) fde->events |= FDEVENT_ERR; 111 | if(epe->events & EPOLLERR) fde->events |= FDEVENT_ERR; 112 | 113 | ready_events.push_back(fde); 114 | } 115 | return &ready_events; 116 | } 117 | 118 | 119 | }; // namespace sim 120 | 121 | #endif 122 | 123 | -------------------------------------------------------------------------------- /test/test_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sim/sim.h" 4 | #include "sim/util/strings.h" 5 | #include "sim/util/thread.h" 6 | 7 | class ThreadHandler : public sim::Handler 8 | { 9 | public: 10 | ThreadHandler(); 11 | // virtual sim::HandlerState accept(const sim::Session &sess); 12 | virtual sim::HandlerState close(const sim::Session &sess); 13 | virtual sim::HandlerState proc(const sim::Request &req, sim::Response *resp); 14 | private: 15 | Mutex mutex; 16 | static void* _run_thread(void *arg); 17 | std::map sessions; 18 | }; 19 | 20 | //sim::HandlerState ThreadHandler::accept(const sim::Session &sess){ 21 | // return ok(); 22 | //} 23 | 24 | sim::HandlerState ThreadHandler::close(const sim::Session &sess){ 25 | Locking l(&mutex); 26 | sessions.erase(sess.id); 27 | return ok(); 28 | } 29 | 30 | ThreadHandler::ThreadHandler(){ 31 | pthread_t tid; 32 | int err = pthread_create(&tid, NULL, &ThreadHandler::_run_thread, this); 33 | if(err != 0){ 34 | log_error("can't create thread: %s", strerror(err)); 35 | } 36 | } 37 | 38 | void* ThreadHandler::_run_thread(void *arg){ 39 | ThreadHandler *handler = (ThreadHandler *)arg; 40 | int num = 0; 41 | while(1){ 42 | sleep(2); 43 | 44 | std::map sessions; 45 | { 46 | Locking l(&handler->mutex); 47 | sessions = handler->sessions; 48 | } 49 | std::map::iterator it; 50 | for(it=sessions.begin(); it!=sessions.end(); it++){ 51 | sim::Response *resp = new sim::Response(); 52 | resp->sess = it->second; 53 | resp->msg.add("timer"); 54 | resp->msg.add(str(num)); 55 | handler->async_send(resp); 56 | } 57 | 58 | num ++; 59 | } 60 | return NULL; 61 | } 62 | 63 | sim::HandlerState ThreadHandler::proc(const sim::Request &req, sim::Response *resp){ 64 | std::string cmd = req.msg.type(); 65 | 66 | if(cmd == "ping"){ 67 | resp->msg.add("ok"); 68 | resp->msg.add("pong"); 69 | }else if(cmd == "subscribe"){ 70 | const std::string *token = req.msg.get(1); 71 | if(token && *token == "123456"){ 72 | resp->msg.add("ok"); 73 | resp->msg.add("subscibe successful."); 74 | 75 | Locking l(&mutex); 76 | sessions[req.sess.id] = req.sess; 77 | }else{ 78 | resp->msg.add("error"); 79 | resp->msg.add("invalid token!"); 80 | } 81 | }else if(cmd == "echo"){ 82 | resp->msg.add("ok"); 83 | const std::map *fields = req.msg.fields(); 84 | std::map::const_iterator it; 85 | for(it=fields->begin(); it!=fields->end(); it++){ 86 | int tag = it->first; 87 | if(tag == 0){ 88 | continue; 89 | } 90 | resp->msg.set(tag, it->second); 91 | } 92 | }else if(cmd == "send"){ 93 | std::map sessions; 94 | { 95 | Locking l(&this->mutex); 96 | sessions = this->sessions; 97 | } 98 | std::map::iterator it; 99 | for(it=sessions.begin(); it!=sessions.end(); it++){ 100 | sim::Response *resp = new sim::Response(); 101 | resp->sess = it->second; 102 | resp->msg = req.msg; 103 | resp->msg.set_type("msg"); 104 | this->async_send(resp); 105 | } 106 | resp->msg.add("ok"); 107 | }else{ 108 | resp->msg.add("client_error"); 109 | resp->msg.add("unknown command!"); 110 | } 111 | 112 | return this->resp(); 113 | } 114 | 115 | 116 | 117 | int main(int argc, char **argv){ 118 | //set_log_level("info"); 119 | 120 | const char *ip = "127.0.0.1"; 121 | int port = 8800; 122 | sim::Server *serv = sim::Server::listen(ip, port); 123 | if(!serv){ 124 | log_fatal("%s", strerror(errno)); 125 | exit(0); 126 | } 127 | log_info("server listen on %s:%d", ip, port); 128 | 129 | ThreadHandler handler; 130 | serv->add_handler(&handler); 131 | 132 | serv->loop(); 133 | 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /src/util/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef SIM_UTIL_LOG_H 7 | #define SIM_UTIL_LOG_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | class Logger{ 30 | public: 31 | static const int LEVEL_NONE = (-1); 32 | static const int LEVEL_MIN = 0; 33 | static const int LEVEL_FATAL = 0; 34 | static const int LEVEL_ERROR = 1; 35 | static const int LEVEL_WARN = 2; 36 | static const int LEVEL_INFO = 3; 37 | static const int LEVEL_DEBUG = 4; 38 | static const int LEVEL_TRACE = 5; 39 | static const int LEVEL_MAX = 5; 40 | 41 | static int get_level(const char *levelname); 42 | 43 | static Logger* shared(); 44 | 45 | std::string level_name(); 46 | std::string output_name(); 47 | uint64_t rotate_size(); 48 | private: 49 | FILE *fp; 50 | char filename[PATH_MAX]; 51 | int level_; 52 | pthread_mutex_t *mutex; 53 | 54 | uint64_t rotate_size_; 55 | struct{ 56 | uint64_t w_curr; 57 | uint64_t w_total; 58 | }stats; 59 | 60 | void rotate(); 61 | void threadsafe(); 62 | public: 63 | Logger(); 64 | ~Logger(); 65 | 66 | int level(){ 67 | return level_; 68 | } 69 | 70 | void set_level(int level){ 71 | this->level_ = level; 72 | } 73 | 74 | int open(FILE *fp, int level=LEVEL_DEBUG, bool is_threadsafe=false); 75 | int open(const char *filename, int level=LEVEL_DEBUG, 76 | bool is_threadsafe=false, uint64_t rotate_size=0); 77 | void close(); 78 | 79 | int logv(int level, const char *fmt, va_list ap); 80 | 81 | int trace(const char *fmt, ...); 82 | int debug(const char *fmt, ...); 83 | int info(const char *fmt, ...); 84 | int warn(const char *fmt, ...); 85 | int error(const char *fmt, ...); 86 | int fatal(const char *fmt, ...); 87 | }; 88 | 89 | 90 | int log_open(FILE *fp, int level=Logger::LEVEL_DEBUG, bool is_threadsafe=false); 91 | int log_open(const char *filename, int level=Logger::LEVEL_DEBUG, 92 | bool is_threadsafe=false, uint64_t rotate_size=0); 93 | int log_level(); 94 | void set_log_level(int level); 95 | void set_log_level(const char *s); 96 | int log_write(int level, const char *fmt, ...); 97 | 98 | 99 | #ifndef IOS 100 | #ifdef NDEBUG 101 | #define log_trace(fmt, args...) do{}while(0) 102 | #else 103 | #define log_trace(fmt, args...) \ 104 | log_write(Logger::LEVEL_TRACE, "%s(%d): " fmt, __FILE__, __LINE__, ##args) 105 | #endif 106 | 107 | #define log_debug(fmt, args...) \ 108 | log_write(Logger::LEVEL_DEBUG, "%s(%d): " fmt, __FILE__, __LINE__, ##args) 109 | #define log_info(fmt, args...) \ 110 | log_write(Logger::LEVEL_INFO, "%s(%d): " fmt, __FILE__, __LINE__, ##args) 111 | #define log_warn(fmt, args...) \ 112 | log_write(Logger::LEVEL_WARN, "%s(%d): " fmt, __FILE__, __LINE__, ##args) 113 | #define log_error(fmt, args...) \ 114 | log_write(Logger::LEVEL_ERROR, "%s(%d): " fmt, __FILE__, __LINE__, ##args) 115 | #define log_fatal(fmt, args...) \ 116 | log_write(Logger::LEVEL_FATAL, "%s(%d): " fmt, __FILE__, __LINE__, ##args) 117 | #else 118 | #define log_trace(fmt, args...) do{}while(0) 119 | #define log_debug(fmt, args...) do{}while(0) 120 | #define log_info(fmt, args...) do{}while(0) 121 | #define log_warn(fmt, args...) do{}while(0) 122 | #define log_error(fmt, args...) do{}while(0) 123 | #define log_fatal(fmt, args...) do{}while(0) 124 | #endif 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/link.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "util/log.h" 8 | #include "link.h" 9 | 10 | namespace sim{ 11 | 12 | Link::Link(bool is_server){ 13 | sock = -1; 14 | noblock_ = false; 15 | error_ = false; 16 | remote_ip[0] = '\0'; 17 | remote_port = -1; 18 | } 19 | 20 | Link::~Link(){ 21 | this->close(); 22 | } 23 | 24 | void Link::close(){ 25 | if(sock >= 0){ 26 | ::close(sock); 27 | sock = -1; 28 | } 29 | } 30 | 31 | void Link::nodelay(bool enable){ 32 | int opt = enable? 1 : 0; 33 | ::setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)); 34 | } 35 | 36 | void Link::keepalive(bool enable){ 37 | int opt = enable? 1 : 0; 38 | ::setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&opt, sizeof(opt)); 39 | } 40 | 41 | void Link::noblock(bool enable){ 42 | noblock_ = enable; 43 | if(enable){ 44 | ::fcntl(sock, F_SETFL, O_NONBLOCK | O_RDWR); 45 | }else{ 46 | ::fcntl(sock, F_SETFL, O_RDWR); 47 | } 48 | } 49 | 50 | Link* Link::connect(const std::string &ip, int port){ 51 | return connect(ip.c_str(), port); 52 | } 53 | 54 | Link* Link::connect(const char *ip, int port){ 55 | Link *link; 56 | int sock = -1; 57 | 58 | struct sockaddr_in addr; 59 | bzero(&addr, sizeof(addr)); 60 | addr.sin_family = AF_INET; 61 | addr.sin_port = htons((short)port); 62 | inet_pton(AF_INET, ip, &addr.sin_addr); 63 | 64 | if((sock = ::socket(AF_INET, SOCK_STREAM, 0)) == -1){ 65 | goto sock_err; 66 | } 67 | if(::connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1){ 68 | goto sock_err; 69 | } 70 | 71 | //log_debug("fd: %d, connect to %s:%d", sock, ip, port); 72 | link = new Link(); 73 | link->sock = sock; 74 | link->keepalive(true); 75 | return link; 76 | sock_err: 77 | //log_debug("connect to %s:%d failed: %s", ip, port, strerror(errno)); 78 | if(sock >= 0){ 79 | ::close(sock); 80 | } 81 | return NULL; 82 | } 83 | 84 | Link* Link::listen(const std::string &ip, int port){ 85 | return listen(ip.c_str(), port); 86 | } 87 | 88 | Link* Link::listen(const char *ip, int port){ 89 | Link *link; 90 | int sock = -1; 91 | 92 | int opt = 1; 93 | struct sockaddr_in addr; 94 | bzero(&addr, sizeof(addr)); 95 | addr.sin_family = AF_INET; 96 | addr.sin_port = htons((short)port); 97 | inet_pton(AF_INET, ip, &addr.sin_addr); 98 | 99 | if((sock = ::socket(AF_INET, SOCK_STREAM, 0)) == -1){ 100 | goto sock_err; 101 | } 102 | if(::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1){ 103 | goto sock_err; 104 | } 105 | if(::bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1){ 106 | goto sock_err; 107 | } 108 | if(::listen(sock, 1024) == -1){ 109 | goto sock_err; 110 | } 111 | //log_debug("server socket fd: %d, listen on: %s:%d", sock, ip, port); 112 | 113 | link = new Link(true); 114 | link->sock = sock; 115 | snprintf(link->remote_ip, sizeof(link->remote_ip), "%s", ip); 116 | link->remote_port = port; 117 | return link; 118 | sock_err: 119 | //log_debug("listen %s:%d failed: %s", ip, port, strerror(errno)); 120 | if(sock >= 0){ 121 | ::close(sock); 122 | } 123 | return NULL; 124 | } 125 | 126 | Link* Link::accept(){ 127 | Link *link; 128 | int client_sock; 129 | struct sockaddr_in addr; 130 | socklen_t addrlen = sizeof(addr); 131 | 132 | while((client_sock = ::accept(sock, (struct sockaddr *)&addr, &addrlen)) == -1){ 133 | if(errno != EINTR){ 134 | //log_error("socket %d accept failed: %s", sock, strerror(errno)); 135 | return NULL; 136 | } 137 | } 138 | 139 | struct linger opt = {1, 0}; 140 | int ret = ::setsockopt(client_sock, SOL_SOCKET, SO_LINGER, (void *)&opt, sizeof(opt)); 141 | if (ret != 0) { 142 | //log_error("socket %d set linger failed: %s", client_sock, strerror(errno)); 143 | } 144 | 145 | link = new Link(); 146 | link->sock = client_sock; 147 | link->keepalive(true); 148 | inet_ntop(AF_INET, &addr.sin_addr, link->remote_ip, sizeof(link->remote_ip)); 149 | link->remote_port = ntohs(addr.sin_port); 150 | return link; 151 | } 152 | 153 | int Link::read(){ 154 | int ret = 0; 155 | char buf[16 * 1024]; 156 | int want = sizeof(buf); 157 | while(1){ 158 | // test 159 | //want = 1; 160 | int len = ::read(sock, buf, want); 161 | if(len == -1){ 162 | if(errno == EINTR){ 163 | continue; 164 | }else if(errno == EWOULDBLOCK){ 165 | break; 166 | }else{ 167 | //log_debug("fd: %d, read: -1, want: %d, error: %s", sock, want, strerror(errno)); 168 | return -1; 169 | } 170 | }else{ 171 | //log_debug("fd: %d, want=%d, read: %d", sock, want, len); 172 | if(len == 0){ 173 | return 0; 174 | } 175 | ret += len; 176 | decoder_.push(buf, len); 177 | } 178 | if(!noblock_){ 179 | break; 180 | } 181 | } 182 | //log_debug("read %d", ret); 183 | return ret; 184 | } 185 | 186 | int Link::write(){ 187 | int ret = 0; 188 | int want; 189 | while((want = output.size()) > 0){ 190 | // test 191 | //want = 1; 192 | int len = ::write(sock, output.data(), want); 193 | if(len == -1){ 194 | if(errno == EINTR){ 195 | continue; 196 | }else if(errno == EWOULDBLOCK){ 197 | break; 198 | }else{ 199 | //log_debug("fd: %d, write: -1, error: %s", sock, strerror(errno)); 200 | return -1; 201 | } 202 | }else{ 203 | //log_info("fd: %d, want: %d, write: %d", sock, want, len); 204 | if(len == 0){ 205 | // ? 206 | break; 207 | } 208 | ret += len; 209 | output = std::string(output.data() + len, output.size() - len); 210 | } 211 | if(!noblock_){ 212 | break; 213 | } 214 | } 215 | return ret; 216 | } 217 | 218 | int Link::flush(){ 219 | int len = 0; 220 | while(!output.empty()){ 221 | int ret = this->write(); 222 | if(ret == -1){ 223 | return -1; 224 | } 225 | len += ret; 226 | } 227 | return len; 228 | } 229 | 230 | int Link::recv(Message *msg){ 231 | return decoder_.parse(msg); 232 | } 233 | 234 | int Link::send(const Message &msg){ 235 | std::string s = msg.encode(); 236 | output.append(s); 237 | return (int)s.size(); 238 | } 239 | 240 | }; // namespace sim 241 | -------------------------------------------------------------------------------- /src/util/app.cpp: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | #include "log.h" 3 | #include "file.h" 4 | #include "config.h" 5 | #include "daemon.h" 6 | #include "strings.h" 7 | #include 8 | 9 | namespace sim{ 10 | 11 | volatile bool quit = false; 12 | 13 | void signal_handler(int sig){ 14 | switch(sig){ 15 | case SIGTERM: 16 | case SIGINT:{ 17 | quit = true; 18 | break; 19 | } 20 | } 21 | } 22 | 23 | int Application::init(){ 24 | return 0; 25 | } 26 | 27 | int Application::free(){ 28 | return 0; 29 | } 30 | 31 | int Application::main(int argc, char **argv){ 32 | signal(SIGINT, signal_handler); 33 | signal(SIGTERM, signal_handler); 34 | 35 | conf = NULL; 36 | 37 | welcome(); 38 | parse_args(argc, argv); 39 | 40 | my_init(); 41 | 42 | log_info("config : %s", this->app_args.conf_file.c_str()); 43 | log_info("pidfile : %s, pid: %d", app_args.pidfile.c_str(), (int)getpid()); 44 | log_info("log_level : %s", Logger::shared()->level_name().c_str()); 45 | log_info("log_output: %s", Logger::shared()->output_name().c_str()); 46 | log_info("log_rotate: %" PRId64, Logger::shared()->rotate_size()); 47 | 48 | if(this->init() == -1){ 49 | return -1; 50 | } 51 | write_pid(); 52 | while(!quit){ 53 | if(this->loop_once() == -1){ 54 | break; 55 | } 56 | } 57 | remove_pidfile(); 58 | this->free(); 59 | 60 | delete conf; 61 | return 0; 62 | } 63 | 64 | void Application::welcome(){ 65 | } 66 | 67 | void Application::usage(int argc, char **argv){ 68 | printf("Usage:\n"); 69 | printf(" %s [-d] /path/to/app.conf [-s start|stop|restart]\n", argv[0]); 70 | printf("Options:\n"); 71 | printf(" -d run as daemon\n"); 72 | printf(" -s option to start|stop|restart the server\n"); 73 | printf(" -h show this message\n"); 74 | } 75 | 76 | void Application::parse_args(int argc, char **argv){ 77 | for(int i=1; i i + 1){ 88 | i ++; 89 | app_args.start_opt = argv[i]; 90 | }else{ 91 | usage(argc, argv); 92 | exit(1); 93 | } 94 | if(app_args.start_opt != "start" && app_args.start_opt != "stop" && app_args.start_opt != "restart"){ 95 | usage(argc, argv); 96 | fprintf(stderr, "Error: bad argument: '%s'\n", app_args.start_opt.c_str()); 97 | exit(1); 98 | } 99 | }else{ 100 | app_args.conf_file = argv[i]; 101 | } 102 | } 103 | 104 | if(app_args.conf_file.empty()){ 105 | usage(argc, argv); 106 | exit(1); 107 | } 108 | } 109 | 110 | void Application::my_init(){ 111 | if(!is_file(app_args.conf_file.c_str())){ 112 | fprintf(stderr, "'%s' is not a file or not exists!\n", app_args.conf_file.c_str()); 113 | exit(1); 114 | } 115 | conf = Config::load(app_args.conf_file.c_str()); 116 | if(!conf){ 117 | fprintf(stderr, "error loading conf file: '%s'\n", app_args.conf_file.c_str()); 118 | exit(1); 119 | } 120 | { 121 | std::string conf_dir = real_dirname(app_args.conf_file.c_str()); 122 | if(chdir(conf_dir.c_str()) == -1){ 123 | fprintf(stderr, "error chdir: %s\n", conf_dir.c_str()); 124 | exit(1); 125 | } 126 | } 127 | 128 | app_args.pidfile = conf->get_str("pidfile"); 129 | 130 | if(app_args.start_opt == "stop"){ 131 | kill_process(); 132 | exit(0); 133 | } 134 | if(app_args.start_opt == "restart"){ 135 | if(file_exists(app_args.pidfile)){ 136 | kill_process(); 137 | } 138 | } 139 | 140 | check_pidfile(); 141 | 142 | { // logger 143 | std::string log_output; 144 | std::string log_level_; 145 | int64_t log_rotate_size; 146 | 147 | log_level_ = conf->get_str("logger.level"); 148 | strtolower(&log_level_); 149 | if(log_level_.empty()){ 150 | log_level_ = "debug"; 151 | } 152 | int level = Logger::get_level(log_level_.c_str()); 153 | log_rotate_size = conf->get_int64("logger.rotate.size"); 154 | log_output = conf->get_str("logger.output"); 155 | if(log_output == ""){ 156 | log_output = "stdout"; 157 | } 158 | if(log_open(log_output.c_str(), level, true, log_rotate_size) == -1){ 159 | fprintf(stderr, "error opening log file: %s\n", log_output.c_str()); 160 | exit(1); 161 | } 162 | } 163 | 164 | app_args.work_dir = conf->get_str("work_dir"); 165 | if(app_args.work_dir.empty()){ 166 | app_args.work_dir = "."; 167 | } 168 | if(!is_dir(app_args.work_dir.c_str())){ 169 | fprintf(stderr, "'%s' is not a directory or not exists!\n", app_args.work_dir.c_str()); 170 | exit(1); 171 | } 172 | 173 | // WARN!!! 174 | // deamonize() MUST be called before any thread is created! 175 | if(app_args.is_daemon){ 176 | daemonize(); 177 | } 178 | } 179 | 180 | int Application::read_pid(){ 181 | if(app_args.pidfile.empty()){ 182 | return -1; 183 | } 184 | std::string s; 185 | file_get_contents(app_args.pidfile, &s); 186 | if(s.empty()){ 187 | return -1; 188 | } 189 | return str_to_int(s); 190 | } 191 | 192 | void Application::write_pid(){ 193 | if(app_args.pidfile.empty()){ 194 | return; 195 | } 196 | int pid = (int)getpid(); 197 | std::string s = str(pid); 198 | int ret = file_put_contents(app_args.pidfile, s); 199 | if(ret == -1){ 200 | log_error("Failed to write pidfile '%s'(%s)", app_args.pidfile.c_str(), strerror(errno)); 201 | exit(1); 202 | } 203 | } 204 | 205 | void Application::check_pidfile(){ 206 | if(app_args.pidfile.size()){ 207 | if(access(app_args.pidfile.c_str(), F_OK) == 0){ 208 | fprintf(stderr, "Fatal error!\nPidfile %s already exists!\n" 209 | "Kill the running process before you run this command,\n" 210 | "or use '-s restart' option to restart the server.\n", 211 | app_args.pidfile.c_str()); 212 | exit(1); 213 | } 214 | } 215 | } 216 | 217 | void Application::remove_pidfile(){ 218 | if(app_args.pidfile.size()){ 219 | remove(app_args.pidfile.c_str()); 220 | } 221 | } 222 | 223 | void Application::kill_process(){ 224 | int pid = read_pid(); 225 | if(pid == -1){ 226 | fprintf(stderr, "could not read pidfile: %s(%s)\n", app_args.pidfile.c_str(), strerror(errno)); 227 | exit(1); 228 | } 229 | if(kill(pid, 0) == -1 && errno == ESRCH){ 230 | fprintf(stderr, "process: %d not running\n", pid); 231 | remove_pidfile(); 232 | return; 233 | } 234 | int ret = kill(pid, SIGTERM); 235 | if(ret == -1){ 236 | fprintf(stderr, "could not kill process: %d(%s)\n", pid, strerror(errno)); 237 | exit(1); 238 | } 239 | 240 | while(file_exists(app_args.pidfile)){ 241 | usleep(100 * 1000); 242 | } 243 | } 244 | 245 | 246 | }; // namespace sim 247 | -------------------------------------------------------------------------------- /src/util/config.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #include "log.h" 7 | #include "config.h" 8 | #include "strings.h" 9 | 10 | #define CONFIG_MAX_LINE 4096 11 | 12 | namespace sim{ 13 | 14 | inline static 15 | int is_kv_seperator(int ch){ 16 | return (ch == '=') || (ch == ':'); 17 | } 18 | 19 | Config* Config::load(const char *filename){ 20 | FILE *fp = NULL; 21 | int lineno = 0; 22 | 23 | if(strcmp(filename, "stdin") == 0){ 24 | fp = stdin; 25 | }else{ 26 | fp = fopen(filename, "r"); 27 | if(!fp){ 28 | log_error("error opening file '%s': %s", filename, strerror(errno)); 29 | return NULL; 30 | } 31 | } 32 | 33 | Config *root = new Config("root", ""); 34 | Config *cfg = root; 35 | int last_indent = 0; 36 | char buf[CONFIG_MAX_LINE]; 37 | while(fgets(buf, sizeof(buf), fp)){ 38 | lineno++; 39 | 40 | buf[strlen(buf) - 1] = '\0'; /* 去除 '\n' */ 41 | if(is_empty_str(buf)){ 42 | continue; 43 | } 44 | 45 | /* 有效行以 \t* 开头 */ 46 | int indent = strspn(buf, "\t"); 47 | char *key = buf + indent; 48 | 49 | if(*key == '#'){ 50 | cfg->add_child("#", key + 1, lineno); 51 | continue; 52 | } 53 | if(indent <= last_indent){ 54 | for(int i = indent; i <= last_indent; i++){ 55 | /* 第一个配置时, 此条件为真 */ 56 | if(cfg != root){ 57 | cfg = cfg->parent; 58 | } 59 | } 60 | }else if(indent > last_indent + 1){ 61 | log_error("invalid indent line(%d)", lineno); 62 | goto err; 63 | } 64 | 65 | if(isspace(*key)){ 66 | log_error("invalid line(%d): unexpected whitespace char '%c'", lineno, *key); 67 | goto err; 68 | } 69 | 70 | char *val = key; 71 | /* 跳过键名 */ 72 | while(*val && !is_kv_seperator(*val)){ 73 | val++; 74 | } 75 | if(*val == '\0'){ 76 | log_error("invalid line(%d): %s, expecting ':' or '='", lineno, *val); 77 | goto err; 78 | }else if(!is_kv_seperator(*val)){ 79 | log_error("invalid line(%d): unexpected char '%c', expecting ':' or '='", lineno, *val); 80 | goto err; 81 | } 82 | *val++ = '\0'; 83 | 84 | /* key 或者 value 的前后空白字符会被过滤 */ 85 | key = trim(key); 86 | val = trim(val); 87 | 88 | cfg = cfg->add_child(key, val, lineno); 89 | if(cfg == NULL){ 90 | goto err; 91 | } 92 | 93 | last_indent = indent; 94 | } 95 | if(ferror(fp)){ 96 | log_error("error while reading file %s", filename); 97 | goto err; 98 | } 99 | fclose(fp); 100 | return root; 101 | err: 102 | if(root){ 103 | delete root; 104 | } 105 | if(fp && fp != stdin){ 106 | fclose(fp); 107 | } 108 | return NULL; 109 | } 110 | 111 | Config::Config(const char *key, const char *val){ 112 | this->parent = NULL; 113 | this->depth = 0; 114 | if(key){ 115 | this->key = key; 116 | } 117 | if(val){ 118 | this->val = val; 119 | } 120 | }; 121 | 122 | Config::~Config(){ 123 | //log_trace("%*sfree %s(%d)", depth*4, "", this->key.c_str(), this->children.size()); 124 | for(int i = 0; i < (int)children.size(); i++){ 125 | delete children[i]; 126 | } 127 | } 128 | 129 | Config* Config::build_key_path(const char *key){ 130 | char path[CONFIG_MAX_LINE]; 131 | Config *conf = this; 132 | Config *c; 133 | 134 | snprintf(path, CONFIG_MAX_LINE, "%s", key); 135 | 136 | char *f, *fs; /* field, field seperator */ 137 | f = fs = path; 138 | while(1){ 139 | switch(*fs++){ 140 | case '.': 141 | case '/': 142 | *(fs - 1) = '\0'; 143 | c = (Config *)conf->find_child(f); 144 | if(c == NULL){ 145 | c = conf->add_child(f); 146 | } 147 | conf = c; 148 | f = fs; 149 | break; 150 | case '\0': 151 | c = (Config *)conf->find_child(f); 152 | if(c == NULL){ 153 | c = conf->add_child(f); 154 | } 155 | return c; 156 | default: 157 | break; 158 | } 159 | } 160 | } 161 | 162 | Config* Config::set(const char *key, const char *val){ 163 | Config *c = this->build_key_path(key); 164 | c->val = val; 165 | log_trace("%*s'%s' : '%s'", depth*4, "", this->key.c_str(), key); 166 | return c; 167 | } 168 | 169 | Config* Config::add_child(const char *key, const char *val, int lineno){ 170 | log_trace("add_child: %s", key); 171 | Config *c = new Config(key, val); 172 | c->parent = this; 173 | c->depth = this->depth + 1; 174 | children.push_back(c); 175 | return c; 176 | } 177 | 178 | const Config* Config::find_child(const char *key) const{ 179 | int i = (int)children.size()-1; 180 | for(; i >= 0; i--){ 181 | if(children[i]->key == key){ 182 | return children[i]; 183 | } 184 | } 185 | return NULL; 186 | } 187 | 188 | const Config* Config::get(const char *key) const{ 189 | char path[CONFIG_MAX_LINE]; 190 | const Config *conf = this; 191 | 192 | snprintf(path, CONFIG_MAX_LINE, "%s", key); 193 | 194 | char *f, *fs; /* field, field seperator */ 195 | f = fs = path; 196 | while(conf){ 197 | switch(*fs++){ 198 | case '.': 199 | case '/': 200 | *(fs - 1) = '\0'; 201 | conf = conf->find_child(f); 202 | f = fs; 203 | break; 204 | case '\0': 205 | conf = conf->find_child(f); 206 | return conf; 207 | default: 208 | break; 209 | } 210 | } 211 | return conf; 212 | } 213 | 214 | int Config::num() const{ 215 | return atoi(this->val.c_str()); 216 | } 217 | 218 | const char* Config::str() const{ 219 | return this->val.c_str(); 220 | } 221 | 222 | int Config::get_num(const char *key) const{ 223 | const Config *c = this->get(key); 224 | if(!c){ 225 | return 0; 226 | } 227 | return c->num(); 228 | } 229 | 230 | int64_t Config::get_int64(const char *key) const{ 231 | const Config *c = this->get(key); 232 | if(!c){ 233 | return 0; 234 | } 235 | return str_to_int64(c->val); 236 | } 237 | 238 | const char* Config::get_str(const char *key) const{ 239 | const Config *c = this->get(key); 240 | if(!c){ 241 | return ""; 242 | } 243 | return c->str(); 244 | } 245 | 246 | int Config::save(FILE *fp) const{ 247 | for(int i = 0; i < (int)children.size(); i++){ 248 | Config *c = children[i]; 249 | for(int j=0; jdepth; j++){ 250 | fputc('\t', fp); 251 | } 252 | 253 | if(c->is_comment()){ 254 | fprintf(fp, "#%s\n", c->val.c_str()); 255 | }else{ 256 | fprintf(fp, "%s: %s\n", c->key.c_str(), c->val.c_str()); 257 | } 258 | c->save(fp); 259 | } 260 | return 0; 261 | } 262 | 263 | int Config::save(const char *filename) const{ 264 | FILE *fp; 265 | 266 | if(strcmp(filename, "stdout") == 0){ 267 | fp = stdout; 268 | }else if(strcmp(filename, "stderr") == 0){ 269 | fp = stderr; 270 | }else{ 271 | fp = fopen(filename, "w"); 272 | if(!fp){ 273 | log_error("error opening file '%s': %s", filename, strerror(errno)); 274 | return -1; 275 | } 276 | } 277 | this->save(fp); 278 | if(fp && fp != stdout && fp != stderr){ 279 | fclose(fp); 280 | } 281 | return 0; 282 | } 283 | 284 | 285 | }; // namespace sim 286 | -------------------------------------------------------------------------------- /src/server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "util/log.h" 7 | #include "sim.h" 8 | #include "fde.h" 9 | #include "server.h" 10 | 11 | #include "util/strings.h" 12 | 13 | static std::string msg_str(const sim::Message &msg){ 14 | std::string ret; 15 | const std::map *fields = msg.fields(); 16 | std::map::const_iterator it; 17 | char buf[50]; 18 | int count = 0; 19 | for(it=fields->begin(); it!=fields->end(); it++){ 20 | if(ret.size() > 100){ 21 | snprintf(buf, sizeof(buf), "[%d more...]", (int)fields->size() - count); 22 | ret.append(buf); 23 | break; 24 | } 25 | 26 | int tag = it->first; 27 | const std::string &val = it->second; 28 | ret.append(str(tag)); 29 | ret.push_back('='); 30 | if(val.size() < 30){ 31 | std::string h = sim::encode(val, true); 32 | ret.append(h); 33 | }else{ 34 | sprintf(buf, "[%d]", (int)val.size()); 35 | ret.append(buf); 36 | } 37 | 38 | count ++; 39 | if(count != (int)fields->size()){ 40 | ret.push_back(' '); 41 | } 42 | } 43 | return ret; 44 | } 45 | 46 | 47 | namespace sim{ 48 | 49 | const static int DEFAULT_TYPE = 0; 50 | const static int HANDLER_TYPE = 1; 51 | 52 | Server::Server(){ 53 | signal(SIGPIPE, SIG_IGN); 54 | link_count = 0; 55 | serv_link = NULL; 56 | fdes = new Fdevents(); 57 | } 58 | 59 | Server::~Server(){ 60 | for(int i=0; ihandlers.size(); i++){ 61 | Handler *handler = this->handlers[i]; 62 | handler->m_free(); 63 | delete handler; 64 | } 65 | this->handlers.clear(); 66 | 67 | delete serv_link; 68 | delete fdes; 69 | } 70 | 71 | Server* Server::listen(const std::string &ip, int port){ 72 | Link *serv_link = Link::listen(ip, port); 73 | if(!serv_link){ 74 | return NULL; 75 | } 76 | 77 | Server *ret = new Server(); 78 | ret->serv_link = serv_link; 79 | ret->fdes->set(serv_link->fd(), FDEVENT_IN, DEFAULT_TYPE, serv_link); 80 | return ret; 81 | } 82 | 83 | void Server::add_handler(Handler *handler){ 84 | handler->m_init(); 85 | this->handlers.push_back(handler); 86 | if(handler->fd() > 0){ 87 | fdes->set(handler->fd(), FDEVENT_IN, HANDLER_TYPE, handler); 88 | } 89 | } 90 | 91 | Session* Server::accept_session(){ 92 | Link *link = serv_link->accept(); 93 | if(link == NULL){ 94 | log_error("accept failed! %s", strerror(errno)); 95 | return NULL; 96 | } 97 | 98 | link->nodelay(); 99 | link->noblock(); 100 | link->create_time = microtime(); 101 | link->active_time = link->create_time; 102 | 103 | Session *sess = new Session(); 104 | sess->link = link; 105 | this->sessions[sess->id] = sess; 106 | 107 | for(int i=0; ihandlers.size(); i++){ 108 | Handler *handler = this->handlers[i]; 109 | HandlerState state = handler->accept(*sess); 110 | if(state == HANDLE_FAIL){ 111 | delete link; 112 | delete sess; 113 | return NULL; 114 | } 115 | } 116 | 117 | this->link_count ++; 118 | log_debug("new link from %s:%d, fd: %d, links: %d", 119 | link->remote_ip, link->remote_port, link->fd(), this->link_count); 120 | fdes->set(link->fd(), FDEVENT_IN, DEFAULT_TYPE, sess); 121 | 122 | return sess; 123 | } 124 | 125 | int Server::close_session(Session *sess){ 126 | Link *link = sess->link; 127 | for(int i=0; ihandlers.size(); i++){ 128 | Handler *handler = this->handlers[i]; 129 | handler->close(*sess); 130 | } 131 | 132 | this->link_count --; 133 | log_debug("delete link %s:%d, fd: %d, links: %d", 134 | link->remote_ip, link->remote_port, link->fd(), this->link_count); 135 | fdes->del(link->fd()); 136 | 137 | this->sessions.erase(sess->id); 138 | delete link; 139 | delete sess; 140 | return 0; 141 | } 142 | 143 | int Server::read_session(Session *sess){ 144 | Link *link = sess->link; 145 | if(link->error()){ 146 | return 0; 147 | } 148 | 149 | int len = link->read(); 150 | if(len <= 0){ 151 | this->close_session(sess); 152 | return -1; 153 | } 154 | 155 | while(1){ 156 | Request req; 157 | int ret = link->recv(&req.msg); 158 | if(ret == -1){ 159 | log_info("fd: %d, parse error, delete link", link->fd()); 160 | this->close_session(sess); 161 | return -1; 162 | }else if(ret == 0){ 163 | // 报文未就绪, 继续读网络 164 | break; 165 | } 166 | req.stime = microtime(); 167 | req.sess = *sess; 168 | 169 | Response resp; 170 | for(int i=0; ihandlers.size(); i++){ 171 | Handler *handler = this->handlers[i]; 172 | req.time_wait = 1000 * (microtime() - req.stime); 173 | HandlerState state = handler->proc(req, &resp); 174 | req.time_proc = 1000 * (microtime() - req.stime) - req.time_wait; 175 | if(state == HANDLE_RESP){ 176 | link->send(resp.msg); 177 | if(link && !link->output.empty()){ 178 | fdes->set(link->fd(), FDEVENT_OUT, DEFAULT_TYPE, sess); 179 | } 180 | 181 | if(log_level() >= Logger::LEVEL_DEBUG){ 182 | log_debug("w:%.3f,p:%.3f, req: %s resp: %s", 183 | req.time_wait, req.time_proc, 184 | msg_str(req.msg).c_str(), 185 | msg_str(resp.msg).c_str()); 186 | } 187 | }else if(state == HANDLE_FAIL){ 188 | this->close_session(sess); 189 | return -1; 190 | } 191 | } 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | int Server::write_session(Session *sess){ 198 | Link *link = sess->link; 199 | if(link->error()){ 200 | return 0; 201 | } 202 | 203 | int len = link->write(); 204 | if(len <= 0){ 205 | log_debug("fd: %d, write: %d, delete link", link->fd(), len); 206 | this->close_session(sess); 207 | return -1; 208 | } 209 | if(link->output.empty()){ 210 | fdes->clr(link->fd(), FDEVENT_OUT); 211 | } 212 | return 0; 213 | } 214 | 215 | Session* Server::get_session(int64_t sess_id){ 216 | std::map::iterator it; 217 | it = sessions.find(sess_id); 218 | if(it == sessions.end()){ 219 | return NULL; 220 | } 221 | return it->second; 222 | } 223 | 224 | void Server::loop(){ 225 | while(1){ 226 | if(this->loop_once() == -1){ 227 | break; 228 | } 229 | } 230 | } 231 | 232 | int Server::loop_once(){ 233 | const Fdevents::events_t *events; 234 | events = fdes->wait(20); 235 | if(events == NULL){ 236 | log_fatal("events.wait error: %s", strerror(errno)); 237 | return 0; 238 | } 239 | 240 | for(int i=0; i<(int)events->size(); i++){ 241 | const Fdevent *fde = events->at(i); 242 | if(fde->data.ptr == serv_link){ 243 | this->accept_session(); 244 | }else if(fde->data.num == HANDLER_TYPE){ 245 | Handler *handler = (Handler *)fde->data.ptr; 246 | while(Response *resp = handler->handle()){ 247 | Session *sess = this->get_session(resp->sess.id); 248 | if(sess){ 249 | Link *link = sess->link; 250 | link->send(resp->msg); 251 | if(link && !link->output.empty()){ 252 | fdes->set(link->fd(), FDEVENT_OUT, DEFAULT_TYPE, sess); 253 | } 254 | } 255 | delete resp; 256 | } 257 | }else{ 258 | Session *sess = (Session *)fde->data.ptr; 259 | Link *link = sess->link; 260 | if(fde->events & FDEVENT_IN){ 261 | if(this->read_session(sess) == -1){ 262 | continue; 263 | } 264 | } 265 | if(fde->events & FDEVENT_OUT){ 266 | if(this->write_session(sess) == -1){ 267 | continue; 268 | } 269 | } 270 | if(link && !link->output.empty()){ 271 | fdes->set(link->fd(), FDEVENT_OUT, DEFAULT_TYPE, sess); 272 | } 273 | } 274 | } 275 | return 0; 276 | } 277 | 278 | }; // namespace sim 279 | -------------------------------------------------------------------------------- /api/php/sim.php: -------------------------------------------------------------------------------- 1 | $v){ 15 | $k = intval($k); 16 | if(is_array($v)){ 17 | $v = sim_encode($v); 18 | } 19 | $v = sim_escape($v); 20 | $ps[] = "$k=$v"; 21 | } 22 | $s = join(' ', $ps) . "\n"; 23 | return $s; 24 | } 25 | return sim_escape($data); 26 | } 27 | 28 | function sim_decode($line){ 29 | $line = trim($line); 30 | $ret = array(); 31 | $auto_tag = 0; 32 | $kvs = explode(' ', $line); 33 | foreach($kvs as $kv){ 34 | $ps = explode('=', $kv, 2); 35 | if(count($ps) == 1){ 36 | $tag = $auto_tag; 37 | $val = $ps[0]; 38 | }else{ 39 | $tag = intval($ps[0]); 40 | $val = $ps[1]; 41 | } 42 | $auto_tag = $tag + 1; 43 | $val = sim_unescape($val); 44 | $ret[$tag] = $val; 45 | } 46 | return $ret; 47 | } 48 | 49 | function sim_escape($str){ 50 | if(!is_string($str)){ 51 | $str .= ''; 52 | } 53 | if(1){ 54 | $str = addcslashes($str, "\t\r\n\x07\x08\x0b\x0c\\"); 55 | $str = str_replace(array("\0", ' '), array("\\0", '\s'), $str); 56 | return $str; 57 | } 58 | // 后面的代码不使用了 59 | static $min_c = 0; 60 | if($min_c == 0){ 61 | $min_c = ord('!'); 62 | } 63 | static $max_c = 0; 64 | if($max_c == 0){ 65 | $max_c = ord('~'); 66 | } 67 | 68 | $ret = ''; 69 | $len = strlen($str); 70 | for($i=0; $i<$len; $i++){ 71 | $c = $str[$i]; 72 | switch($c){ 73 | case ' ': 74 | $ret .= '\\s'; 75 | break; 76 | case "\\": 77 | $ret .= "\\\\"; 78 | break; 79 | case "\x07": // \a 80 | $ret .= "\\a"; 81 | break; 82 | case "\x08": // \b 83 | $ret .= "\\b"; 84 | break; 85 | case "\x0c": // \f 86 | $ret .= "\\f"; 87 | break; 88 | case "\x0b": // \v 89 | $ret .= "\\v"; 90 | break; 91 | case "\r": 92 | $ret .= "\\r"; 93 | break; 94 | case "\n": 95 | $ret .= "\\n"; 96 | break; 97 | case "\t": 98 | $ret .= "\\t"; 99 | break; 100 | case "\0": 101 | $ret .= "\\0"; 102 | break; 103 | default: 104 | $ret .= $c; 105 | // TODO: 对非 UTF-8 字符进行转义 106 | /* 107 | $ord = ord($c); 108 | if($ord >= $min_c && $ord <= $max_c){ 109 | $ret .= $c; 110 | }else{ 111 | $ret .= "\\x"; 112 | $ret .= sprintf('%02x', $ord); 113 | } 114 | */ 115 | break; 116 | } 117 | } 118 | return $ret; 119 | } 120 | 121 | function sim_unescape($str){ 122 | if(!is_string($str)){ 123 | $str .= ''; 124 | } 125 | if(1){ 126 | // 不确定是否将 \s 转成空格, 所以不能使用 stripcslashes() 127 | #$str = stripcslashes($str); 128 | #return $str; 129 | } 130 | $ret = ''; 131 | $len = strlen($str); 132 | for($i=0; $i<$len; $i++){ 133 | $c = $str[$i]; 134 | if($c != '\\'){ 135 | $ret .= $c; 136 | continue; 137 | } 138 | if($i >= $len - 1){ 139 | break; 140 | } 141 | $c2 = $str[++$i]; 142 | switch($c2){ 143 | case 's': 144 | $ret .= ' '; 145 | break; 146 | case '\\': 147 | $ret .= "\\"; 148 | break; 149 | case 'a': 150 | $ret .= "\x07"; 151 | break; 152 | case 'b': 153 | $ret .= "\x08"; 154 | break; 155 | case 'f': 156 | $ret .= "\x0c"; 157 | break; 158 | case 'v': 159 | $ret .= "\x0b"; 160 | break; 161 | case 'r': 162 | $ret .= "\r"; 163 | break; 164 | case 'n': 165 | $ret .= "\n"; 166 | break; 167 | case 't': 168 | $ret .= "\t"; 169 | break; 170 | case '0': 171 | $ret .= "\0"; 172 | break; 173 | case 'x': 174 | $hex = substr($str, $i+1, 2); 175 | $ret .= hex2bin($hex); 176 | $i += 2; 177 | break; 178 | default: 179 | $ret .= $c2; 180 | break; 181 | } 182 | } 183 | return $ret; 184 | } 185 | 186 | 187 | class SimException extends Exception 188 | { 189 | } 190 | 191 | class SimTimeoutException extends SimException 192 | { 193 | } 194 | 195 | class SimClient 196 | { 197 | private $debug = false; 198 | public $sock = null; 199 | private $_closed = false; 200 | private $recv_buf = ''; 201 | public $last_resp = null; 202 | 203 | function __construct($host, $port, $timeout_ms=2000){ 204 | $timeout_f = (float)$timeout_ms/1000; 205 | $this->sock = @stream_socket_client("$host:$port", $errno, $errstr, $timeout_f); 206 | if(!$this->sock){ 207 | throw new SimException("$errno: $errstr"); 208 | } 209 | if($timeout_ms > 0){ 210 | $timeout_sec = intval($timeout_ms/1000); 211 | $timeout_usec = ($timeout_ms - $timeout_sec * 1000) * 1000; 212 | @stream_set_timeout($this->sock, $timeout_sec, $timeout_usec); 213 | } 214 | if(function_exists('stream_set_chunk_size')){ 215 | @stream_set_chunk_size($this->sock, 1024 * 1024); 216 | } 217 | } 218 | 219 | function close(){ 220 | if(!$this->_closed){ 221 | @fclose($this->sock); 222 | $this->_closed = true; 223 | $this->sock = null; 224 | } 225 | } 226 | 227 | function closed(){ 228 | return $this->_closed; 229 | } 230 | 231 | private $batch_reqs = null; 232 | 233 | function batch(){ 234 | $this->batch_reqs = array(); 235 | } 236 | 237 | function exec(){ 238 | if(!is_array($this->batch_reqs)){ 239 | return array(); 240 | } 241 | $ret = array(); 242 | foreach($this->batch_reqs as $req){ 243 | $this->send($req); 244 | } 245 | foreach($this->batch_reqs as $req){ 246 | $resp = $this->recv(); 247 | $ret[] = $resp; 248 | } 249 | $this->batch_reqs = null; 250 | return $ret; 251 | } 252 | 253 | function request($arg1){ 254 | $req = array(); 255 | $args = func_get_args(); 256 | foreach($args as $index=>$arg){ 257 | if(is_array($arg)){ 258 | $req = array_merge($req, $arg); 259 | }else{ 260 | $req[] = $arg.''; 261 | } 262 | } 263 | 264 | if(is_array($this->batch_reqs)){ 265 | $this->batch_reqs[] = $req; 266 | }else{ 267 | $this->send($req); 268 | $resp = $this->recv(); 269 | return $resp; 270 | } 271 | return true; 272 | } 273 | 274 | function send($data){ 275 | if(is_array($data)){ 276 | $s = sim_encode($data); 277 | }else{ 278 | $s = $data; 279 | } 280 | if($this->debug){ 281 | echo '> ' . str_replace(array("\r", "\n"), array('\r', '\n'), $s) . "\n"; 282 | } 283 | try{ 284 | while(true){ 285 | $ret = @fwrite($this->sock, $s); 286 | if($ret === false || $ret === 0){ 287 | $this->close(); 288 | throw new SimException('Connection lost'); 289 | } 290 | $s = substr($s, $ret); 291 | if(strlen($s) == 0){ 292 | break; 293 | } 294 | @fflush($this->sock); 295 | } 296 | }catch(Exception $e){ 297 | $this->close(); 298 | throw new SimException($e->getMessage()); 299 | } 300 | return $ret; 301 | } 302 | 303 | function recv(){ 304 | while(true){ 305 | $ret = $this->parse(); 306 | if($ret !== null){ 307 | return $ret; 308 | } 309 | try{ 310 | $data = @fread($this->sock, 1024 * 1024); 311 | if($this->debug){ 312 | echo '< ' . str_replace(array("\r", "\n"), array('\r', '\n'), $data) . "\n"; 313 | } 314 | }catch(Exception $e){ 315 | $data = ''; 316 | } 317 | if($data === false || $data === ''){ 318 | if(feof($this->sock)){ 319 | $this->close(); 320 | throw new SimException('Connection lost'); 321 | }else{ 322 | throw new SimTimeoutException('Connection timeout'); 323 | } 324 | } 325 | $this->recv_buf .= $data; 326 | # echo "read " . strlen($data) . " total: " . strlen($this->recv_buf) . "\n"; 327 | } 328 | } 329 | 330 | private function parse(){ 331 | $msg_end = strpos($this->recv_buf, "\n"); 332 | if($msg_end === false){ 333 | return null; 334 | } 335 | $line = substr($this->recv_buf, 0, $msg_end + 1); 336 | $this->recv_buf = substr($this->recv_buf, $msg_end + 1); 337 | return sim_decode($line); 338 | /* 339 | // TODO: 340 | if(isset($ret[SIM_LIST_TAG])){ 341 | $list = parse_msgs_in_str($ret[SIM_LIST_TAG]); 342 | $ret = $list; 343 | } 344 | if(isset($ret[SIM_MAP_TAG])){ 345 | $list = parse_msgs_in_str($ret[SIM_MAP_TAG]); 346 | $map = ... $list... 347 | $ret = $map; 348 | } 349 | */ 350 | return $ret; 351 | } 352 | 353 | } 354 | 355 | -------------------------------------------------------------------------------- /src/util/log.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #include "log.h" 7 | #include 8 | 9 | static Logger logger; 10 | 11 | int log_open(FILE *fp, int level, bool is_threadsafe){ 12 | return logger.open(fp, level, is_threadsafe); 13 | } 14 | 15 | int log_open(const char *filename, int level, bool is_threadsafe, uint64_t rotate_size){ 16 | return logger.open(filename, level, is_threadsafe, rotate_size); 17 | } 18 | 19 | int log_level(){ 20 | return logger.level(); 21 | } 22 | 23 | void set_log_level(int level){ 24 | logger.set_level(level); 25 | } 26 | 27 | void set_log_level(const char *s){ 28 | std::string ss(s); 29 | std::transform(ss.begin(), ss.end(), ss.begin(), ::tolower); 30 | int level = Logger::LEVEL_DEBUG; 31 | if(ss == "fatal"){ 32 | level = Logger::LEVEL_FATAL; 33 | }else if(ss == "error"){ 34 | level = Logger::LEVEL_ERROR; 35 | }else if(ss == "warn"){ 36 | level = Logger::LEVEL_WARN; 37 | }else if(ss == "info"){ 38 | level = Logger::LEVEL_INFO; 39 | }else if(ss == "debug"){ 40 | level = Logger::LEVEL_DEBUG; 41 | }else if(ss == "trace"){ 42 | level = Logger::LEVEL_TRACE; 43 | } 44 | logger.set_level(level); 45 | } 46 | 47 | int log_write(int level, const char *fmt, ...){ 48 | va_list ap; 49 | va_start(ap, fmt); 50 | int ret = logger.logv(level, fmt, ap); 51 | va_end(ap); 52 | return ret; 53 | } 54 | 55 | /*****/ 56 | 57 | Logger* Logger::shared(){ 58 | return &logger; 59 | } 60 | 61 | Logger::Logger(){ 62 | fp = stdout; 63 | level_ = LEVEL_DEBUG; 64 | mutex = NULL; 65 | 66 | filename[0] = '\0'; 67 | rotate_size_ = 0; 68 | stats.w_curr = 0; 69 | stats.w_total = 0; 70 | } 71 | 72 | Logger::~Logger(){ 73 | if(mutex){ 74 | pthread_mutex_destroy(mutex); 75 | free(mutex); 76 | } 77 | this->close(); 78 | } 79 | 80 | std::string Logger::level_name(){ 81 | switch(level_){ 82 | case Logger::LEVEL_FATAL: 83 | return "fatal"; 84 | case Logger::LEVEL_ERROR: 85 | return "error"; 86 | case Logger::LEVEL_WARN: 87 | return "warn"; 88 | case Logger::LEVEL_INFO: 89 | return "info"; 90 | case Logger::LEVEL_DEBUG: 91 | return "debug"; 92 | case Logger::LEVEL_TRACE: 93 | return "trace"; 94 | } 95 | return ""; 96 | } 97 | 98 | std::string Logger::output_name(){ 99 | return filename; 100 | } 101 | 102 | uint64_t Logger::rotate_size(){ 103 | return rotate_size_; 104 | } 105 | 106 | void Logger::threadsafe(){ 107 | if(mutex){ 108 | pthread_mutex_destroy(mutex); 109 | free(mutex); 110 | mutex = NULL; 111 | } 112 | mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); 113 | pthread_mutex_init(mutex, NULL); 114 | } 115 | 116 | int Logger::open(FILE *fp, int level, bool is_threadsafe){ 117 | this->fp = fp; 118 | this->level_ = level; 119 | if(is_threadsafe){ 120 | this->threadsafe(); 121 | } 122 | return 0; 123 | } 124 | 125 | int Logger::open(const char *filename, int level, bool is_threadsafe, uint64_t rotate_size){ 126 | if(strlen(filename) > PATH_MAX - 20){ 127 | fprintf(stderr, "log filename too long!"); 128 | return -1; 129 | } 130 | this->level_ = level; 131 | this->rotate_size_ = rotate_size; 132 | strcpy(this->filename, filename); 133 | 134 | FILE *fp; 135 | if(strcmp(filename, "stdout") == 0){ 136 | fp = stdout; 137 | }else if(strcmp(filename, "stderr") == 0){ 138 | fp = stderr; 139 | }else{ 140 | fp = fopen(filename, "a"); 141 | if(fp == NULL){ 142 | return -1; 143 | } 144 | 145 | struct stat st; 146 | int ret = fstat(fileno(fp), &st); 147 | if(ret == -1){ 148 | fprintf(stderr, "fstat log file %s error!", filename); 149 | return -1; 150 | }else{ 151 | stats.w_curr = st.st_size; 152 | } 153 | } 154 | return this->open(fp, level, is_threadsafe); 155 | } 156 | 157 | void Logger::close(){ 158 | if(fp != stdin && fp != stdout){ 159 | fclose(fp); 160 | } 161 | } 162 | 163 | void Logger::rotate(){ 164 | fclose(fp); 165 | char newpath[PATH_MAX]; 166 | time_t time; 167 | struct timeval tv; 168 | struct tm *tm; 169 | gettimeofday(&tv, NULL); 170 | time = tv.tv_sec; 171 | tm = localtime(&time); 172 | sprintf(newpath, "%s.%04d%02d%02d-%02d%02d%02d", 173 | this->filename, 174 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 175 | tm->tm_hour, tm->tm_min, tm->tm_sec); 176 | 177 | //printf("rename %s => %s\n", this->filename, newpath); 178 | int ret = rename(this->filename, newpath); 179 | if(ret == -1){ 180 | return; 181 | } 182 | fp = fopen(this->filename, "a"); 183 | if(fp == NULL){ 184 | return; 185 | } 186 | stats.w_curr = 0; 187 | } 188 | 189 | int Logger::get_level(const char *levelname){ 190 | if(strcmp("trace", levelname) == 0){ 191 | return LEVEL_TRACE; 192 | } 193 | if(strcmp("debug", levelname) == 0){ 194 | return LEVEL_DEBUG; 195 | } 196 | if(strcmp("info", levelname) == 0){ 197 | return LEVEL_INFO; 198 | } 199 | if(strcmp("warn", levelname) == 0){ 200 | return LEVEL_WARN; 201 | } 202 | if(strcmp("error", levelname) == 0){ 203 | return LEVEL_ERROR; 204 | } 205 | if(strcmp("fatal", levelname) == 0){ 206 | return LEVEL_FATAL; 207 | } 208 | if(strcmp("none", levelname) == 0){ 209 | return LEVEL_NONE; 210 | } 211 | return LEVEL_DEBUG; 212 | } 213 | 214 | inline static const char* get_level_name(int level){ 215 | switch(level){ 216 | case Logger::LEVEL_FATAL: 217 | return "[FATAL] "; 218 | case Logger::LEVEL_ERROR: 219 | return "[ERROR] "; 220 | case Logger::LEVEL_WARN: 221 | return "[WARN ] "; 222 | case Logger::LEVEL_INFO: 223 | return "[INFO ] "; 224 | case Logger::LEVEL_DEBUG: 225 | return "[DEBUG] "; 226 | case Logger::LEVEL_TRACE: 227 | return "[TRACE] "; 228 | } 229 | return ""; 230 | } 231 | 232 | #define LEVEL_NAME_LEN 8 233 | #define LOG_BUF_LEN 4096 234 | 235 | int Logger::logv(int level, const char *fmt, va_list ap){ 236 | if(logger.level_ < level){ 237 | return 0; 238 | } 239 | 240 | char buf[LOG_BUF_LEN]; 241 | int len; 242 | char *ptr = buf; 243 | 244 | time_t time; 245 | struct timeval tv; 246 | struct tm *tm; 247 | gettimeofday(&tv, NULL); 248 | time = tv.tv_sec; 249 | tm = localtime(&time); 250 | /* %3ld 在数值位数超过3位的时候不起作用, 所以这里转成int */ 251 | len = sprintf(ptr, "%04d-%02d-%02d %02d:%02d:%02d.%03d ", 252 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 253 | tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec/1000)); 254 | if(len < 0){ 255 | return -1; 256 | } 257 | ptr += len; 258 | 259 | memcpy(ptr, get_level_name(level), LEVEL_NAME_LEN); 260 | ptr += LEVEL_NAME_LEN; 261 | 262 | int space = sizeof(buf) - (ptr - buf) - 10; 263 | len = vsnprintf(ptr, space, fmt, ap); 264 | if(len < 0){ 265 | return -1; 266 | } 267 | ptr += len > space? space : len; 268 | *ptr++ = '\n'; 269 | *ptr = '\0'; 270 | 271 | len = ptr - buf; 272 | // change to write(), without locking? 273 | if(this->mutex){ 274 | pthread_mutex_lock(this->mutex); 275 | } 276 | fwrite(buf, len, 1, this->fp); 277 | fflush(this->fp); 278 | 279 | stats.w_curr += len; 280 | stats.w_total += len; 281 | if(rotate_size_ > 0 && stats.w_curr > rotate_size_){ 282 | this->rotate(); 283 | } 284 | if(this->mutex){ 285 | pthread_mutex_unlock(this->mutex); 286 | } 287 | 288 | return len; 289 | } 290 | 291 | int Logger::trace(const char *fmt, ...){ 292 | va_list ap; 293 | va_start(ap, fmt); 294 | int ret = logger.logv(Logger::LEVEL_TRACE, fmt, ap); 295 | va_end(ap); 296 | return ret; 297 | } 298 | 299 | int Logger::debug(const char *fmt, ...){ 300 | va_list ap; 301 | va_start(ap, fmt); 302 | int ret = logger.logv(Logger::LEVEL_DEBUG, fmt, ap); 303 | va_end(ap); 304 | return ret; 305 | } 306 | 307 | int Logger::info(const char *fmt, ...){ 308 | va_list ap; 309 | va_start(ap, fmt); 310 | int ret = logger.logv(Logger::LEVEL_INFO, fmt, ap); 311 | va_end(ap); 312 | return ret; 313 | } 314 | 315 | int Logger::warn(const char *fmt, ...){ 316 | va_list ap; 317 | va_start(ap, fmt); 318 | int ret = logger.logv(Logger::LEVEL_WARN, fmt, ap); 319 | va_end(ap); 320 | return ret; 321 | } 322 | 323 | int Logger::error(const char *fmt, ...){ 324 | va_list ap; 325 | va_start(ap, fmt); 326 | int ret = logger.logv(Logger::LEVEL_ERROR, fmt, ap); 327 | va_end(ap); 328 | return ret; 329 | } 330 | 331 | int Logger::fatal(const char *fmt, ...){ 332 | va_list ap; 333 | va_start(ap, fmt); 334 | int ret = logger.logv(Logger::LEVEL_FATAL, fmt, ap); 335 | va_end(ap); 336 | return ret; 337 | } 338 | -------------------------------------------------------------------------------- /src/util/strings.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2014 The SSDB Authors. All rights reserved. 3 | Use of this source code is governed by a BSD-style license that can be 4 | found in the LICENSE file. 5 | */ 6 | #ifndef SIM_UTIL_STRING_H 7 | #define SIM_UTIL_STRING_H 8 | 9 | #ifndef __STDC_FORMAT_MACROS 10 | #define __STDC_FORMAT_MACROS 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | inline static 25 | int is_empty_str(const char *str){ 26 | const char *p = str; 27 | while(*p && isspace(*p)){ 28 | p++; 29 | } 30 | return *p == '\0'; 31 | } 32 | 33 | /* 返回左边不包含空白字符的字符串的指针 */ 34 | inline static 35 | char *ltrim(const char *str){ 36 | const char *p = str; 37 | while(*p && isspace(*p)){ 38 | p++; 39 | } 40 | return (char *)p; 41 | } 42 | 43 | /* 返回指向字符串结尾的指针, 会修改字符串内容 */ 44 | inline static 45 | char *rtrim(char *str){ 46 | char *p; 47 | p = str + strlen(str) - 1; 48 | while(p >= str && isspace(*p)){ 49 | p--; 50 | } 51 | *(++p) = '\0'; 52 | return p; 53 | } 54 | 55 | /* 返回左边不包含空白字符的字符串的指针 */ 56 | inline static 57 | char *trim(char *str){ 58 | char *p; 59 | p = ltrim(str); 60 | rtrim(p); 61 | return p; 62 | } 63 | 64 | inline static 65 | void strtolower(std::string *str){ 66 | std::transform(str->begin(), str->end(), str->begin(), ::tolower); 67 | } 68 | 69 | inline static 70 | void strtoupper(std::string *str){ 71 | std::transform(str->begin(), str->end(), str->begin(), ::toupper); 72 | } 73 | 74 | inline static 75 | std::string real_dirname(const char *filepath){ 76 | std::string dir; 77 | if(filepath[0] != '/'){ 78 | char buf[1024]; 79 | char *p = getcwd(buf, sizeof(buf)); 80 | if(p != NULL){ 81 | dir.append(p); 82 | } 83 | dir.append("/"); 84 | } 85 | 86 | const char *p = strrchr(filepath, '/'); 87 | if(p != NULL){ 88 | dir.append(filepath, p - filepath); 89 | } 90 | return dir; 91 | } 92 | 93 | inline static 94 | std::string str_escape(const char *s, int size){ 95 | static const char *hex = "0123456789abcdef"; 96 | std::string ret; 97 | for(int i=0; i= '!' && c <= '~'){ 117 | ret.push_back(c); 118 | }else{ 119 | ret.append("\\x"); 120 | unsigned char d = c; 121 | ret.push_back(hex[d >> 4]); 122 | ret.push_back(hex[d & 0x0f]); 123 | } 124 | break; 125 | } 126 | } 127 | return ret; 128 | } 129 | 130 | inline static 131 | std::string str_escape(const std::string &s){ 132 | return str_escape(s.data(), (int)s.size()); 133 | } 134 | 135 | inline static 136 | int hex_int(char c){ 137 | if(c >= '0' && c <= '9'){ 138 | return c - '0'; 139 | }else{ 140 | return c - 'a' + 10; 141 | } 142 | } 143 | 144 | inline static 145 | std::string str_unescape(const char *s, int size){ 146 | std::string ret; 147 | for(int i=0; i= size - 1){ 153 | continue; 154 | } 155 | char c2 = s[++i]; 156 | switch(c2){ 157 | case 'a': 158 | ret.push_back('\a'); 159 | break; 160 | case 'b': 161 | ret.push_back('\b'); 162 | break; 163 | case 'f': 164 | ret.push_back('\f'); 165 | break; 166 | case 'v': 167 | ret.push_back('\v'); 168 | break; 169 | case 'r': 170 | ret.push_back('\r'); 171 | break; 172 | case 'n': 173 | ret.push_back('\n'); 174 | break; 175 | case 't': 176 | ret.push_back('\t'); 177 | break; 178 | case '\\': 179 | ret.push_back('\\'); 180 | break; 181 | case 'x': 182 | if(i < size - 2){ 183 | char c3 = s[++i]; 184 | char c4 = s[++i]; 185 | ret.push_back((char)((hex_int(c3) << 4) + hex_int(c4))); 186 | } 187 | break; 188 | default: 189 | ret.push_back(c2); 190 | break; 191 | } 192 | } 193 | } 194 | return ret; 195 | } 196 | 197 | inline static 198 | std::string str_unescape(const std::string &s){ 199 | return str_unescape(s.data(), (int)s.size()); 200 | } 201 | 202 | inline static 203 | std::string hexmem(const void *p, int size){ 204 | return str_escape((char *)p, size); 205 | /* 206 | std::string ret; 207 | char buf[4]; 208 | for(int i=0; i\n", s.c_str()); 240 | } 241 | 242 | 243 | static inline 244 | std::string str(const char *s){ 245 | return std::string(s); 246 | } 247 | 248 | static inline 249 | std::string str(int v){ 250 | char buf[21] = {0}; 251 | snprintf(buf, sizeof(buf), "%d", v); 252 | return std::string(buf); 253 | } 254 | 255 | static inline 256 | std::string str(uint32_t v){ 257 | char buf[21] = {0}; 258 | snprintf(buf, sizeof(buf), "%d", v); 259 | return std::string(buf); 260 | } 261 | 262 | static inline 263 | std::string str(int64_t v){ 264 | char buf[21] = {0}; 265 | snprintf(buf, sizeof(buf), "%" PRId64 "", v); 266 | return std::string(buf); 267 | } 268 | 269 | static inline 270 | std::string str(uint64_t v){ 271 | char buf[21] = {0}; 272 | snprintf(buf, sizeof(buf), "%" PRIu64 "", v); 273 | return std::string(buf); 274 | } 275 | 276 | static inline 277 | std::string str(double v){ 278 | char buf[21] = {0}; 279 | if(v - floor(v) == 0){ 280 | snprintf(buf, sizeof(buf), "%.0f", v); 281 | }else{ 282 | snprintf(buf, sizeof(buf), "%f", v); 283 | } 284 | return std::string(buf); 285 | } 286 | 287 | static inline 288 | std::string str(float v){ 289 | return str((double)v); 290 | } 291 | 292 | // all str_to_xx methods set errno on error 293 | 294 | static inline 295 | int str_to_int(const std::string &str){ 296 | const char *start = str.c_str(); 297 | char *end; 298 | int ret = (int)strtol(start, &end, 10); 299 | // the WHOLE string must be string represented integer 300 | if(*end == '\0' && size_t(end - start) == str.size()){ 301 | errno = 0; 302 | }else{ 303 | // strtoxx do not set errno all the time! 304 | if(errno == 0){ 305 | errno = EINVAL; 306 | } 307 | } 308 | return ret; 309 | } 310 | 311 | static inline 312 | int str_to_int(const char *p, int size){ 313 | return str_to_int(std::string(p, size)); 314 | } 315 | 316 | static inline 317 | int64_t str_to_int64(const std::string &str){ 318 | const char *start = str.c_str(); 319 | char *end; 320 | int64_t ret = (int64_t)strtoll(start, &end, 10); 321 | // the WHOLE string must be string represented integer 322 | if(*end == '\0' && size_t(end - start) == str.size()){ 323 | errno = 0; 324 | }else{ 325 | // strtoxx do not set errno all the time! 326 | if(errno == 0){ 327 | errno = EINVAL; 328 | } 329 | } 330 | return ret; 331 | } 332 | 333 | static inline 334 | int64_t str_to_int64(const char *p, int size){ 335 | return str_to_int64(std::string(p, size)); 336 | } 337 | 338 | static inline 339 | uint64_t str_to_uint64(const std::string &str){ 340 | const char *start = str.c_str(); 341 | char *end; 342 | uint64_t ret = (uint64_t)strtoull(start, &end, 10); 343 | // the WHOLE string must be string represented integer 344 | if(*end == '\0' && size_t(end - start) == str.size()){ 345 | errno = 0; 346 | }else{ 347 | // strtoxx do not set errno all the time! 348 | if(errno == 0){ 349 | errno = EINVAL; 350 | } 351 | } 352 | return ret; 353 | } 354 | 355 | static inline 356 | uint64_t str_to_uint64(const char *p, int size){ 357 | return str_to_uint64(std::string(p, size)); 358 | } 359 | 360 | static inline 361 | double str_to_double(const char *p, int size){ 362 | return atof(std::string(p, size).c_str()); 363 | } 364 | 365 | static inline 366 | std::string substr(const std::string &str, int start, int size){ 367 | if(start < 0){ 368 | start = (int)str.size() + start; 369 | } 370 | if(size < 0){ 371 | // 忽略掉 abs(size) 个字节 372 | size = ((int)str.size() + size) - start; 373 | } 374 | if(start < 0 || size_t(start) >= str.size() || size < 0){ 375 | return ""; 376 | } 377 | return str.substr(start, size); 378 | } 379 | 380 | static inline 381 | std::string str_slice(const std::string &str, int start, int end){ 382 | if(start < 0){ 383 | start = (int)str.size() + start; 384 | } 385 | int size; 386 | if(end < 0){ 387 | size = ((int)str.size() + end + 1) - start; 388 | }else{ 389 | size = end - start + 1; 390 | } 391 | if(start < 0 || size_t(start) >= str.size() || size < 0){ 392 | return ""; 393 | } 394 | return str.substr(start, size); 395 | } 396 | 397 | static inline 398 | int bitcount(const char *p, int size){ 399 | int n = 0; 400 | for(int i=0; i> 1; 405 | } 406 | } 407 | return n; 408 | } 409 | 410 | // is big endia. TODO: auto detect 411 | #if 0 412 | #define big_endian(v) (v) 413 | #else 414 | static inline 415 | uint16_t big_endian(uint16_t v){ 416 | return (v>>8) | (v<<8); 417 | } 418 | 419 | static inline 420 | uint32_t big_endian(uint32_t v){ 421 | return (v >> 24) | ((v >> 8) & 0xff00) | ((v << 8) & 0xff0000) | (v << 24); 422 | } 423 | 424 | static inline 425 | uint64_t big_endian(uint64_t v){ 426 | uint32_t h = v >> 32; 427 | uint32_t l = v & 0xffffffffull; 428 | return big_endian(h) | ((uint64_t)big_endian(l) << 32); 429 | } 430 | #endif 431 | 432 | 433 | #endif 434 | --------------------------------------------------------------------------------