├── src ├── dk_task_connection.h ├── dk_error.h ├── dk_util.h ├── dk_core.h ├── example │ ├── Makefile │ ├── tcp_client.cpp │ ├── http_client.cpp │ └── echo_srv.cpp ├── dk_thread_pool.h ├── dk_common.h ├── dk_thread_pool.cpp ├── dk_base_thread.cpp ├── dk_lock.h ├── Makefile ├── dk_base_thread.h ├── dk_queue.h ├── dk_worker.cpp ├── dk_util.cpp ├── dk_log.h ├── dk_internal.h ├── dk_ev_thread.cpp ├── dk_worker.h ├── dk_ev_thread.h ├── dk_base_server.h ├── dk_http.cpp ├── dk_http.h ├── dk_base_server.cpp ├── dk_base_connection.h └── dk_base_connection.cpp ├── README.md └── .gitignore /src/dk_task_connection.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2013-05-13 4 | */ 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | donkey_server 2 | ============= 3 | 4 | A tcp server framework based on libevent-bufferevent. Depend on libevent2.0.x. -------------------------------------------------------------------------------- /src/dk_error.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-23 4 | */ 5 | 6 | #ifndef __DONKEY_ERROR_INCLUDE__ 7 | #define __DONKEY_ERROR_INCLUDE__ 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /src/dk_util.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * author:jlijian3@gmail.com 4 | * date:2012-03-08 5 | */ 6 | 7 | #ifndef __DONKEY_UTIL_INCLUDE__ 8 | #define __DONKEY_UTIL_INCLUDE__ 9 | 10 | #include "dk_common.h" 11 | 12 | bool DKGetHostByName(const std::string &host, std::string &ip); 13 | bool DKGetHostName(std::string &host); 14 | bool DKGetHostIp(std::string &ip); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/dk_core.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * author:jlijian3@gmail.com 4 | * date:2012-03 5 | */ 6 | 7 | #ifndef __DONKEY_CORE_INCLUDE__ 8 | #define __DONKEY_CORE_INCLUDE__ 9 | 10 | #include "dk_base_connection.h" 11 | #include "dk_log.h" 12 | #include "dk_http.h" 13 | #include "dk_internal.h" 14 | #include "dk_ev_thread.h" 15 | #include "dk_worker.h" 16 | #include "dk_base_thread.h" 17 | #include "dk_base_server.h" 18 | #include "dk_thread_pool.h" 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/example/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LIBS=../libdk_base_server.a 3 | CXXFLAGS=-g -I.. -D_DONKEY_DEBUG 4 | LDFLAGS=-lrt -levent -levent_pthreads 5 | 6 | PROGS=echo_srv http_client tcp_client 7 | all:$(PROGS) 8 | 9 | .PHONY:all 10 | 11 | echo_srv:echo_srv.o $(LIBS) 12 | g++ $(CXXFLAGS) -o echo_srv echo_srv.o $(LIBS) $(LDFLAGS) 13 | 14 | http_client:http_client.o $(LIBS) 15 | g++ $(CXXFLAGS) -o http_client http_client.o $(LIBS) $(LDFLAGS) 16 | 17 | tcp_client:tcp_client.o $(LIBS) 18 | g++ $(CXXFLAGS) -o tcp_client tcp_client.o $(LIBS) $(LDFLAGS) 19 | 20 | .PHONY:clean 21 | clean: 22 | rm *.o $(PROGS) 23 | -------------------------------------------------------------------------------- /src/dk_thread_pool.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #ifndef __DKINNER_THREAD_POOL_INCLUDE__ 7 | #define __DKINNER_THREAD_POOL_INCLUDE__ 8 | 9 | #include 10 | 11 | #include "dk_core.h" 12 | 13 | class DKThreadPool { 14 | public: 15 | DKThreadPool() { 16 | } 17 | 18 | ~DKThreadPool() { 19 | } 20 | 21 | bool Init(int threads); 22 | 23 | bool CallInThread(deferred_cb_fn cb, void *arg); 24 | 25 | private: 26 | std::vector workers_; 27 | sem_t sem_; 28 | LockQueue que_; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/dk_common.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * author:jlijian3@gmail.com 4 | * date:2012-03-08 5 | */ 6 | 7 | #ifndef __DONKEY_COMMON_INCLUDE__ 8 | #define __DONKEY_COMMON_INCLUDE__ 9 | 10 | /* sys */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | /* stl */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* event */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/dk_thread_pool.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #include "dk_thread_pool.h" 7 | 8 | bool DKThreadPool::Init(int threads) { 9 | if (threads <= 0) 10 | return false; 11 | 12 | if (sem_init(&sem_, 0, 0) != 0){ 13 | return false; 14 | } 15 | 16 | workers_.resize(threads); 17 | for (size_t i = 0; i < workers_.size(); i++) { 18 | if (!workers_[i].Init()) 19 | return false; 20 | 21 | workers_[i].set_extern_sem_queue(&sem_, &que_); 22 | workers_[i].Create(); 23 | } 24 | 25 | return true; 26 | } 27 | 28 | bool DKThreadPool::CallInThread(deferred_cb_fn cb, void *arg) { 29 | que_.push(DeferredCb(cb, arg)); 30 | return 0 == sem_post(&sem_); 31 | } 32 | -------------------------------------------------------------------------------- /src/dk_base_thread.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #include "dk_common.h" 7 | #include "dk_base_thread.h" 8 | 9 | void DKBaseThread::Create() { 10 | pthread_attr_t attr; 11 | int ret; 12 | 13 | pthread_attr_init(&attr); 14 | 15 | if ((ret = pthread_create(&thread_id_, &attr, 16 | PThreadRoutine, (void *)this)) != 0) { 17 | fprintf(stderr, "Can't create thread: %s\n", strerror(ret)); 18 | exit(1); 19 | } 20 | } 21 | 22 | void *DKBaseThread::PThreadRoutine(void *arg) { 23 | assert(arg); 24 | int rv; 25 | 26 | DKBaseThread *me = (DKBaseThread *)arg; 27 | rv = me->ThreadRoutine(); 28 | pthread_exit((void *)&rv); 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/dk_lock.h: -------------------------------------------------------------------------------- 1 | #ifndef __DK_LOCK__ 2 | #define __DK_LOCK__ 3 | #include 4 | #include 5 | 6 | class Mutex 7 | { 8 | public: 9 | Mutex() { 10 | pthread_mutex_init(&mtx_, NULL); 11 | }; 12 | 13 | ~Mutex() { 14 | pthread_mutex_destroy(&mtx_); 15 | }; 16 | 17 | void lock() { 18 | pthread_mutex_lock(&mtx_); 19 | } 20 | 21 | void unlock() { 22 | pthread_mutex_unlock(&mtx_); 23 | } 24 | 25 | private: 26 | pthread_mutex_t mtx_; 27 | }; 28 | 29 | class Lock { 30 | public: 31 | Lock(Mutex &mtx) { 32 | pmtx_ = &mtx; 33 | mtx.lock(); 34 | } 35 | 36 | ~Lock() { 37 | if (pmtx_) 38 | pmtx_->unlock(); 39 | } 40 | private: 41 | Mutex *pmtx_; 42 | }; 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | OBJECTS=dk_base_server.o dk_base_connection.o dk_http.o dk_util.o \ 2 | dk_base_thread.o dk_ev_thread.o dk_worker.o dk_thread_pool.o 3 | 4 | CXXFLAGS=-g -Wall -D_DONKEY_DEBUG 5 | 6 | LIB_NAME=libdk_base_server.a 7 | 8 | $(LIB_NAME):$(OBJECTS) 9 | ar cru $(LIB_NAME) $(OBJECTS) 10 | ranlib $(LIB_NAME) 11 | 12 | 13 | SOURCES=dk_base_server.cpp dk_base_connection.cpp dk_http.cpp dk_util.cpp \ 14 | dk_base_thread.cpp dk_ev_thread.cpp dk_worker.cpp dk_thread_pool.cpp 15 | 16 | DEPS=.depend 17 | 18 | $(DEPS):$(SOURCES) 19 | makedepend -f- -I./ -Y $(SOURCES) 2> /dev/null > $(DEPS) 20 | 21 | include $(DEPS) 22 | 23 | .PHONY:clean 24 | clean: 25 | rm $(LIB_NAME) $(OBJECTS) 26 | 27 | .PHONY:distclean 28 | distclean: 29 | $(MAKE) clean 30 | rm $(DEPS) 31 | -------------------------------------------------------------------------------- /src/dk_base_thread.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #ifndef __DONKEY_BASE_THREAD_INCLUDE__ 7 | #define __DONKEY_BASE_THREAD_INCLUDE__ 8 | 9 | #include 10 | 11 | class DKBaseThread { 12 | 13 | public: 14 | DKBaseThread() : thread_id_(0) { 15 | } 16 | 17 | virtual ~DKBaseThread() { 18 | } 19 | 20 | void Create(); 21 | 22 | int Wait() { 23 | return pthread_join(thread_id_, NULL); 24 | } 25 | 26 | virtual bool Stop() { 27 | return true; 28 | }; 29 | 30 | pthread_t get_thread_id() { 31 | return thread_id_; 32 | } 33 | 34 | protected: 35 | virtual int ThreadRoutine() = 0; 36 | static void *PThreadRoutine(void *arg); 37 | 38 | private: 39 | pthread_t thread_id_; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/dk_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __DK_QUEUE_H__ 2 | #define __DK_QUEUE_H__ 3 | #include 4 | #include 5 | #include "dk_lock.h" 6 | 7 | 8 | 9 | template 10 | class LockQueue 11 | { 12 | Mutex mtx_; 13 | 14 | protected: 15 | std::queue q_; 16 | 17 | public: 18 | 19 | typename std::queue::size_type size() { 20 | Lock lock(mtx_); 21 | return q_.size(); 22 | } 23 | 24 | bool empty() { 25 | Lock lock(mtx_); 26 | return q_.empty(); 27 | } 28 | 29 | void push(const T& elem) { 30 | Lock lock(mtx_); 31 | q_.push(elem); 32 | } 33 | 34 | T pop() { 35 | Lock lock(mtx_); 36 | if (q_.empty()) throw std::exception(); 37 | T elem(q_.front()); 38 | q_.pop(); 39 | return elem; 40 | } 41 | 42 | T& front() { 43 | Lock lock(mtx_); 44 | if (q_.empty()) throw std::exception(); 45 | return q_.front(); 46 | } 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/dk_worker.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #include "dk_common.h" 7 | #include "dk_worker.h" 8 | 9 | int DKWorker::ThreadRoutine() { 10 | int ret = 0; 11 | 12 | sem_t *asem = extern_event_sem_ ? extern_event_sem_ : &event_sem_; 13 | LockQueue *aque = 14 | extern_pending_cbs_ ? extern_pending_cbs_ : &pending_cbs_; 15 | 16 | for ( ; !stop_ ; ) { 17 | /* no events and pending cb */ 18 | if (pending_cbs_.empty()) { 19 | struct timespec ts; 20 | if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { 21 | ts.tv_sec += 1; 22 | if (sem_timedwait(asem, &ts) == -1) { 23 | if (errno == EINTR) { 24 | continue; 25 | } else if (errno == ETIMEDOUT) { 26 | } else { 27 | perror("sem_timedwait"); 28 | break; 29 | } 30 | } 31 | } 32 | } 33 | 34 | for ( ; !aque->empty(); ) { 35 | try { 36 | DeferredCb deferred_cb = aque->pop(); 37 | deferred_cb.Call(this); 38 | } catch (const std::exception e) { 39 | break; 40 | } 41 | } 42 | } 43 | 44 | return ret; 45 | } 46 | -------------------------------------------------------------------------------- /src/dk_util.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * author:jlijian3@gmail.com 4 | * date:2012-03-08 5 | */ 6 | 7 | #include "dk_util.h" 8 | #include "dk_log.h" 9 | 10 | using namespace std; 11 | 12 | bool DKGetHostByName(const string &host, string &ip) { 13 | struct hostent hent, *ent; 14 | char buf[2048]; 15 | int err; 16 | struct in_addr addr; 17 | 18 | /* Libevent evget_addrinfo has a bug */ 19 | if (gethostbyname_r(host.c_str(), &hent, buf, sizeof(buf), &ent, &err) || 20 | ent == NULL) { 21 | DK_DEBUG("[error] %s: gethostbyname_r %s\n", __func__, host.c_str()); 22 | ip = host; 23 | return false; 24 | } 25 | 26 | for (int i = 0; ent->h_addr_list[i]; ++i) { 27 | memcpy(&addr, ent->h_addr_list[i], ent->h_length); 28 | ip = inet_ntoa(addr); 29 | break; 30 | } 31 | 32 | return true; 33 | } 34 | 35 | bool DKGetHostName(std::string &host) { 36 | char buf[NI_MAXHOST]; 37 | if (gethostname(buf, sizeof(buf)) == 0) { 38 | host.assign(buf, strlen(buf)); 39 | return true; 40 | } 41 | return false; 42 | } 43 | 44 | bool DKGetHostIp(std::string &ip) { 45 | string host; 46 | DKGetHostName(host); 47 | return DKGetHostByName(host, ip); 48 | } 49 | -------------------------------------------------------------------------------- /src/dk_log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-22 4 | */ 5 | 6 | #ifndef __DONKEY_LOG_INCLUDE__ 7 | #define __DONKEY_LOG_INCLUDE__ 8 | 9 | #include 10 | #include 11 | 12 | typedef void (*log_func_t)(const char *msg, int msg_len); 13 | 14 | class DKLog { 15 | public: 16 | static void Debug(const char *format, ...) { 17 | va_list va; 18 | va_start(va, format); 19 | 20 | if (log_func_) { 21 | int fmt_len; 22 | char buf[1024]; 23 | int buf_size = sizeof(buf); 24 | 25 | fmt_len = vsnprintf(buf, buf_size, format, va); 26 | if (fmt_len > buf_size) { 27 | *(buf + buf_size - 1) = '\0'; 28 | fmt_len = buf_size - 1; 29 | } 30 | 31 | log_func_(buf, fmt_len); 32 | } else { 33 | FILE *f = fstream_?fstream_:stderr; 34 | vfprintf(f, format, va); 35 | } 36 | va_end(va); 37 | } 38 | 39 | static void set_log_func(log_func_t log_func) { 40 | log_func_ = log_func; 41 | } 42 | 43 | static void set_fstream(FILE *f) { 44 | fstream_ = f; 45 | } 46 | 47 | private: 48 | static log_func_t log_func_; 49 | static FILE *fstream_; 50 | }; 51 | 52 | #define DK_DEBUG(args...) DKLog::Debug(args) 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/dk_internal.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-23 4 | */ 5 | 6 | #ifndef __DONKEY_INTERVAL_INCLUDE__ 7 | #define __DONKEY_INTERVAL_INCLUDE__ 8 | 9 | class DKBaseThread; 10 | 11 | enum READ_STATUS { 12 | READ_BAD_CLIENT, 13 | READ_NEED_MORE_DATA, 14 | READ_ALL_DATA, 15 | READ_MEMORY_ERROR, 16 | READ_INNER_ERROR 17 | }; 18 | 19 | enum DKConnectionKind { 20 | CON_INCOMING, 21 | CON_OUTGOING 22 | }; 23 | 24 | #define DK_CONNECT_TIMEOUT 45 25 | #define DK_READ_TIMEOUT 50 26 | #define DK_WRITE_TIMEOUT 50 27 | 28 | enum DKConnectionState { 29 | DKCON_DISCONNECTED, 30 | DKCON_CONNECTING, 31 | DKCON_IDLE, 32 | DKCON_READING, 33 | DKCON_WRITING 34 | }; 35 | 36 | enum DKConnectionError { 37 | DKCON_ERROR_NONE, 38 | DKCON_ERROR_TIMEOUT, 39 | DKCON_ERROR_EOF, 40 | DKCON_ERROR_ERRNO, 41 | DKCON_ERROR_BUFFER, 42 | DKCON_ERROR_PARSE_ERROR 43 | }; 44 | 45 | typedef void (*deferred_cb_fn)(DKBaseThread *thread, void *arg); 46 | 47 | class DeferredCb { 48 | public: 49 | DeferredCb(deferred_cb_fn cb, void *arg) 50 | : cb_(cb), arg_(arg) { 51 | } 52 | 53 | void Call(DKBaseThread *thread) { 54 | if (cb_) 55 | cb_(thread, arg_); 56 | } 57 | 58 | private: 59 | deferred_cb_fn cb_; 60 | void *arg_; 61 | }; 62 | 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/dk_ev_thread.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #include "dk_common.h" 7 | #include "dk_ev_thread.h" 8 | 9 | bool DKEventThread::Init() { 10 | base_ = event_base_new(); 11 | 12 | if (!base_) 13 | return false; 14 | 15 | int fds[2]; 16 | if (pipe(fds)) { 17 | perror("Can't create notify pipe"); 18 | return false; 19 | } 20 | 21 | notify_receive_fd_ = fds[0]; 22 | notify_send_fd_ = fds[1]; 23 | evutil_make_socket_nonblocking(notify_receive_fd_); 24 | evutil_make_socket_nonblocking(notify_send_fd_); 25 | 26 | event_assign(¬ify_event_, base_, notify_receive_fd_ , 27 | EV_READ | EV_PERSIST, EventNotifyCb, this); 28 | if (event_add(¬ify_event_, 0) == -1) 29 | return false; 30 | 31 | return true; 32 | } 33 | 34 | int DKEventThread::ThreadRoutine() { 35 | return event_base_dispatch(base_); 36 | } 37 | 38 | void DKEventThread::EventNotifyCb(int fd, short which, void *arg) { 39 | DKEventThread *worker = (DKEventThread *)arg; 40 | assert(worker); 41 | 42 | worker->NotifyCb(fd, which); 43 | } 44 | 45 | void DKEventThread::NotifyCb(int fd, short which) { 46 | assert(base_); 47 | char buf[1024]; 48 | 49 | while (read(fd, buf, sizeof(buf)) > 0) 50 | ; 51 | 52 | for ( ; !pending_cbs_.empty(); ) { 53 | try { 54 | DeferredCb deferred_cb = pending_cbs_.pop(); 55 | deferred_cb.Call(this); 56 | } catch (const std::exception e) { 57 | break; 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/dk_worker.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #ifndef __DONKEY_WORKER__INCLUDE__ 7 | #define __DONKEY_WORKER__INCLUDE__ 8 | 9 | #include "dk_queue.h" 10 | #include "dk_base_thread.h" 11 | #include "dk_internal.h" 12 | 13 | class DKWorker : public DKBaseThread { 14 | public: 15 | DKWorker() 16 | : stop_(false), 17 | extern_event_sem_(NULL), 18 | extern_pending_cbs_(NULL) { 19 | } 20 | 21 | bool Init() { 22 | return 0 == sem_init(&event_sem_, 0, 0); 23 | } 24 | 25 | virtual ~DKWorker() { 26 | } 27 | 28 | virtual bool Stop() { 29 | stop_ = true; 30 | return true; 31 | } 32 | 33 | size_t PendingQueSize() { 34 | return pending_cbs_.size(); 35 | } 36 | 37 | virtual int ThreadRoutine(); 38 | 39 | bool CallInThread(deferred_cb_fn cb, void *arg) { 40 | LockQueue *aque = 41 | extern_pending_cbs_ ? extern_pending_cbs_ : &pending_cbs_; 42 | aque->push(DeferredCb(cb, arg)); 43 | sem_t *asem = extern_event_sem_ ? extern_event_sem_ : &event_sem_; 44 | return 0 == sem_post(asem); 45 | } 46 | 47 | void set_extern_sem_queue(sem_t *asem, LockQueue *aque) { 48 | extern_event_sem_ = asem; 49 | extern_pending_cbs_ = aque; 50 | } 51 | 52 | protected: 53 | bool stop_; 54 | sem_t event_sem_; 55 | LockQueue pending_cbs_; 56 | 57 | sem_t *extern_event_sem_; 58 | LockQueue *extern_pending_cbs_; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/dk_ev_thread.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) jlijian3@gmail.com 4 | */ 5 | 6 | #ifndef __DONKEY_EVENT_THREAD__INCLUDE__ 7 | #define __DONKEY_EVENT_THREAD__INCLUDE__ 8 | 9 | #include "dk_queue.h" 10 | #include "dk_base_thread.h" 11 | #include "dk_internal.h" 12 | 13 | class DKEventThread : public DKBaseThread { 14 | public: 15 | DKEventThread() : base_(NULL) { 16 | } 17 | 18 | bool Init(); 19 | 20 | virtual ~DKEventThread() { 21 | if (base_) 22 | event_base_free(base_); 23 | } 24 | 25 | virtual bool Stop() { 26 | if (base_) 27 | return 0 == event_base_loopbreak(base_); 28 | return false; 29 | } 30 | 31 | size_t PendingQueSize() { 32 | return pending_cbs_.size(); 33 | } 34 | 35 | struct event_base *get_base() { 36 | return base_; 37 | } 38 | 39 | bool CallInThread(deferred_cb_fn cb, void *arg) { 40 | pending_cbs_.push(DeferredCb(cb, arg)); 41 | return Notify(); 42 | } 43 | 44 | virtual int ThreadRoutine(); 45 | 46 | void NotifyCb(int fd, short which); 47 | 48 | static void EventNotifyCb(int fd, short which, void *arg); 49 | 50 | 51 | bool Notify() { 52 | int rv, cnt = 0; 53 | do { 54 | rv = write(notify_send_fd_, "", 1); 55 | } while (rv < 0 && errno == EAGAIN && ++cnt < 100); 56 | return rv > 0; 57 | } 58 | 59 | protected: 60 | struct event_base *base_; 61 | struct event notify_event_; 62 | int notify_receive_fd_; 63 | int notify_send_fd_; 64 | LockQueue pending_cbs_; 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/dk_base_server.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-08 4 | */ 5 | 6 | #ifndef __DONKEY_SERVER_INCLUDE__ 7 | #define __DONKEY_SERVER_INCLUDE__ 8 | 9 | #include "dk_common.h" 10 | #include "dk_ev_thread.h" 11 | 12 | struct event_base; 13 | struct evconnlistener; 14 | struct event; 15 | struct sockaddr_in; 16 | 17 | class DKBaseConnection; 18 | 19 | class DKBaseServer : public DKEventThread { 20 | public: 21 | enum {FREE_CONNS = 200}; 22 | DKBaseServer(); 23 | virtual ~DKBaseServer(); 24 | 25 | bool Init(); 26 | 27 | bool StartListenTCP(const char *address, 28 | unsigned short port, 29 | int backlog); 30 | 31 | int EventLoop(); 32 | 33 | bool MakeConnection(int fd, const char *host, unsigned short port); 34 | 35 | void FreeConn(DKBaseConnection *conn); 36 | 37 | DKBaseConnection *get_conn(int conn_id); 38 | 39 | int get_conns_map_size() { 40 | return conns_map_.size(); 41 | } 42 | 43 | int get_free_conns_size() { 44 | return free_conns_.size(); 45 | } 46 | 47 | unsigned int get_current_time() { 48 | return current_time_; 49 | } 50 | 51 | void set_timeout(int timeout_in_secs) { 52 | timeout_ = timeout_in_secs; 53 | } 54 | 55 | protected: 56 | virtual void ClockCallback() { 57 | } 58 | 59 | virtual DKBaseConnection *NewConnection(); 60 | 61 | virtual void ConnectionMade(DKBaseConnection *conn) { 62 | } 63 | 64 | private: 65 | static void IncomingConnFreeCallback(DKBaseConnection *conn, void *arg); 66 | 67 | private: 68 | static void ListenerCallback(struct evconnlistener *listener, 69 | int fd, 70 | struct sockaddr *sa, 71 | int socklen, 72 | void *arg); 73 | 74 | static void ClockHandler(int fd, short which, void *arg); 75 | 76 | private: 77 | /*struct event_base *base_;*/ 78 | struct evconnlistener *listener_; 79 | struct event *signal_event_; 80 | 81 | struct sockaddr_in sin_; 82 | std::vector free_conns_; 83 | static unsigned int current_time_; 84 | std::map conns_map_; 85 | int timeout_; 86 | }; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/example/tcp_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "dk_core.h" 7 | 8 | #define dlog1 printf 9 | #define HOST "localhost" 10 | #define PORT 60006 11 | 12 | static DKBaseServer *server; 13 | 14 | class MyConnection: public DKBaseConnection { 15 | virtual enum READ_STATUS OnRead() { 16 | set_keep_alive(true); 17 | char *resp = (char *)evbuffer_pullup(get_input_buffer(), -1); 18 | dlog1(">>>>>>>>>>>>>>\n"); 19 | dlog1("Connection::%s: %s\n", __func__, resp); 20 | dlog1("<<<<<<<<<<<<<<\n"); 21 | evbuffer_drain(get_input_buffer(), -1); 22 | //server->Stop(); 23 | return READ_ALL_DATA; 24 | } 25 | 26 | virtual void OnConnect() { 27 | dlog1("new Connection fd:%d %s:%d\n", fd_, host_.c_str(), port_); 28 | } 29 | 30 | virtual void OnClose() { 31 | dlog1("Connection close fd:%d %s:%d\n", 32 | fd_, host_.c_str(), port_); 33 | server->Stop(); 34 | } 35 | 36 | virtual void OnError() { 37 | dlog1("Connection Error %s\n", this->get_error_string()); 38 | server->Stop(); 39 | } 40 | }; 41 | 42 | static MyConnection *my_conn; 43 | 44 | void send_tcp_request(const void *data, int len) { 45 | assert(data && len > 0); 46 | 47 | if (!my_conn) { 48 | my_conn = new MyConnection(); 49 | if (!my_conn) { 50 | dlog1("new PSConnection failed\n"); 51 | return; 52 | } 53 | 54 | bool res = my_conn->Init(server->get_base(), -1, HOST, PORT); 55 | my_conn->Connect(); 56 | if (!res) { 57 | delete my_conn; 58 | my_conn = NULL; 59 | dlog1("my_conn_->Init failed\n"); 60 | return; 61 | } 62 | my_conn->set_timeout(10); 63 | my_conn->set_keep_alive(true); 64 | } 65 | 66 | my_conn->set_timeout(10); 67 | my_conn->set_keep_alive(true); 68 | my_conn->Send(data, len); 69 | } 70 | 71 | int main(int argc, char **argv) { 72 | server = new DKBaseServer(); 73 | 74 | if (!server || !server->Init()) { 75 | dlog1("new DKBaseServer Init error\n"); 76 | return 1; 77 | } 78 | 79 | dlog1("server start ......\n"); 80 | 81 | signal(SIGPIPE, SIG_IGN); 82 | signal(SIGHUP, SIG_IGN); 83 | 84 | dlog1("begin http_load run ....\n"); 85 | const char *buf = "GET / HTTP/1.1\r\nHost: dev5\r\nConnection: keep-alive\r\n\r\n"; 86 | send_tcp_request(buf, strlen(buf)); 87 | server->EventLoop(); 88 | 89 | delete server; 90 | } 91 | -------------------------------------------------------------------------------- /src/example/http_client.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "dk_core.h" 8 | 9 | #define HTTP_HOST "localhost" 10 | #define HTTP_PORT 60006 11 | #define HTTP_POST_DATA "aaaaaaaaaaaaaaaaaaaaa" 12 | #define PARALLEL 1 13 | #define TIMEOUT 3600 14 | #define HTTP_CONNS 10 15 | using namespace std; 16 | 17 | DKEventThread *ev_thread; 18 | DKHttpClient *s_http_client; 19 | 20 | static bool http_run_once(); 21 | 22 | static int start_time; 23 | static int end_time; 24 | static long send_http_reqs = 0; 25 | static long recv_http_resps = 0; 26 | 27 | class MyHttpRequest: public DKHttpRequest { 28 | virtual void HandleResponse(struct evhttp_request *req, void *arg) { 29 | DKHttpRequest::DebugResponse(req); 30 | recv_http_resps++; 31 | http_run_once(); 32 | } 33 | }; 34 | 35 | static bool http_run_once() { 36 | static int http_run_init = 0; 37 | static int last_secs = 0; 38 | if (!ev_thread) 39 | return false; 40 | 41 | if (!s_http_client) { 42 | s_http_client = new DKHttpClient(); 43 | if (!s_http_client) 44 | return false; 45 | 46 | if (!s_http_client->Init(ev_thread->get_base(), HTTP_HOST, HTTP_PORT, HTTP_CONNS)) 47 | return false; 48 | 49 | s_http_client->SetTimeout(HTTP_CONNS); 50 | } 51 | 52 | int parallel = 1; 53 | if (http_run_init == 0) { 54 | parallel = PARALLEL; 55 | start_time = time(0); 56 | http_run_init = 1; 57 | } 58 | 59 | for (int i = 0; i < parallel; i++) { 60 | send_http_reqs++; 61 | if (send_http_reqs % 10000 == 0) { 62 | end_time = time(0); 63 | int secs = end_time - start_time; 64 | if (secs > last_secs) { 65 | last_secs = secs; 66 | fprintf(stderr, "send_reqs:%d, recv_reqs:%d, secs:%d\n", 67 | send_http_reqs, recv_http_resps, secs); 68 | } 69 | } 70 | 71 | MyHttpRequest *req = new MyHttpRequest(); 72 | if (!req) 73 | return false; 74 | 75 | if (!req->Init()) { 76 | delete req; 77 | return false; 78 | } 79 | 80 | req->AddHeader("Host", HTTP_HOST); 81 | req->AddHeader("Connection", "keep-alive"); 82 | 83 | //int post_data_len = strlen(HTTP_POST_DATA); 84 | //char temp[16]; 85 | //sprintf(temp, "%d", post_data_len); 86 | 87 | //req->AddHeader("Content-length", temp); 88 | //req->AddPostData((void *)HTTP_POST_DATA, post_data_len); 89 | 90 | if (!s_http_client->SendRequest(req, EVHTTP_REQ_GET, "/dump")) { 91 | delete req; 92 | } 93 | } 94 | } 95 | 96 | int main(int argc, char **argv) { 97 | ev_thread = new DKEventThread(); 98 | 99 | if (!ev_thread || !ev_thread->Init()) { 100 | fprintf(stderr, "new DKBaseServer Init error\n"); 101 | return 1; 102 | } 103 | 104 | signal(SIGPIPE, SIG_IGN); 105 | signal(SIGHUP, SIG_IGN); 106 | 107 | fprintf(stderr, "begin http_load run ...."); 108 | http_run_once(); 109 | ev_thread->ThreadRoutine(); 110 | 111 | delete ev_thread; 112 | } 113 | -------------------------------------------------------------------------------- /src/example/echo_srv.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "dk_core.h" 8 | 9 | #define RESP_DATA "'HTTP/1.1 200 OK\r\nServer: nginx/0.8.44\r\nContent-length: 5\r\nConnection: keep-alive\r\n\r\nhello" 10 | #define dlog1 printf 11 | 12 | using namespace std; 13 | 14 | class MyServer; 15 | 16 | static MyServer *server; 17 | 18 | class SrvConnection: public DKBaseConnection { 19 | 20 | virtual void OnConnect() { 21 | dlog1("new SrvConnection fd:%d %s:%d\n", fd_, host_.c_str(), port_); 22 | } 23 | 24 | virtual void OnClose() { 25 | dlog1("SrvConnection close fd:%d %s:%d\n", 26 | fd_, host_.c_str(), port_); 27 | } 28 | 29 | virtual void OnError() { 30 | dlog1("SrvConnection Error %s\n", this->get_error_string()); 31 | } 32 | 33 | virtual void OnWrite() { 34 | //dlog1("SrvConnection %s\n", __func__); 35 | 36 | } 37 | 38 | virtual enum READ_STATUS OnRead() { 39 | struct evbuffer *inbuf = get_input_buffer(); 40 | size_t inbuf_size = evbuffer_get_length(inbuf); 41 | 42 | //dlog1("OnRead %d\n", inbuf_size); 43 | if (inbuf_size <= 0) 44 | return READ_BAD_CLIENT; 45 | 46 | char buf[1024]; 47 | int nread = evbuffer_remove(inbuf, buf, sizeof(buf)); 48 | 49 | if (nread > 0) { 50 | 51 | if (strncmp(buf, "quit", 4) == 0) { 52 | /* close connection after response */ 53 | set_keep_alive(false); 54 | } else { 55 | set_keep_alive(true); 56 | } 57 | Send(buf, nread); 58 | //Send(RESP_DATA, strlen(RESP_DATA)); 59 | } else 60 | return READ_BAD_CLIENT; 61 | 62 | return READ_ALL_DATA; 63 | } 64 | }; 65 | 66 | class MyServer: public DKBaseServer { 67 | virtual void ClockCallback() { 68 | //dlog1("conns: %d\n", this->get_conns_map_size()); 69 | } 70 | 71 | virtual DKBaseConnection *NewConnection() { 72 | return new SrvConnection(); 73 | } 74 | 75 | virtual void ConnectionMade(DKBaseConnection *conn) { 76 | } 77 | }; 78 | 79 | static void thread_func(DKBaseThread *thread, void *arg) { 80 | printf("threadid:%d %d\n", thread->get_thread_id(), (long)arg); 81 | } 82 | 83 | #define PORT 60006 84 | 85 | int main(int argc, char **argv) { 86 | DKThreadPool th_pool; 87 | server = new MyServer(); 88 | 89 | if (!server || !server->Init()) { 90 | dlog1("new DKBaseServer Init error\n"); 91 | return 1; 92 | } 93 | 94 | dlog1("server start ......\n"); 95 | 96 | th_pool.Init(10); 97 | for (int i=0; i<10000; i++) { 98 | th_pool.CallInThread(thread_func, (void *)i); 99 | } 100 | 101 | signal(SIGPIPE, SIG_IGN); 102 | signal(SIGHUP, SIG_IGN); 103 | 104 | int port = PORT; 105 | if (argc > 1) { 106 | port = atoi(argv[1]); 107 | } 108 | 109 | if (!server->StartListenTCP(NULL, port, 1024)) { 110 | dlog1("StartListenTCP fail (:%d)\n", port); 111 | return 1; 112 | } 113 | 114 | server->set_timeout(10); 115 | server->EventLoop(); 116 | 117 | delete server; 118 | } 119 | -------------------------------------------------------------------------------- /src/dk_http.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03 4 | */ 5 | 6 | #include "dk_core.h" 7 | using namespace std; 8 | 9 | 10 | /*********** DKHttpRequest ************/ 11 | 12 | void DKHttpRequest::HandleResponse(struct evhttp_request *req, void *arg) { 13 | DebugResponse(req); 14 | } 15 | 16 | void DKHttpRequest::EventHttpRequestCb( 17 | struct evhttp_request *req, void *arg) { 18 | DKHttpRequest *http_req = (DKHttpRequest *)arg; 19 | 20 | if (http_req) { 21 | http_req->DoHandleResponse(req); 22 | delete http_req; 23 | } 24 | } 25 | 26 | void DKHttpRequest::DebugResponse(struct evhttp_request *req) { 27 | if (!req) 28 | return; 29 | 30 | struct evkeyvalq *headers; 31 | struct evbuffer *input_buffer; 32 | const char * value; 33 | int content_length = -1; 34 | string s_headers = "headers:\n"; 35 | string body; 36 | 37 | DK_DEBUG(">>>>>>>>>>>\n"); 38 | DK_DEBUG("Http status %d\n", evhttp_request_get_response_code(req)); 39 | headers = evhttp_request_get_input_headers(req); 40 | DebugHeaders(headers); 41 | 42 | value = evhttp_find_header(headers, "Content-Length"); 43 | if (value) 44 | content_length = atoi(value); 45 | 46 | input_buffer = evhttp_request_get_input_buffer(req); 47 | 48 | if (input_buffer) { 49 | const char *tmp = (const char *)evbuffer_pullup(input_buffer, content_length); 50 | if (tmp) 51 | body.assign(tmp, content_length); 52 | DK_DEBUG("Http body: %.*s\n", body.size(), body.data()); 53 | } 54 | 55 | DK_DEBUG("<<<<<<<<<<<\n"); 56 | } 57 | 58 | void DKHttpRequest::DebugRequest(struct evhttp_request *req) { 59 | if (!req) 60 | return; 61 | struct evkeyvalq *headers = evhttp_request_get_output_headers(req); 62 | DK_DEBUG(">>>>>>>>>>>\n"); 63 | DebugHeaders(headers); 64 | DK_DEBUG("<<<<<<<<<<<\n"); 65 | } 66 | 67 | void DKHttpRequest::DebugHeaders(struct evkeyvalq *headers) { 68 | struct evkeyval *header; 69 | string s_headers = "Http headers:\n"; 70 | 71 | if (!headers) 72 | return; 73 | 74 | for (header = headers->tqh_first; header; 75 | header = header->next.tqe_next) { 76 | s_headers += header->key; 77 | s_headers += ":"; 78 | s_headers += header->value; 79 | s_headers += "\n"; 80 | } 81 | 82 | DK_DEBUG("%.*s\n", s_headers.size(), s_headers.data()); 83 | } 84 | 85 | 86 | /*********** DKHttpClient ************/ 87 | 88 | bool DKHttpClient::Init( 89 | struct event_base *evbase, const char *host, unsigned short port, int conns) { 90 | if (!http_conns_.empty()) 91 | return false; 92 | 93 | if (!evbase || !host || port == 0) 94 | return false; 95 | 96 | host_ = host; 97 | port_ = port; 98 | 99 | string ip; 100 | 101 | /* Libevent evget_addrinfo has a bug */ 102 | DKGetHostByName(host, ip); 103 | if (ip.empty()) 104 | ip = host; 105 | 106 | if (conns <=0) 107 | conns = 10; 108 | 109 | for (int i = 0; i < conns; i++) { 110 | struct evhttp_connection * conn = 111 | evhttp_connection_base_new(evbase, NULL, ip.c_str(), port); 112 | if (!conn) 113 | return false; 114 | 115 | http_conns_.push_back(conn); 116 | evhttp_connection_set_closecb(conn, EventHttpCloseCb, this); 117 | } 118 | 119 | return true; 120 | } 121 | 122 | void DKHttpClient::EventHttpCloseCb(struct evhttp_connection *conn, void *arg) { 123 | DKHttpClient *http_client = (DKHttpClient *)arg; 124 | 125 | if (http_client) 126 | http_client->OnClose(conn); 127 | } 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/dk_http.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03 4 | */ 5 | 6 | #ifndef __DONKEY_HTTP_INCLUDE__ 7 | #define __DONKEY_HTTP_INCLUDE__ 8 | 9 | #include "dk_common.h" 10 | 11 | class DKHttpClient; 12 | 13 | class DKHttpRequest { 14 | public: 15 | DKHttpRequest() : http_req_(NULL), arg_(NULL) { 16 | } 17 | 18 | virtual ~DKHttpRequest() { 19 | } 20 | 21 | bool Init(void *arg=NULL) { 22 | http_req_ = evhttp_request_new(EventHttpRequestCb, (void *)this); 23 | arg_ = arg; 24 | return http_req_ != NULL; 25 | } 26 | 27 | bool AddHeader(const char *key, 28 | const char *value) { 29 | assert(http_req_); 30 | return 0 == evhttp_add_header(evhttp_request_get_output_headers(http_req_), 31 | key, value); 32 | } 33 | 34 | bool AddPostData(const void *data, size_t len) { 35 | assert(http_req_); 36 | return 0 == evbuffer_add(evhttp_request_get_output_buffer(http_req_), data, len); 37 | } 38 | 39 | struct evhttp_request *get_http_req() { 40 | return http_req_; 41 | } 42 | 43 | virtual void HandleResponse(struct evhttp_request *req, void *arg); 44 | void DoHandleResponse(struct evhttp_request *req) { 45 | HandleResponse(req, arg_); 46 | } 47 | 48 | void DebugResponse(struct evhttp_request *req); 49 | void DebugHeaders(struct evkeyvalq *headers); 50 | void DebugRequest(struct evhttp_request *req); 51 | 52 | private: 53 | static void EventHttpRequestCb(struct evhttp_request *req, void *arg); 54 | 55 | protected: 56 | struct evhttp_request *http_req_; 57 | void *arg_; 58 | }; 59 | 60 | class DKHttpClient { 61 | public: 62 | DKHttpClient() : last_conn_idx_(0) { 63 | } 64 | 65 | virtual ~DKHttpClient() { 66 | for (size_t i = 0; i < http_conns_.size(); i++) { 67 | if (http_conns_[i]) 68 | evhttp_connection_free(http_conns_[i]); 69 | } 70 | } 71 | 72 | bool Init(struct event_base *evbase, 73 | const char *host, 74 | unsigned short port, 75 | int conns=10); 76 | 77 | DKHttpRequest *NewRequest() { 78 | DKHttpRequest *req = new DKHttpRequest(); 79 | if (req && !req->Init()) { 80 | delete req; 81 | req = NULL; 82 | } 83 | 84 | return req; 85 | } 86 | 87 | struct evhttp_connection *get_http_conn() { 88 | if (http_conns_.empty()) 89 | return NULL; 90 | 91 | int idx = last_conn_idx_ % http_conns_.size(); 92 | last_conn_idx_ = idx + 1; 93 | return http_conns_[idx]; 94 | } 95 | 96 | void SetTimeout(int timeout_in_secs) { 97 | for (size_t i = 0; i < http_conns_.size(); i++) { 98 | evhttp_connection_set_timeout(http_conns_[i], timeout_in_secs); 99 | } 100 | } 101 | 102 | void SetRetries(int retries) { 103 | for (size_t i = 0; i < http_conns_.size(); i++) { 104 | evhttp_connection_set_retries(http_conns_[i], retries); 105 | } 106 | } 107 | 108 | void SetLocalAddress(const char *address) { 109 | for (size_t i = 0; i < http_conns_.size(); i++) { 110 | evhttp_connection_set_local_address(http_conns_[i], address); 111 | } 112 | } 113 | 114 | void SetLocalPort(int port) { 115 | for (size_t i = 0; i < http_conns_.size(); i++) { 116 | evhttp_connection_set_local_port(http_conns_[i], port); 117 | } 118 | } 119 | 120 | /* defined in 121 | * enum evhttp_cmd_type { 122 | EVHTTP_REQ_GET = 1 << 0, 123 | EVHTTP_REQ_POST = 1 << 1, 124 | EVHTTP_REQ_HEAD = 1 << 2, 125 | EVHTTP_REQ_PUT = 1 << 3, 126 | EVHTTP_REQ_DELETE = 1 << 4, 127 | EVHTTP_REQ_OPTIONS = 1 << 5, 128 | EVHTTP_REQ_TRACE = 1 << 6, 129 | EVHTTP_REQ_CONNECT = 1 << 7, 130 | EVHTTP_REQ_PATCH = 1 << 8 131 | }; 132 | 133 | dk_http_req will be auto free after response 134 | */ 135 | bool SendRequest(DKHttpRequest *dk_http_req, 136 | enum evhttp_cmd_type cmd_type, 137 | const char *uri) { 138 | struct evhttp_connection *conn = get_http_conn(); 139 | if (!conn || !dk_http_req || !uri) 140 | return false; 141 | 142 | return 0 == evhttp_make_request( 143 | conn, dk_http_req->get_http_req(), cmd_type, uri); 144 | } 145 | 146 | 147 | virtual void OnClose(struct evhttp_connection *conn) {} 148 | 149 | private: 150 | static void EventHttpCloseCb(struct evhttp_connection *conn, void *arg); 151 | 152 | protected: 153 | std::vector http_conns_; 154 | std::string host_; 155 | unsigned short port_; 156 | int last_conn_idx_; 157 | }; 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/dk_base_server.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-08 4 | */ 5 | 6 | #include "dk_core.h" 7 | #include "dk_log.h" 8 | 9 | using namespace std; 10 | 11 | log_func_t DKLog::log_func_ = NULL; 12 | FILE *DKLog::fstream_ = NULL; 13 | 14 | unsigned int DKBaseServer::current_time_; 15 | 16 | DKBaseServer::DKBaseServer() 17 | : listener_(NULL), 18 | signal_event_(NULL), 19 | timeout_(-1) { 20 | } 21 | 22 | DKBaseServer::~DKBaseServer() { 23 | if (listener_) 24 | evconnlistener_free(listener_); 25 | if (signal_event_) 26 | event_free(signal_event_); 27 | } 28 | 29 | bool DKBaseServer::Init() { 30 | DKEventThread::Init(); 31 | ClockHandler(0, 0, this); 32 | return true; 33 | } 34 | 35 | bool DKBaseServer::StartListenTCP(const char *address, 36 | unsigned short port, 37 | int backlog) { 38 | if (!base_) 39 | return false; 40 | 41 | if (listener_) 42 | return false; 43 | 44 | memset(&sin_, 0, sizeof(sin_)); 45 | sin_.sin_family = AF_INET; 46 | sin_.sin_port = htons(port); 47 | if (address) 48 | inet_aton(address, &sin_.sin_addr); 49 | 50 | listener_ = evconnlistener_new_bind(base_, ListenerCallback, 51 | (void *)this, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, 52 | backlog, (struct sockaddr*)&sin_, sizeof(sin_)); 53 | 54 | if (!listener_) { 55 | DK_DEBUG("[error] %s %s\n", __func__, strerror(errno)); 56 | return false; 57 | } 58 | 59 | return true; 60 | } 61 | 62 | int DKBaseServer::EventLoop() { 63 | return ThreadRoutine(); 64 | } 65 | 66 | bool DKBaseServer::MakeConnection(int fd, 67 | const char *host, 68 | unsigned short port) { 69 | DKBaseConnection *conn = NULL; 70 | if (free_conns_.empty()) { 71 | conn = NewConnection(); 72 | } else { 73 | conn = free_conns_.back(); 74 | free_conns_.pop_back(); 75 | DK_DEBUG("%s get free conn %x\n", __func__, conn); 76 | } 77 | 78 | 79 | if (!conn) { 80 | evutil_closesocket(fd); 81 | return false; 82 | } 83 | 84 | if (!conn->Init(base_, fd, host, port)) { 85 | evutil_closesocket(fd); 86 | delete conn; 87 | return false; 88 | } 89 | 90 | map::iterator it = conns_map_.find(conn->get_id()); 91 | if (it != conns_map_.end() && it->second != NULL) { 92 | DKBaseConnection *old_conn = it->second; 93 | DK_DEBUG("[error] %s: conn_id %d fd %d already exists in conns_map_\n", 94 | __func__, old_conn->get_id(), old_conn->get_fd()); 95 | conn->Reset(); 96 | delete conn; 97 | return false; 98 | } else 99 | conns_map_[conn->get_id()] = conn; 100 | 101 | conn->set_incoming_conn_free_cb(IncomingConnFreeCallback, this); 102 | 103 | ConnectionMade(conn); 104 | 105 | conn->ConnectMade(); 106 | 107 | conn->set_timeout(timeout_); 108 | return conn->StartRead(); 109 | } 110 | 111 | void DKBaseServer::ListenerCallback(struct evconnlistener *listener, 112 | evutil_socket_t fd, 113 | struct sockaddr *sa, 114 | int salen, 115 | void *arg) { 116 | DKBaseServer *server = (DKBaseServer *)arg; 117 | assert(server); 118 | 119 | char ntop[NI_MAXHOST]; 120 | char strport[NI_MAXSERV]; 121 | 122 | int ret = getnameinfo(sa, salen, 123 | ntop, sizeof(ntop), strport, sizeof(strport), 124 | NI_NUMERICHOST|NI_NUMERICSERV); 125 | if (ret != 0) 126 | perror("getnameinfo"); 127 | 128 | if (!server->MakeConnection(fd, ntop, atoi(strport))) 129 | DK_DEBUG("[error] %s MakeConnection failed\n", __func__); 130 | } 131 | 132 | void DKBaseServer::ClockHandler(int fd, short which, void *arg) { 133 | static struct event clockevent; 134 | static bool initialized = false; 135 | 136 | struct timeval t; 137 | DKBaseServer *server = (DKBaseServer *)arg; 138 | assert(server); 139 | assert(server->get_base()); 140 | 141 | if (initialized) { 142 | /* only delete the event if it's actually there. */ 143 | evtimer_del(&clockevent); 144 | } else { 145 | initialized = true; 146 | } 147 | 148 | struct timeval timer; 149 | 150 | gettimeofday(&timer, NULL); 151 | current_time_ = (unsigned int)timer.tv_sec; 152 | 153 | server->ClockCallback(); 154 | 155 | t.tv_sec = 1; 156 | t.tv_usec = 0; 157 | evtimer_set(&clockevent, ClockHandler, arg); 158 | event_base_set(server->get_base(), &clockevent); 159 | evtimer_add(&clockevent, &t); 160 | } 161 | 162 | void DKBaseServer::IncomingConnFreeCallback( 163 | DKBaseConnection *conn, void *arg) { 164 | DKBaseServer *me = (DKBaseServer *)arg; 165 | if (me) 166 | me->FreeConn(conn); 167 | } 168 | 169 | void DKBaseServer::FreeConn(DKBaseConnection *conn) { 170 | if (!conn) 171 | return; 172 | 173 | 174 | map::iterator it = conns_map_.find(conn->get_id()); 175 | 176 | if (it == conns_map_.end()) { 177 | DK_DEBUG("[error] %s: conn_id %d fd %d not exists in conns_map_\n", 178 | __func__, conn->get_id(), conn->get_fd()); 179 | } else 180 | conns_map_.erase(conn->get_id()); 181 | 182 | if (free_conns_.size() > FREE_CONNS) 183 | delete conn; 184 | else 185 | free_conns_.push_back(conn); 186 | } 187 | 188 | DKBaseConnection *DKBaseServer::get_conn(int conn_id) { 189 | map::iterator it = conns_map_.find(conn_id); 190 | if (it != conns_map_.end()) 191 | return it->second; 192 | else 193 | return NULL; 194 | } 195 | 196 | DKBaseConnection *DKBaseServer::NewConnection() { 197 | return new DKBaseConnection(); 198 | } 199 | -------------------------------------------------------------------------------- /src/dk_base_connection.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-08 4 | */ 5 | 6 | #ifndef __DONKEY_BASE_CONNECTION_INCLUDE__ 7 | #define __DONKEY_BASE_CONNECTION_INCLUDE__ 8 | 9 | #include "dk_common.h" 10 | #include "dk_internal.h" 11 | #include "dk_util.h" 12 | 13 | struct event_base; 14 | 15 | class DKBaseConnection { 16 | public: 17 | DKBaseConnection(); 18 | virtual ~DKBaseConnection(); 19 | 20 | bool Init(struct event_base *base, 21 | int fd, 22 | const char *host, 23 | unsigned short port); 24 | 25 | void Destruct(); 26 | 27 | void Reset(); 28 | 29 | bool Connect(); 30 | void ConnectMade(); 31 | 32 | bool StartRead(); 33 | bool StartWrite(); 34 | int TryWrite(); 35 | 36 | void EnableRead() { 37 | int event = bufferevent_get_enabled(bufev_); 38 | if (event & EV_READ) 39 | return; 40 | bufferevent_enable(bufev_, EV_READ); 41 | } 42 | 43 | void EnableWrite() { 44 | int event = bufferevent_get_enabled(bufev_); 45 | if (event & EV_WRITE) 46 | return; 47 | bufferevent_enable(bufev_, EV_WRITE); 48 | } 49 | 50 | void DisableRead() { 51 | int event = bufferevent_get_enabled(bufev_); 52 | if (event & EV_READ) 53 | bufferevent_disable(bufev_, EV_READ); 54 | } 55 | 56 | void DisableWrite() { 57 | int event = bufferevent_get_enabled(bufev_); 58 | if (event & EV_WRITE) 59 | bufferevent_disable(bufev_, EV_WRITE); 60 | } 61 | 62 | void DisableReadWrite() { 63 | int old = bufferevent_get_enabled(bufev_); 64 | if (old) 65 | bufferevent_disable(bufev_, old); 66 | } 67 | 68 | bool Send(struct evbuffer *buf) { 69 | if (kind_ == CON_OUTGOING) { 70 | if (!IsConnected()) { 71 | if (!Connect()) 72 | return false; 73 | else 74 | return 0 == evbuffer_add_buffer(temp_output_buf_, buf); 75 | } 76 | 77 | AddOutputBuffer(buf); 78 | return StartWrite(); 79 | } else { 80 | AddOutputBuffer(buf); 81 | return StartWrite(); 82 | } 83 | 84 | return true; 85 | } 86 | 87 | bool Send(const std::string &buf) { 88 | return Send(buf.data(), buf.size()); 89 | } 90 | 91 | bool Send(const void *buf, int len) { 92 | if (kind_ == CON_OUTGOING) { 93 | if (!IsConnected()) { 94 | if (!Connect()) 95 | return false; 96 | else 97 | return 0 == evbuffer_add(temp_output_buf_, buf, len); 98 | } 99 | 100 | AddOutputBuffer(buf, len); 101 | return StartWrite(); 102 | } else { 103 | AddOutputBuffer(buf, len); 104 | return StartWrite(); 105 | } 106 | 107 | return true; 108 | } 109 | 110 | void AddOutputBuffer(struct evbuffer *buf) { 111 | assert(buf); 112 | assert(bufev_); 113 | bufferevent_write_buffer(bufev_, buf); 114 | } 115 | 116 | void AddOutputBuffer(const std::string &data) { 117 | assert(bufev_); 118 | evbuffer_add(get_output_buffer(), data.data(), data.size()); 119 | } 120 | 121 | void AddOutputBuffer(const void *data, int len) { 122 | assert(bufev_); 123 | evbuffer_add(get_output_buffer(), data, len); 124 | } 125 | 126 | bool IsConnected() { 127 | switch (state_) { 128 | case DKCON_DISCONNECTED: 129 | case DKCON_CONNECTING: 130 | return false; 131 | 132 | default: 133 | return true; 134 | } 135 | } 136 | 137 | struct evbuffer *get_input_buffer() { 138 | return bufferevent_get_input(bufev_); 139 | } 140 | 141 | struct evbuffer *get_output_buffer() { 142 | return bufferevent_get_output(bufev_); 143 | } 144 | 145 | int get_fd() { 146 | return fd_; 147 | } 148 | 149 | void set_fd(int fd) { 150 | fd_ = fd; 151 | } 152 | 153 | bool keep_alive() { 154 | return keep_alive_; 155 | } 156 | 157 | void set_keep_alive(bool keep_alive) { 158 | keep_alive_ = keep_alive; 159 | } 160 | 161 | const std::string &get_host() { 162 | return host_; 163 | } 164 | 165 | unsigned short get_port() { 166 | return port_; 167 | } 168 | 169 | DKConnectionState get_state() { 170 | return state_; 171 | } 172 | 173 | void set_state(DKConnectionState state) { 174 | state_ = state; 175 | } 176 | 177 | struct event_base *get_base() { 178 | return base_; 179 | } 180 | 181 | DKConnectionError get_error() { 182 | return error_; 183 | } 184 | 185 | const char *get_error_string() { 186 | return StrError(error_); 187 | } 188 | 189 | int get_id() { 190 | return id_; 191 | } 192 | 193 | int get_timeout() { 194 | return timeout_; 195 | } 196 | 197 | void set_timeout(int timeout) { 198 | if (!bufev_) 199 | return; 200 | 201 | timeout_ = timeout; 202 | if (timeout_ != -1) 203 | bufferevent_settimeout(bufev_, timeout_, timeout_); 204 | else 205 | bufferevent_settimeout(bufev_, DK_READ_TIMEOUT, DK_WRITE_TIMEOUT); 206 | } 207 | 208 | void set_bind_address(const char *address) { 209 | bind_address_ = address; 210 | } 211 | 212 | void set_bind_port(int port) { 213 | bind_port_ = port; 214 | } 215 | 216 | static const char *StrError(DKConnectionError error) { 217 | switch (error) { 218 | case DKCON_ERROR_TIMEOUT: 219 | return "connection timeout"; 220 | 221 | case DKCON_ERROR_EOF: 222 | return "connection close"; 223 | 224 | case DKCON_ERROR_ERRNO: 225 | return strerror(errno); 226 | 227 | case DKCON_ERROR_BUFFER: 228 | return "read or write error"; 229 | 230 | case DKCON_ERROR_PARSE_ERROR: 231 | return "parse error"; 232 | 233 | case DKCON_ERROR_NONE: 234 | default: 235 | return "none"; 236 | } 237 | } 238 | 239 | void set_incoming_conn_free_cb( 240 | void (*cb)(DKBaseConnection *, void *), void *arg) { 241 | incoming_conn_free_cb_ = cb; 242 | incoming_conn_free_cb_arg_ = arg; 243 | } 244 | 245 | public: 246 | void Fail(DKConnectionError error); 247 | void ConnectFail(DKConnectionError error); 248 | 249 | protected: 250 | void FreeIncomingConn(); 251 | 252 | virtual void OnConnect() {} 253 | virtual void OnClose() {} 254 | virtual void OnError(DKConnectionError error) {} 255 | virtual void OnWrite() {} 256 | 257 | virtual enum READ_STATUS OnRead() { 258 | evbuffer_drain(bufferevent_get_input(bufev_), -1); 259 | return READ_ALL_DATA; 260 | } 261 | 262 | 263 | private: 264 | void WriteDone(); 265 | void ReadDone(); 266 | 267 | void ReadHandler(); 268 | 269 | static void EventReadCb(struct bufferevent *bufev, void *arg); 270 | 271 | static void EventWriteCb(struct bufferevent *bufev, void *arg); 272 | 273 | static void EventErrorCb(struct bufferevent *bufev, 274 | short which, 275 | void *arg); 276 | 277 | static void EventConnectCb(struct bufferevent *bufev, 278 | short which, 279 | void *arg); 280 | 281 | protected: 282 | bool inited_; 283 | struct event_base *base_; 284 | std::string host_; 285 | unsigned short port_; 286 | int fd_; 287 | bool keep_alive_; 288 | int timeout_; 289 | int id_; 290 | 291 | struct bufferevent *bufev_; 292 | DKConnectionState state_; 293 | DKConnectionKind kind_; 294 | DKConnectionError error_; 295 | struct evbuffer *temp_output_buf_; 296 | 297 | private: 298 | static int last_conn_id_; 299 | std::string bind_address_; 300 | int bind_port_; 301 | void (*incoming_conn_free_cb_)(DKBaseConnection *, void *); 302 | void *incoming_conn_free_cb_arg_; 303 | }; 304 | 305 | #endif 306 | -------------------------------------------------------------------------------- /src/dk_base_connection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * author:jlijian3@gmail.com 3 | * date:2012-03-08 4 | */ 5 | 6 | #include "dk_core.h" 7 | #include "dk_util.h" 8 | 9 | using namespace std; 10 | 11 | int DKBaseConnection::last_conn_id_; 12 | 13 | const char *DKCON_STATE_NAMES[] = { 14 | "DKCON_DISCONNECTED", 15 | "DKCON_CONNECTING", 16 | "DKCON_IDLE", 17 | "DKCON_READING", 18 | "DKCON_WRITING" 19 | }; 20 | 21 | DKBaseConnection::DKBaseConnection() 22 | : inited_(false), 23 | base_(NULL), 24 | port_(0), 25 | fd_(-1), 26 | keep_alive_(false), 27 | timeout_(-1), 28 | id_(-1), 29 | bufev_(NULL), 30 | state_(DKCON_DISCONNECTED), 31 | kind_(CON_INCOMING), 32 | error_(DKCON_ERROR_NONE), 33 | temp_output_buf_(NULL), 34 | bind_port_(0), 35 | incoming_conn_free_cb_(NULL) { 36 | } 37 | 38 | DKBaseConnection::~DKBaseConnection() { 39 | Destruct(); 40 | } 41 | 42 | void DKBaseConnection::Destruct() { 43 | Reset(); 44 | if (bufev_) 45 | bufferevent_free(bufev_); 46 | if (temp_output_buf_) 47 | evbuffer_free(temp_output_buf_); 48 | } 49 | 50 | bool DKBaseConnection::Init(struct event_base *base, 51 | int fd, 52 | const char *host, 53 | unsigned short port) { 54 | if (inited_) 55 | return true; 56 | 57 | if (!base) 58 | return false; 59 | 60 | if (fd == -1) 61 | kind_ = CON_OUTGOING; 62 | else 63 | kind_ = CON_INCOMING; 64 | 65 | fd_ = fd; 66 | base_ = base; 67 | host_ = host; 68 | port_ = port; 69 | 70 | if (bufev_) { 71 | bufferevent_setfd(bufev_, fd_); 72 | bufferevent_setcb(bufev_, 73 | EventReadCb, 74 | EventWriteCb, 75 | EventErrorCb, 76 | this); 77 | } else { 78 | bufev_ = bufferevent_new(fd_, 79 | EventReadCb, 80 | EventWriteCb, 81 | EventErrorCb, 82 | (void *)this); 83 | } 84 | 85 | if (!bufev_) 86 | return false; 87 | 88 | if (!temp_output_buf_) { 89 | temp_output_buf_ = evbuffer_new(); 90 | if (!temp_output_buf_) 91 | return false; 92 | } 93 | 94 | bufferevent_base_set(base_, bufev_); 95 | 96 | id_ = ++last_conn_id_; 97 | if (id_ == -1) 98 | id_ = ++last_conn_id_; 99 | 100 | inited_ = true; 101 | keep_alive_ = false; 102 | 103 | return true; 104 | } 105 | 106 | bool DKBaseConnection::StartRead() { 107 | assert(bufev_); 108 | 109 | /* Set up an event to read data */ 110 | DisableWrite(); 111 | EnableRead(); 112 | state_ = DKCON_READING; 113 | /* Reset the bufferevent callbacks */ 114 | bufferevent_setcb(bufev_, 115 | EventReadCb, 116 | EventWriteCb, 117 | EventErrorCb, 118 | this); 119 | 120 | evbuffer *buf = bufferevent_get_input(bufev_); 121 | if (evbuffer_get_length(buf) > 0) { 122 | ReadHandler(); 123 | } 124 | 125 | return true; 126 | } 127 | 128 | bool DKBaseConnection::StartWrite() { 129 | if (!IsConnected()) { 130 | DK_DEBUG("[warn] %s: not connected\n", __func__); 131 | return false; 132 | } 133 | 134 | assert(bufev_); 135 | 136 | if (evbuffer_get_length(get_output_buffer()) == 0) 137 | return true; 138 | 139 | state_ = DKCON_WRITING; 140 | 141 | int res = TryWrite(); 142 | if (res < 0) { 143 | Fail(DKCON_ERROR_ERRNO); 144 | return false; 145 | } else if (res == 0) { 146 | Fail(DKCON_ERROR_EOF); 147 | return false; 148 | } 149 | 150 | if (evbuffer_get_length(get_output_buffer()) == 0) { 151 | WriteDone(); 152 | return true; 153 | } 154 | EnableWrite(); 155 | 156 | /* Disable the read callback: we don't actually care about data; 157 | * we only care about close detection. (We don't disable reading, 158 | * since we *do* want to learn about any close events.) */ 159 | /* 160 | bufferevent_setcb(bufev_, 161 | NULL, 162 | EventWriteCb, 163 | EventErrorCb, 164 | this); 165 | */ 166 | 167 | return true; 168 | } 169 | 170 | static const int atmost = 16348; 171 | int DKBaseConnection::TryWrite() { 172 | //return 1; 173 | evbuffer_unfreeze(get_output_buffer(), 1); 174 | int res = evbuffer_write(get_output_buffer(), fd_); 175 | evbuffer_freeze(get_output_buffer(), 1); 176 | 177 | if (res == -1) { 178 | int err = evutil_socket_geterror(fd_); 179 | if (err == EINTR || err == EAGAIN) 180 | return 1; 181 | else 182 | return -1; 183 | } else if (res == 0) { 184 | return 0; 185 | } 186 | return 2; 187 | } 188 | 189 | void DKBaseConnection::ReadHandler() { 190 | if (!bufev_) 191 | return; 192 | 193 | enum READ_STATUS read_status; 194 | bool stop = false; 195 | int nreqs = 200; 196 | 197 | switch (state_) { 198 | case DKCON_READING: 199 | case DKCON_IDLE: 200 | { 201 | if (state_ == DKCON_IDLE) 202 | state_ = DKCON_READING; 203 | 204 | while (!stop && nreqs-- > 0) { 205 | read_status = OnRead(); 206 | 207 | switch (read_status) { 208 | case READ_ALL_DATA: 209 | ReadDone(); 210 | if (evbuffer_get_length(get_input_buffer()) == 0) 211 | stop = true; 212 | break; 213 | 214 | case READ_INNER_ERROR: 215 | case READ_MEMORY_ERROR: 216 | case READ_BAD_CLIENT: 217 | Fail(DKCON_ERROR_PARSE_ERROR); 218 | stop = true; 219 | break; 220 | 221 | case READ_NEED_MORE_DATA: 222 | stop = true; 223 | break; 224 | 225 | default: 226 | stop = true; 227 | break; 228 | } 229 | } /* while */ 230 | } 231 | break; 232 | 233 | default: 234 | DK_DEBUG("[warn] %s state %d is uncorrect\n", __func__, state_); 235 | break; 236 | } 237 | } 238 | 239 | void DKBaseConnection::ReadDone() { 240 | if (kind_ == CON_OUTGOING) { 241 | set_state(DKCON_IDLE); 242 | 243 | if (!keep_alive()) { 244 | Reset(); 245 | return; 246 | } 247 | 248 | if (!IsConnected()) 249 | Connect(); 250 | else { 251 | if (evbuffer_get_length(get_output_buffer()) > 0) 252 | StartWrite(); 253 | } 254 | return; 255 | } 256 | } 257 | 258 | void DKBaseConnection::WriteDone() { 259 | OnWrite(); 260 | 261 | if (kind_ == CON_INCOMING) { 262 | if (!keep_alive()) { 263 | Reset(); 264 | FreeIncomingConn(); 265 | return; 266 | } 267 | 268 | StartRead(); 269 | return; 270 | } 271 | 272 | if (kind_ == CON_OUTGOING) { 273 | StartRead(); 274 | } 275 | } 276 | 277 | void DKBaseConnection::EventReadCb(struct bufferevent *bufev, 278 | void *arg) { 279 | DKBaseConnection *conn = (DKBaseConnection *)arg; 280 | assert(conn); 281 | 282 | conn->ReadHandler(); 283 | } 284 | 285 | void DKBaseConnection::EventWriteCb(struct bufferevent *bufev, 286 | void *arg) { 287 | DKBaseConnection *conn = (DKBaseConnection *)arg; 288 | assert(conn); 289 | 290 | conn->WriteDone(); 291 | } 292 | 293 | void DKBaseConnection::EventErrorCb(struct bufferevent *bufev, 294 | short what, 295 | void *arg) { 296 | DKBaseConnection *conn = (DKBaseConnection *)arg; 297 | assert(conn); 298 | 299 | switch (conn->get_state()) { 300 | case DKCON_CONNECTING: 301 | if (what & BEV_EVENT_TIMEOUT) { 302 | DK_DEBUG("[error] %s: conecting timeout for %s:%d on %d\n", 303 | __func__, conn->get_host().c_str(), conn->get_port(), 304 | conn->get_fd()); 305 | conn->ConnectFail(DKCON_ERROR_TIMEOUT); 306 | return; 307 | } 308 | break; 309 | 310 | default: 311 | break; 312 | } 313 | 314 | if (what & (BEV_EVENT_TIMEOUT|BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 315 | DKConnectionError error; 316 | 317 | if (what & BEV_EVENT_TIMEOUT) { 318 | error = DKCON_ERROR_TIMEOUT; 319 | } 320 | if (what & BEV_EVENT_EOF) { 321 | error = DKCON_ERROR_EOF; 322 | } 323 | if (what & BEV_EVENT_ERROR) { 324 | error = DKCON_ERROR_ERRNO; 325 | } 326 | 327 | conn->Fail(error); 328 | } else { 329 | conn->Fail(DKCON_ERROR_ERRNO); 330 | } 331 | } 332 | 333 | void DKBaseConnection::EventConnectCb(struct bufferevent *bufev, 334 | short what, 335 | void *arg) { 336 | DKBaseConnection *conn = (DKBaseConnection *)arg; 337 | int error; 338 | ev_socklen_t errsz = sizeof(error); 339 | 340 | int fd = bufferevent_getfd(bufev); 341 | conn->set_fd(fd); 342 | 343 | if (!(what & BEV_EVENT_CONNECTED)) { 344 | EventErrorCb(bufev, what, arg); 345 | return; 346 | } 347 | 348 | /* Check if the connection completed */ 349 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error, 350 | &errsz) == -1) { 351 | 352 | goto cleanup; 353 | } 354 | 355 | if (error) { 356 | DK_DEBUG("[error] %s: %s %s:%d on %d\n", __func__, strerror(errno), 357 | conn->get_host().c_str(), conn->get_port(), conn->get_fd()); 358 | goto cleanup; 359 | } 360 | 361 | conn->ConnectMade(); 362 | return; 363 | 364 | cleanup: 365 | conn->ConnectFail(DKCON_ERROR_ERRNO); 366 | } 367 | 368 | void DKBaseConnection::Fail(DKConnectionError error) { 369 | error_ = error; 370 | 371 | /* 372 | if (keep_alive() && 373 | error == DKCON_ERROR_TIMEOUT && 374 | state_ != DKCON_CONNECTING) { 375 | OnError(error); 376 | if (state_ == DKCON_WRITING) 377 | EnableWrite(); 378 | else 379 | EnableRead(); 380 | 381 | error_ = DKCON_ERROR_NONE; 382 | return; 383 | } 384 | */ 385 | 386 | DK_DEBUG("[error] %s: %s for %s:%d on %d %s\n", __func__, StrError(error), 387 | get_host().c_str(), get_port(), get_fd(), 388 | DKCON_STATE_NAMES[get_state()]); 389 | 390 | DisableReadWrite(); 391 | OnError(error); 392 | Reset(); 393 | 394 | if (kind_ == CON_INCOMING) { 395 | FreeIncomingConn(); 396 | return; 397 | } 398 | } 399 | 400 | void DKBaseConnection::ConnectFail(DKConnectionError error) { 401 | error_ = error; 402 | OnError(error); 403 | Reset(); 404 | } 405 | 406 | void DKBaseConnection::Reset() { 407 | if (!bufev_) 408 | return; 409 | 410 | DisableReadWrite(); 411 | 412 | if (fd_ == -1) 413 | fd_ = bufferevent_getfd(bufev_); 414 | 415 | if (fd_ != -1) { 416 | //if (IsConnected()) 417 | OnClose(); 418 | 419 | shutdown(fd_, SHUT_WR); 420 | close(fd_); 421 | fd_ = -1; 422 | bufferevent_setfd(bufev_, -1); 423 | } 424 | 425 | struct evbuffer *tmp = get_input_buffer(); 426 | evbuffer_drain(tmp, evbuffer_get_length(tmp)); 427 | tmp = get_output_buffer(); 428 | evbuffer_unfreeze(tmp, 1); 429 | evbuffer_drain(tmp, evbuffer_get_length(tmp)); 430 | evbuffer_freeze(tmp, 1); 431 | if (temp_output_buf_) { 432 | tmp = temp_output_buf_; 433 | evbuffer_drain(tmp, evbuffer_get_length(tmp)); 434 | } 435 | 436 | inited_ = false; 437 | state_ = DKCON_DISCONNECTED; 438 | error_ = DKCON_ERROR_NONE; 439 | } 440 | 441 | bool DKBaseConnection::Connect() { 442 | if (state_ == DKCON_CONNECTING) 443 | return true; 444 | 445 | if (!bufev_) 446 | return false; 447 | 448 | Reset(); 449 | kind_ = CON_OUTGOING; 450 | 451 | bufferevent_setfd(bufev_, fd_); 452 | bufferevent_setcb(bufev_, 453 | NULL /* EventReadCb */, 454 | NULL /* EventWriteCb */, 455 | EventConnectCb, 456 | (void *)this); 457 | bufferevent_settimeout(bufev_, 0, 458 | timeout_ != -1 ? timeout_ : DK_CONNECT_TIMEOUT); 459 | 460 | EnableWrite(); 461 | 462 | string ip; 463 | 464 | /* Libevent evget_addrinfo has a bug */ 465 | if (!DKGetHostByName(host_, ip)) { 466 | ConnectFail(DKCON_ERROR_ERRNO); 467 | return false; 468 | } 469 | 470 | if (ip.empty()) 471 | ip = host_; 472 | 473 | state_ = DKCON_CONNECTING; 474 | 475 | int res = bufferevent_socket_connect_hostname(bufev_, 476 | NULL, AF_INET, ip.c_str(), port_); 477 | 478 | if (res < 0) { 479 | DK_DEBUG("[error] %s: bufferevent_socket_connect_hostname return %d\n", 480 | __func__, res); 481 | ConnectFail(DKCON_ERROR_ERRNO); 482 | return false; 483 | } 484 | 485 | int fd = bufferevent_getfd(bufev_); 486 | if (fd != -1) { 487 | socklen_t namelen = sizeof(struct sockaddr_in); 488 | struct sockaddr_in name; 489 | socklen_t peer_namelen = sizeof(struct sockaddr_in); 490 | struct sockaddr_in peer_name; 491 | 492 | 493 | if (0 == getpeername(fd, 494 | (struct sockaddr *)&peer_name, &peer_namelen)) 495 | { 496 | if (0 == getsockname(fd, 497 | (struct sockaddr *)&name, &namelen)) 498 | { 499 | int flags = 1; 500 | if ((name.sin_addr.s_addr == peer_name.sin_addr.s_addr) && 501 | (name.sin_port == peer_name.sin_port)) { 502 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 503 | (void *)&flags, sizeof(flags)); 504 | DK_DEBUG("[error] src addr == dest addr\n"); 505 | ConnectFail(DKCON_ERROR_ERRNO); 506 | return false; 507 | } 508 | } 509 | } 510 | } 511 | 512 | return true; 513 | } 514 | 515 | void DKBaseConnection::ConnectMade() { 516 | OnConnect(); 517 | 518 | if (kind_ == CON_OUTGOING) { 519 | set_state(DKCON_IDLE); 520 | bufferevent_setcb(bufev_, 521 | EventReadCb, 522 | EventWriteCb, 523 | EventErrorCb, 524 | this); 525 | 526 | if (timeout_ != -1) { 527 | bufferevent_settimeout(bufev_, timeout_, timeout_); 528 | } 529 | 530 | if (temp_output_buf_ && evbuffer_get_length(temp_output_buf_) > 0) { 531 | AddOutputBuffer(temp_output_buf_); 532 | } 533 | 534 | if (evbuffer_get_length(get_output_buffer()) > 0) 535 | StartWrite(); 536 | } 537 | } 538 | 539 | void DKBaseConnection::FreeIncomingConn() { 540 | if (incoming_conn_free_cb_) 541 | incoming_conn_free_cb_(this, incoming_conn_free_cb_arg_); 542 | } 543 | --------------------------------------------------------------------------------